blob: 82381bddb15948db427fd7f4cadfd8eec60f04a1 [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
Sailesh Nepal9d58de52014-07-18 14:53:19 -070019import android.app.PendingIntent;
Sailesh Nepalc92c4362014-07-04 18:33:21 -070020import android.content.ComponentName;
Sailesh Nepale8ecb982014-07-11 17:19:42 -070021import android.net.Uri;
Sailesh Nepalc92c4362014-07-04 18:33:21 -070022import android.os.Bundle;
23import android.os.Handler;
24import android.os.IBinder;
25import android.os.Message;
26import android.os.RemoteException;
27import android.telecomm.CallAudioState;
Sailesh Nepalc92c4362014-07-04 18:33:21 -070028import android.telecomm.ConnectionRequest;
Santos Cordonf2f14ef2014-07-20 18:00:20 -070029import android.telecomm.ConnectionService;
Sailesh Nepalc92c4362014-07-04 18:33:21 -070030import android.telecomm.GatewayInfo;
Santos Cordon72890ce2014-07-21 01:32:04 -070031import android.telecomm.ParcelableConnection;
Ihab Awadb78b2762014-07-25 15:16:23 -070032import android.telecomm.PhoneAccount;
33import android.telecomm.PhoneAccountHandle;
Sailesh Nepal35faf8c2014-07-08 22:02:34 -070034import android.telecomm.StatusHints;
Sailesh Nepalc92c4362014-07-04 18:33:21 -070035import android.telephony.DisconnectCause;
36
37import com.android.internal.os.SomeArgs;
Sailesh Nepalc92c4362014-07-04 18:33:21 -070038import com.android.internal.telecomm.IConnectionService;
39import com.android.internal.telecomm.IConnectionServiceAdapter;
Andrew Lee3bcf9352014-07-23 12:36:05 -070040import com.android.internal.telecomm.IVideoCallProvider;
Sailesh Nepalc92c4362014-07-04 18:33:21 -070041import com.android.internal.telecomm.RemoteServiceCallback;
Sailesh Nepalc92c4362014-07-04 18:33:21 -070042import com.google.common.base.Preconditions;
Sailesh Nepalc92c4362014-07-04 18:33:21 -070043
Ihab Awadb78b2762014-07-25 15:16:23 -070044import java.io.IOException;
Sailesh Nepalc92c4362014-07-04 18:33:21 -070045import java.util.ArrayList;
46import java.util.Collection;
Ihab Awadb78b2762014-07-25 15:16:23 -070047import java.util.Collections;
Sailesh Nepalc92c4362014-07-04 18:33:21 -070048import java.util.HashMap;
49import java.util.HashSet;
50import java.util.List;
51import java.util.Map;
52import java.util.Set;
53
54/**
55 * Wrapper for {@link IConnectionService}s, handles binding to {@link IConnectionService} and keeps
56 * track of when the object can safely be unbound. Other classes should not use
57 * {@link IConnectionService} directly and instead should use this class to invoke methods of
58 * {@link IConnectionService}.
59 */
60final class ConnectionServiceWrapper extends ServiceBinder<IConnectionService> {
Sailesh Nepal664837f2014-07-14 16:31:51 -070061 private static final int MSG_HANDLE_CREATE_CONNECTION_SUCCESSFUL = 1;
62 private static final int MSG_HANDLE_CREATE_CONNECTION_FAILED = 2;
63 private static final int MSG_HANDLE_CREATE_CONNECTION_CANCELLED = 3;
64 private static final int MSG_SET_ACTIVE = 4;
65 private static final int MSG_SET_RINGING = 5;
66 private static final int MSG_SET_DIALING = 6;
67 private static final int MSG_SET_DISCONNECTED = 7;
68 private static final int MSG_SET_ON_HOLD = 8;
69 private static final int MSG_SET_REQUESTING_RINGBACK = 9;
70 private static final int MSG_SET_CALL_CAPABILITIES = 10;
71 private static final int MSG_SET_IS_CONFERENCED = 11;
72 private static final int MSG_ADD_CONFERENCE_CALL = 12;
73 private static final int MSG_REMOVE_CALL = 13;
74 private static final int MSG_ON_POST_DIAL_WAIT = 14;
75 private static final int MSG_QUERY_REMOTE_CALL_SERVICES = 15;
76 private static final int MSG_SET_CALL_VIDEO_PROVIDER = 16;
77 private static final int MSG_SET_AUDIO_MODE_IS_VOIP = 17;
78 private static final int MSG_SET_STATUS_HINTS = 18;
79 private static final int MSG_SET_HANDLE = 19;
80 private static final int MSG_SET_CALLER_DISPLAY_NAME = 20;
Tyler Gunn0a388fc2014-07-17 12:21:17 -070081 private static final int MSG_SET_VIDEO_STATE = 21;
Sailesh Nepal9d58de52014-07-18 14:53:19 -070082 private static final int MSG_START_ACTIVITY_FROM_IN_CALL = 22;
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) {
Sailesh Nepal664837f2014-07-14 16:31:51 -070089 case MSG_HANDLE_CREATE_CONNECTION_SUCCESSFUL: {
Santos Cordon72890ce2014-07-21 01:32:04 -070090 SomeArgs args = (SomeArgs) msg.obj;
91 try {
92 ConnectionRequest request = (ConnectionRequest) args.arg1;
93 if (mPendingResponses.containsKey(request.getCallId())) {
94 ParcelableConnection connection = (ParcelableConnection) args.arg2;
95 mPendingResponses.remove(request.getCallId()).
96 handleCreateConnectionSuccessful(request, connection);
97 }
98 } finally {
99 args.recycle();
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700100 }
101 break;
102 }
Sailesh Nepal664837f2014-07-14 16:31:51 -0700103 case MSG_HANDLE_CREATE_CONNECTION_FAILED: {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700104 SomeArgs args = (SomeArgs) msg.obj;
105 try {
Sailesh Nepala49c6432014-07-07 22:47:11 -0700106 ConnectionRequest request = (ConnectionRequest) args.arg1;
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700107 int statusCode = args.argi1;
108 String statusMsg = (String) args.arg2;
Santos Cordonfd6ca442014-07-24 15:34:01 -0700109 removeCall(
110 mCallIdMapper.getCall(request.getCallId()),
111 statusCode,
112 statusMsg);
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700113 } finally {
114 args.recycle();
115 }
116 break;
117 }
Sailesh Nepal664837f2014-07-14 16:31:51 -0700118 case MSG_HANDLE_CREATE_CONNECTION_CANCELLED: {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700119 ConnectionRequest request = (ConnectionRequest) msg.obj;
Sailesh Nepal664837f2014-07-14 16:31:51 -0700120 if (mPendingResponses.containsKey(request.getCallId())) {
121 mPendingResponses.remove(
122 request.getCallId()).handleCreateConnectionCancelled();
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700123 } else {
Sailesh Nepal664837f2014-07-14 16:31:51 -0700124 //Log.w(this, "handleCreateConnectionCancelled, unknown call: %s", callId);
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700125 }
126 break;
127 }
128 case MSG_SET_ACTIVE:
129 call = mCallIdMapper.getCall(msg.obj);
130 if (call != null) {
131 mCallsManager.markCallAsActive(call);
132 } else {
133 //Log.w(this, "setActive, unknown call id: %s", msg.obj);
134 }
135 break;
136 case MSG_SET_RINGING:
137 call = mCallIdMapper.getCall(msg.obj);
138 if (call != null) {
139 mCallsManager.markCallAsRinging(call);
140 } else {
141 //Log.w(this, "setRinging, unknown call id: %s", msg.obj);
142 }
143 break;
144 case MSG_SET_DIALING:
145 call = mCallIdMapper.getCall(msg.obj);
146 if (call != null) {
147 mCallsManager.markCallAsDialing(call);
148 } else {
149 //Log.w(this, "setDialing, unknown call id: %s", msg.obj);
150 }
151 break;
152 case MSG_SET_DISCONNECTED: {
153 SomeArgs args = (SomeArgs) msg.obj;
154 try {
155 call = mCallIdMapper.getCall(args.arg1);
156 String disconnectMessage = (String) args.arg2;
157 int disconnectCause = args.argi1;
158 if (call != null) {
159 mCallsManager.markCallAsDisconnected(call, disconnectCause,
160 disconnectMessage);
161 } else {
162 //Log.w(this, "setDisconnected, unknown call id: %s", args.arg1);
163 }
164 } finally {
165 args.recycle();
166 }
167 break;
168 }
169 case MSG_SET_ON_HOLD:
170 call = mCallIdMapper.getCall(msg.obj);
171 if (call != null) {
172 mCallsManager.markCallAsOnHold(call);
173 } else {
174 //Log.w(this, "setOnHold, unknown call id: %s", msg.obj);
175 }
176 break;
177 case MSG_SET_REQUESTING_RINGBACK: {
Sailesh Nepal9d58de52014-07-18 14:53:19 -0700178 call = mCallIdMapper.getCall(msg.obj);
179 if (call != null) {
180 call.setRequestingRingback(msg.arg1 == 1);
181 } else {
182 //Log.w(this, "setRingback, unknown call id: %s", args.arg1);
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700183 }
184 break;
185 }
Sailesh Nepale20bc972014-07-09 21:22:36 -0700186 case MSG_SET_CALL_CAPABILITIES: {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700187 call = mCallIdMapper.getCall(msg.obj);
188 if (call != null) {
Sailesh Nepale20bc972014-07-09 21:22:36 -0700189 call.setCallCapabilities(msg.arg1);
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700190 } else {
191 //Log.w(ConnectionServiceWrapper.this,
Sailesh Nepale20bc972014-07-09 21:22:36 -0700192 // "setCallCapabilities, unknown call id: %s", msg.obj);
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700193 }
194 break;
195 }
196 case MSG_SET_IS_CONFERENCED: {
197 SomeArgs args = (SomeArgs) msg.obj;
198 try {
199 Call childCall = mCallIdMapper.getCall(args.arg1);
200 if (childCall != null) {
201 String conferenceCallId = (String) args.arg2;
202 if (conferenceCallId == null) {
203 childCall.setParentCall(null);
204 } else {
205 Call conferenceCall = mCallIdMapper.getCall(conferenceCallId);
206 if (conferenceCall != null &&
207 !mPendingConferenceCalls.contains(conferenceCall)) {
208 childCall.setParentCall(conferenceCall);
209 } else {
210 //Log.w(this, "setIsConferenced, unknown conference id %s",
211 // conferenceCallId);
212 }
213 }
214 } else {
215 //Log.w(this, "setIsConferenced, unknown call id: %s", args.arg1);
216 }
217 } finally {
218 args.recycle();
219 }
220 break;
221 }
222 case MSG_ADD_CONFERENCE_CALL: {
223 Call conferenceCall = mCallIdMapper.getCall(msg.obj);
224 if (mPendingConferenceCalls.remove(conferenceCall)) {
225 Log.v(this, "confirming conf call %s", conferenceCall);
226 conferenceCall.confirmConference();
227 } else {
228 //Log.w(this, "addConference, unknown call id: %s", callId);
229 }
230 break;
231 }
232 case MSG_REMOVE_CALL:
233 break;
234 case MSG_ON_POST_DIAL_WAIT: {
235 SomeArgs args = (SomeArgs) msg.obj;
236 try {
237 call = mCallIdMapper.getCall(args.arg1);
238 if (call != null) {
239 String remaining = (String) args.arg2;
240 call.onPostDialWait(remaining);
241 } else {
242 //Log.w(this, "onPostDialWait, unknown call id: %s", args.arg1);
243 }
244 } finally {
245 args.recycle();
246 }
247 break;
248 }
249 case MSG_QUERY_REMOTE_CALL_SERVICES: {
Sailesh Nepal664837f2014-07-14 16:31:51 -0700250 queryRemoteConnectionServices((RemoteServiceCallback) msg.obj);
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700251 break;
252 }
253 case MSG_SET_CALL_VIDEO_PROVIDER: {
254 SomeArgs args = (SomeArgs) msg.obj;
255 try {
256 call = mCallIdMapper.getCall(args.arg1);
Andrew Lee3bcf9352014-07-23 12:36:05 -0700257 IVideoCallProvider videoCallProvider = (IVideoCallProvider) args.arg2;
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700258 if (call != null) {
Andrew Lee3bcf9352014-07-23 12:36:05 -0700259 call.setVideoCallProvider(videoCallProvider);
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700260 }
261 } finally {
262 args.recycle();
263 }
264 break;
265 }
Sailesh Nepal7e669572014-07-08 21:29:12 -0700266 case MSG_SET_AUDIO_MODE_IS_VOIP: {
Sailesh Nepale20bc972014-07-09 21:22:36 -0700267 call = mCallIdMapper.getCall(msg.obj);
268 if (call != null) {
269 call.setAudioModeIsVoip(msg.arg1 == 1);
Sailesh Nepal7e669572014-07-08 21:29:12 -0700270 }
271 break;
272 }
Sailesh Nepal35faf8c2014-07-08 22:02:34 -0700273 case MSG_SET_STATUS_HINTS: {
274 SomeArgs args = (SomeArgs) msg.obj;
275 try {
276 call = mCallIdMapper.getCall(args.arg1);
277 StatusHints statusHints = (StatusHints) args.arg2;
278 if (call != null) {
279 call.setStatusHints(statusHints);
280 }
281 } finally {
282 args.recycle();
283 }
284 break;
285 }
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700286 case MSG_SET_HANDLE: {
287 SomeArgs args = (SomeArgs) msg.obj;
288 try {
289 call = mCallIdMapper.getCall(args.arg1);
290 if (call != null) {
291 call.setHandle((Uri) args.arg2, args.argi1);
292 }
293 } finally {
294 args.recycle();
295 }
296 break;
297 }
298 case MSG_SET_CALLER_DISPLAY_NAME: {
299 SomeArgs args = (SomeArgs) msg.obj;
300 try {
301 call = mCallIdMapper.getCall(args.arg1);
302 if (call != null) {
303 call.setCallerDisplayName((String) args.arg2, args.argi1);
304 }
305 } finally {
306 args.recycle();
307 }
308 break;
309 }
Tyler Gunn0a388fc2014-07-17 12:21:17 -0700310 case MSG_SET_VIDEO_STATE: {
311 call = mCallIdMapper.getCall(msg.obj);
312 if (call != null) {
313 call.setVideoState(msg.arg1);
314 }
Tyler Gunn7e0df312014-07-21 14:34:58 -0700315 break;
Tyler Gunn0a388fc2014-07-17 12:21:17 -0700316 }
Sailesh Nepal9d58de52014-07-18 14:53:19 -0700317 case MSG_START_ACTIVITY_FROM_IN_CALL: {
318 SomeArgs args = (SomeArgs) msg.obj;
319 try {
320 call = mCallIdMapper.getCall(args.arg1);
321 if (call != null) {
322 call.startActivityFromInCall((PendingIntent) args.arg2);
323 }
324 } finally {
325 args.recycle();
326 }
327 break;
328 }
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700329 }
330 }
331 };
332
333 private final class Adapter extends IConnectionServiceAdapter.Stub {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700334 @Override
Santos Cordon72890ce2014-07-21 01:32:04 -0700335 public void handleCreateConnectionSuccessful(
336 ConnectionRequest request, ParcelableConnection connection) {
337
Sailesh Nepal664837f2014-07-14 16:31:51 -0700338 logIncoming("handleCreateConnectionSuccessful %s", request);
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700339 mCallIdMapper.checkValidCallId(request.getCallId());
Santos Cordon72890ce2014-07-21 01:32:04 -0700340 SomeArgs args = SomeArgs.obtain();
341 args.arg1 = request;
342 args.arg2 = connection;
343 mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_SUCCESSFUL, args).sendToTarget();
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700344 }
345
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700346 @Override
Sailesh Nepal664837f2014-07-14 16:31:51 -0700347 public void handleCreateConnectionFailed(
348 ConnectionRequest request, int errorCode, String errorMsg) {
349 logIncoming("handleCreateConnectionFailed %s %d %s", request, errorCode, errorMsg);
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700350 mCallIdMapper.checkValidCallId(request.getCallId());
351 SomeArgs args = SomeArgs.obtain();
352 args.arg1 = request;
353 args.argi1 = errorCode;
354 args.arg2 = errorMsg;
Sailesh Nepal664837f2014-07-14 16:31:51 -0700355 mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_FAILED, args).sendToTarget();
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700356 }
357
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700358 @Override
Sailesh Nepal664837f2014-07-14 16:31:51 -0700359 public void handleCreateConnectionCancelled(ConnectionRequest request) {
360 logIncoming("handleCreateConnectionCancelled %s", request);
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700361 mCallIdMapper.checkValidCallId(request.getCallId());
Sailesh Nepal664837f2014-07-14 16:31:51 -0700362 mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_CANCELLED, request).sendToTarget();
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700363 }
364
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700365 @Override
366 public void setActive(String callId) {
367 logIncoming("setActive %s", callId);
368 mCallIdMapper.checkValidCallId(callId);
369 mHandler.obtainMessage(MSG_SET_ACTIVE, callId).sendToTarget();
370 }
371
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700372 @Override
373 public void setRinging(String callId) {
374 logIncoming("setRinging %s", callId);
375 mCallIdMapper.checkValidCallId(callId);
376 mHandler.obtainMessage(MSG_SET_RINGING, callId).sendToTarget();
377 }
378
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700379 @Override
Andrew Lee3bcf9352014-07-23 12:36:05 -0700380 public void setVideoCallProvider(String callId, IVideoCallProvider videoCallProvider) {
381 logIncoming("setVideoCallProvider %s", callId);
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700382 mCallIdMapper.checkValidCallId(callId);
383 SomeArgs args = SomeArgs.obtain();
384 args.arg1 = callId;
Andrew Lee3bcf9352014-07-23 12:36:05 -0700385 args.arg2 = videoCallProvider;
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700386 mHandler.obtainMessage(MSG_SET_CALL_VIDEO_PROVIDER, args).sendToTarget();
387 }
388
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700389 @Override
390 public void setDialing(String callId) {
391 logIncoming("setDialing %s", callId);
392 mCallIdMapper.checkValidCallId(callId);
393 mHandler.obtainMessage(MSG_SET_DIALING, callId).sendToTarget();
394 }
395
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700396 @Override
397 public void setDisconnected(
398 String callId, int disconnectCause, String disconnectMessage) {
399 logIncoming("setDisconnected %s %d %s", callId, disconnectCause, disconnectMessage);
400 mCallIdMapper.checkValidCallId(callId);
401 SomeArgs args = SomeArgs.obtain();
402 args.arg1 = callId;
403 args.arg2 = disconnectMessage;
404 args.argi1 = disconnectCause;
405 mHandler.obtainMessage(MSG_SET_DISCONNECTED, args).sendToTarget();
406 }
407
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700408 @Override
409 public void setOnHold(String callId) {
410 logIncoming("setOnHold %s", callId);
411 mCallIdMapper.checkValidCallId(callId);
412 mHandler.obtainMessage(MSG_SET_ON_HOLD, callId).sendToTarget();
413 }
414
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700415 @Override
416 public void setRequestingRingback(String callId, boolean ringback) {
417 logIncoming("setRequestingRingback %s %b", callId, ringback);
418 mCallIdMapper.checkValidCallId(callId);
Sailesh Nepal9d58de52014-07-18 14:53:19 -0700419 mHandler.obtainMessage(MSG_SET_REQUESTING_RINGBACK, ringback ? 1 : 0, 0, callId)
420 .sendToTarget();
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700421 }
422
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700423 @Override
424 public void removeCall(String callId) {
425 logIncoming("removeCall %s", callId);
426 }
427
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700428 @Override
Sailesh Nepale20bc972014-07-09 21:22:36 -0700429 public void setCallCapabilities(String callId, int callCapabilities) {
430 logIncoming("setCallCapabilities %s %d", callId, callCapabilities);
431 mHandler.obtainMessage(MSG_SET_CALL_CAPABILITIES, callCapabilities, 0, callId)
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700432 .sendToTarget();
433 }
434
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700435 @Override
436 public void setIsConferenced(String callId, String conferenceCallId) {
437 logIncoming("setIsConferenced %s %s", callId, conferenceCallId);
438 SomeArgs args = SomeArgs.obtain();
439 args.arg1 = callId;
440 args.arg2 = conferenceCallId;
441 mHandler.obtainMessage(MSG_SET_IS_CONFERENCED, args).sendToTarget();
442 }
443
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700444 @Override
445 public void addConferenceCall(String callId) {
446 logIncoming("addConferenceCall %s", callId);
447 mCallIdMapper.checkValidCallId(callId);
448 mHandler.obtainMessage(MSG_ADD_CONFERENCE_CALL, callId).sendToTarget();
449 }
450
451 @Override
452 public void onPostDialWait(String callId, String remaining) throws RemoteException {
453 logIncoming("onPostDialWait %s %s", callId, remaining);
454 mCallIdMapper.checkValidCallId(callId);
455 SomeArgs args = SomeArgs.obtain();
456 args.arg1 = callId;
457 args.arg2 = remaining;
458 mHandler.obtainMessage(MSG_ON_POST_DIAL_WAIT, args).sendToTarget();
459 }
460
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700461 @Override
462 public void queryRemoteConnectionServices(RemoteServiceCallback callback) {
463 logIncoming("queryRemoteCSs");
464 mHandler.obtainMessage(MSG_QUERY_REMOTE_CALL_SERVICES, callback).sendToTarget();
465 }
466
467 @Override
Tyler Gunn0a388fc2014-07-17 12:21:17 -0700468 public void setVideoState(String callId, int videoState) {
469 logIncoming("setVideoState %s %d", callId, videoState);
470 mCallIdMapper.checkValidCallId(callId);
471 mHandler.obtainMessage(MSG_SET_VIDEO_STATE, videoState, 0, callId).sendToTarget();
472 }
473
474 @Override
Sailesh Nepal7e669572014-07-08 21:29:12 -0700475 public void setAudioModeIsVoip(String callId, boolean isVoip) {
Sailesh Nepal7fa33ca2014-07-10 15:28:21 -0700476 logIncoming("setAudioModeIsVoip %s %b", callId, isVoip);
Sailesh Nepal7e669572014-07-08 21:29:12 -0700477 mCallIdMapper.checkValidCallId(callId);
Sailesh Nepale20bc972014-07-09 21:22:36 -0700478 mHandler.obtainMessage(MSG_SET_AUDIO_MODE_IS_VOIP, isVoip ? 1 : 0, 0,
479 callId).sendToTarget();
Sailesh Nepal7e669572014-07-08 21:29:12 -0700480 }
Sailesh Nepal35faf8c2014-07-08 22:02:34 -0700481
482 @Override
483 public void setStatusHints(String callId, StatusHints statusHints) {
484 logIncoming("setStatusHints %s %s", callId, statusHints);
485 mCallIdMapper.checkValidCallId(callId);
486 SomeArgs args = SomeArgs.obtain();
487 args.arg1 = callId;
488 args.arg2 = statusHints;
489 mHandler.obtainMessage(MSG_SET_STATUS_HINTS, args).sendToTarget();
490 }
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700491
492 @Override
493 public void setHandle(String callId, Uri handle, int presentation) {
494 logIncoming("setHandle %s %s %d", callId, handle, presentation);
495 mCallIdMapper.checkValidCallId(callId);
496 SomeArgs args = SomeArgs.obtain();
497 args.arg1 = callId;
498 args.arg2 = handle;
499 args.argi1 = presentation;
500 mHandler.obtainMessage(MSG_SET_HANDLE, args).sendToTarget();
501 }
502
503 @Override
504 public void setCallerDisplayName(
505 String callId, String callerDisplayName, int presentation) {
506 logIncoming("setCallerDisplayName %s %s %d", callId, callerDisplayName, presentation);
507 mCallIdMapper.checkValidCallId(callId);
508 SomeArgs args = SomeArgs.obtain();
509 args.arg1 = callId;
510 args.arg2 = callerDisplayName;
511 args.argi1 = presentation;
512 mHandler.obtainMessage(MSG_SET_CALLER_DISPLAY_NAME, args).sendToTarget();
513 }
Sailesh Nepal9d58de52014-07-18 14:53:19 -0700514
515 @Override
516 public void startActivityFromInCall(String callId, PendingIntent intent) {
517 logIncoming("startActivityFromInCall %s %s", callId, intent);
518 mCallIdMapper.checkValidCallId(callId);
519 SomeArgs args = SomeArgs.obtain();
520 args.arg1 = callId;
521 args.arg2 = intent;
522 mHandler.obtainMessage(MSG_START_ACTIVITY_FROM_IN_CALL, args).sendToTarget();
523 }
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700524 }
525
526 private final Adapter mAdapter = new Adapter();
527 private final CallsManager mCallsManager = CallsManager.getInstance();
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700528 private final Set<Call> mPendingConferenceCalls = new HashSet<>();
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700529 private final CallIdMapper mCallIdMapper = new CallIdMapper("ConnectionService");
Sailesh Nepal664837f2014-07-14 16:31:51 -0700530 private final Map<String, CreateConnectionResponse> mPendingResponses = new HashMap<>();
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700531
532 private Binder mBinder = new Binder();
533 private IConnectionService mServiceInterface;
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700534 private final ConnectionServiceRepository mConnectionServiceRepository;
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700535
536 /**
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700537 * Creates a connection service.
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700538 *
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700539 * @param componentName The component name of the service with which to bind.
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700540 * @param connectionServiceRepository Connection service repository.
Ihab Awadb78b2762014-07-25 15:16:23 -0700541 * @param phoneAccountRegistrar Phone account registrar
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700542 */
543 ConnectionServiceWrapper(
Ihab Awadb78b2762014-07-25 15:16:23 -0700544 ComponentName componentName,
545 ConnectionServiceRepository connectionServiceRepository,
546 PhoneAccountRegistrar phoneAccountRegistrar) {
Santos Cordonf2f14ef2014-07-20 18:00:20 -0700547 super(ConnectionService.SERVICE_INTERFACE, componentName);
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700548 mConnectionServiceRepository = connectionServiceRepository;
Ihab Awadb78b2762014-07-25 15:16:23 -0700549 phoneAccountRegistrar.addListener(new PhoneAccountRegistrar.Listener() {
550 // TODO -- Upon changes to PhoneAccountRegistrar, need to re-wire connections
551 // To do this, we must proxy remote ConnectionService objects
552 });
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700553 }
554
555 /** See {@link IConnectionService#addConnectionServiceAdapter}. */
556 private void addConnectionServiceAdapter(IConnectionServiceAdapter adapter) {
557 if (isServiceValid("addConnectionServiceAdapter")) {
558 try {
Sailesh Nepal3fe8b722014-07-08 10:07:26 -0700559 logOutgoing("addConnectionServiceAdapter %s", adapter);
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700560 mServiceInterface.addConnectionServiceAdapter(adapter);
561 } catch (RemoteException e) {
562 }
563 }
564 }
565
566 /**
Sailesh Nepal664837f2014-07-14 16:31:51 -0700567 * Creates a new connection for a new outgoing call or to attach to an existing incoming call.
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700568 */
Sailesh Nepal664837f2014-07-14 16:31:51 -0700569 void createConnection(final Call call, final CreateConnectionResponse response) {
570 Log.d(this, "createConnection(%s) via %s.", call, getComponentName());
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700571 BindCallback callback = new BindCallback() {
572 @Override
573 public void onSuccess() {
574 String callId = mCallIdMapper.getCallId(call);
Sailesh Nepal664837f2014-07-14 16:31:51 -0700575 mPendingResponses.put(callId, response);
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700576
577 GatewayInfo gatewayInfo = call.getGatewayInfo();
578 Bundle extras = call.getExtras();
579 if (gatewayInfo != null && gatewayInfo.getGatewayProviderPackageName() != null &&
580 gatewayInfo.getOriginalHandle() != null) {
581 extras = (Bundle) extras.clone();
582 extras.putString(
583 NewOutgoingCallIntentBroadcaster.EXTRA_GATEWAY_PROVIDER_PACKAGE,
584 gatewayInfo.getGatewayProviderPackageName());
585 extras.putParcelable(
586 NewOutgoingCallIntentBroadcaster.EXTRA_GATEWAY_ORIGINAL_URI,
587 gatewayInfo.getOriginalHandle());
588 }
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700589
590 try {
Ihab Awadb78b2762014-07-25 15:16:23 -0700591 mServiceInterface.createConnection(
592 call.getConnectionManagerPhoneAccount(),
593 new ConnectionRequest(
594 call.getTargetPhoneAccount(),
595 callId,
596 call.getHandle(),
597 call.getHandlePresentation(),
598 extras,
599 call.getVideoState()),
600 call.isIncoming());
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700601 } catch (RemoteException e) {
Sailesh Nepal664837f2014-07-14 16:31:51 -0700602 Log.e(this, e, "Failure to createConnection -- %s", getComponentName());
603 mPendingResponses.remove(callId).handleCreateConnectionFailed(
Santos Cordonfd6ca442014-07-24 15:34:01 -0700604 DisconnectCause.OUTGOING_FAILURE, e.toString());
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700605 }
606 }
607
608 @Override
609 public void onFailure() {
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700610 Log.e(this, new Exception(), "Failure to call %s", getComponentName());
Santos Cordonfd6ca442014-07-24 15:34:01 -0700611 response.handleCreateConnectionFailed(DisconnectCause.OUTGOING_FAILURE, null);
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700612 }
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
Santos Cordonfd6ca442014-07-24 15:34:01 -0700632 removeCall(call, DisconnectCause.LOCAL, null);
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700633 }
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
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700670 /** @see ConnectionService#disconnect(String) */
671 void disconnect(Call call) {
672 if (isServiceValid("disconnect")) {
673 try {
674 logOutgoing("disconnect %s", mCallIdMapper.getCallId(call));
675 mServiceInterface.disconnect(mCallIdMapper.getCallId(call));
676 } catch (RemoteException e) {
677 }
678 }
679 }
680
Ihab Awadb78b2762014-07-25 15:16:23 -0700681 /** @see ConnectionService#answer(String,int) */
Andrew Lee38931d02014-07-16 10:17:36 -0700682 void answer(Call call, int videoState) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700683 if (isServiceValid("answer")) {
684 try {
Andrew Lee38931d02014-07-16 10:17:36 -0700685 logOutgoing("answer %s %d", mCallIdMapper.getCallId(call), videoState);
686 mServiceInterface.answer(mCallIdMapper.getCallId(call), videoState);
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700687 } catch (RemoteException e) {
688 }
689 }
690 }
691
692 /** @see ConnectionService#reject(String) */
693 void reject(Call call) {
694 if (isServiceValid("reject")) {
695 try {
696 logOutgoing("reject %s", mCallIdMapper.getCallId(call));
697 mServiceInterface.reject(mCallIdMapper.getCallId(call));
698 } catch (RemoteException e) {
699 }
700 }
701 }
702
703 /** @see ConnectionService#playDtmfTone(String,char) */
704 void playDtmfTone(Call call, char digit) {
705 if (isServiceValid("playDtmfTone")) {
706 try {
707 logOutgoing("playDtmfTone %s %c", mCallIdMapper.getCallId(call), digit);
708 mServiceInterface.playDtmfTone(mCallIdMapper.getCallId(call), digit);
709 } catch (RemoteException e) {
710 }
711 }
712 }
713
714 /** @see ConnectionService#stopDtmfTone(String) */
715 void stopDtmfTone(Call call) {
716 if (isServiceValid("stopDtmfTone")) {
717 try {
718 logOutgoing("stopDtmfTone %s", mCallIdMapper.getCallId(call));
719 mServiceInterface.stopDtmfTone(mCallIdMapper.getCallId(call));
720 } catch (RemoteException e) {
721 }
722 }
723 }
724
725 void addCall(Call call) {
726 if (mCallIdMapper.getCallId(call) == null) {
727 mCallIdMapper.addCall(call);
728 }
729 }
730
731 /**
732 * Associates newCall with this connection service by replacing callToReplace.
733 */
734 void replaceCall(Call newCall, Call callToReplace) {
735 Preconditions.checkState(callToReplace.getConnectionService() == this);
736 mCallIdMapper.replaceCall(newCall, callToReplace);
737 }
738
739 void removeCall(Call call) {
Santos Cordonfd6ca442014-07-24 15:34:01 -0700740 removeCall(call, DisconnectCause.ERROR_UNSPECIFIED, null);
741 }
742
743 void removeCall(Call call, int disconnectCause, String disconnectMessage) {
Sailesh Nepal664837f2014-07-14 16:31:51 -0700744 CreateConnectionResponse response = mPendingResponses.remove(mCallIdMapper.getCallId(call));
745 if (response != null) {
Santos Cordonfd6ca442014-07-24 15:34:01 -0700746 response.handleCreateConnectionFailed(disconnectCause, disconnectMessage);
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700747 }
748
749 mCallIdMapper.removeCall(call);
750 }
751
752 void onPostDialContinue(Call call, boolean proceed) {
753 if (isServiceValid("onPostDialContinue")) {
754 try {
755 logOutgoing("onPostDialContinue %s %b", mCallIdMapper.getCallId(call), proceed);
756 mServiceInterface.onPostDialContinue(mCallIdMapper.getCallId(call), proceed);
757 } catch (RemoteException ignored) {
758 }
759 }
760 }
761
762 void onPhoneAccountClicked(Call call) {
763 if (isServiceValid("onPhoneAccountClicked")) {
764 try {
765 logOutgoing("onPhoneAccountClicked %s", mCallIdMapper.getCallId(call));
766 mServiceInterface.onPhoneAccountClicked(mCallIdMapper.getCallId(call));
767 } catch (RemoteException ignored) {
768 }
769 }
770 }
771
772 void conference(final Call conferenceCall, Call call) {
773 if (isServiceValid("conference")) {
774 try {
775 conferenceCall.setConnectionService(this);
776 mPendingConferenceCalls.add(conferenceCall);
777 mHandler.postDelayed(new Runnable() {
778 @Override public void run() {
779 if (mPendingConferenceCalls.remove(conferenceCall)) {
780 conferenceCall.expireConference();
781 Log.i(this, "Conference call expired: %s", conferenceCall);
782 }
783 }
784 }, Timeouts.getConferenceCallExpireMillis());
785
786 logOutgoing("conference %s %s",
787 mCallIdMapper.getCallId(conferenceCall),
788 mCallIdMapper.getCallId(call));
789 mServiceInterface.conference(
790 mCallIdMapper.getCallId(conferenceCall),
791 mCallIdMapper.getCallId(call));
792 } catch (RemoteException ignored) {
793 }
794 }
795 }
796
797 void splitFromConference(Call call) {
798 if (isServiceValid("splitFromConference")) {
799 try {
800 logOutgoing("splitFromConference %s", mCallIdMapper.getCallId(call));
801 mServiceInterface.splitFromConference(mCallIdMapper.getCallId(call));
802 } catch (RemoteException ignored) {
803 }
804 }
805 }
806
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700807 void swapWithBackgroundCall(Call call) {
808 if (isServiceValid("swapWithBackgroundCall")) {
809 try {
810 logOutgoing("swapWithBackgroundCall %s", mCallIdMapper.getCallId(call));
811 mServiceInterface.swapWithBackgroundCall(mCallIdMapper.getCallId(call));
812 } catch (RemoteException ignored) {
813 }
814 }
815 }
816
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700817 /** {@inheritDoc} */
818 @Override
819 protected void setServiceInterface(IBinder binder) {
820 if (binder == null) {
821 // We have lost our service connection. Notify the world that this service is done.
822 // We must notify the adapter before CallsManager. The adapter will force any pending
823 // outgoing calls to try the next service. This needs to happen before CallsManager
824 // tries to clean up any calls still associated with this service.
825 handleConnectionServiceDeath();
826 CallsManager.getInstance().handleConnectionServiceDeath(this);
827 mServiceInterface = null;
828 } else {
829 mServiceInterface = IConnectionService.Stub.asInterface(binder);
830 addConnectionServiceAdapter(mAdapter);
831 }
832 }
833
834 /**
835 * Called when the associated connection service dies.
836 */
837 private void handleConnectionServiceDeath() {
Sailesh Nepal664837f2014-07-14 16:31:51 -0700838 if (!mPendingResponses.isEmpty()) {
839 CreateConnectionResponse[] responses = mPendingResponses.values().toArray(
840 new CreateConnectionResponse[mPendingResponses.values().size()]);
841 mPendingResponses.clear();
842 for (int i = 0; i < responses.length; i++) {
843 responses[i].handleCreateConnectionFailed(DisconnectCause.ERROR_UNSPECIFIED, null);
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700844 }
845 }
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700846 mCallIdMapper.clear();
847 }
848
849 private void logIncoming(String msg, Object... params) {
850 Log.d(this, "ConnectionService -> Telecomm: " + msg, params);
851 }
852
853 private void logOutgoing(String msg, Object... params) {
854 Log.d(this, "Telecomm -> ConnectionService: " + msg, params);
855 }
856
857 private void queryRemoteConnectionServices(final RemoteServiceCallback callback) {
Ihab Awadb78b2762014-07-25 15:16:23 -0700858 PhoneAccountRegistrar registrar = TelecommApp.getInstance().getPhoneAccountRegistrar();
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700859
Ihab Awadb78b2762014-07-25 15:16:23 -0700860 // Only give remote connection services to this connection service if it is listed as
861 // the connection manager.
862 PhoneAccountHandle simCallManager = registrar.getSimCallManager();
Ihab Awadc17294c2014-08-04 19:23:37 -0700863 Log.d(this, "queryRemoteConnectionServices finds simCallManager = %s", simCallManager);
Ihab Awadb78b2762014-07-25 15:16:23 -0700864 if (simCallManager == null ||
865 !simCallManager.getComponentName().equals(getComponentName())) {
866 noRemoteServices(callback);
867 return;
868 }
Sailesh Nepal664837f2014-07-14 16:31:51 -0700869
Ihab Awadb78b2762014-07-25 15:16:23 -0700870 // Make a list of ConnectionServices that are listed as being associated with SIM accounts
871 final Set<ConnectionServiceWrapper> simServices = new HashSet<>();
872 for (PhoneAccountHandle handle : registrar.getEnabledPhoneAccounts()) {
873 PhoneAccount account = registrar.getPhoneAccount(handle);
874 if ((account.getCapabilities() & PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION) != 0) {
875 ConnectionServiceWrapper service =
876 mConnectionServiceRepository.getService(handle.getComponentName());
877 if (service != null) {
878 simServices.add(service);
879 }
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700880 }
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700881 }
Ihab Awadb78b2762014-07-25 15:16:23 -0700882
883 final List<ComponentName> simServiceComponentNames = new ArrayList<>();
884 final List<IBinder> simServiceBinders = new ArrayList<>();
885
886 Log.v(this, "queryRemoteConnectionServices, simServices = %s", simServices);
887
888 for (ConnectionServiceWrapper simService : simServices) {
889 if (simService == this) {
890 // Only happens in the unlikely case that a SIM service is also a SIM call manager
891 continue;
892 }
893
894 final ConnectionServiceWrapper currentSimService = simService;
895
896 currentSimService.mBinder.bind(new BindCallback() {
897 @Override
898 public void onSuccess() {
899 Log.d(this, "Adding simService %s", currentSimService.getComponentName());
900 simServiceComponentNames.add(currentSimService.getComponentName());
901 simServiceBinders.add(currentSimService.mServiceInterface.asBinder());
902 maybeComplete();
903 }
904
905 @Override
906 public void onFailure() {
907 Log.d(this, "Failed simService %s", currentSimService.getComponentName());
908 // We know maybeComplete() will always be a no-op from now on, so go ahead and
909 // signal failure of the entire request
910 noRemoteServices(callback);
911 }
912
913 private void maybeComplete() {
914 if (simServiceComponentNames.size() == simServices.size()) {
915 setRemoteServices(callback, simServiceComponentNames, simServiceBinders);
916 }
917 }
918 });
919 }
920 }
921
922 private void setRemoteServices(
923 RemoteServiceCallback callback,
924 List<ComponentName> componentNames,
925 List<IBinder> binders) {
926 try {
927 callback.onResult(componentNames, binders);
928 } catch (RemoteException e) {
929 Log.e(this, e, "Contacting ConnectionService %s",
930 ConnectionServiceWrapper.this.getComponentName());
931 }
932 }
933
934 private void noRemoteServices(RemoteServiceCallback callback) {
935 try {
936 callback.onResult(Collections.EMPTY_LIST, Collections.EMPTY_LIST);
937 } catch (RemoteException e) {
938 Log.e(this, e, "Contacting ConnectionService %s", this.getComponentName());
939 }
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700940 }
941}