blob: f6ba53b0cf86cad036826ada47a2294d06eddc1e [file] [log] [blame]
Ihab Awad542e0ea2014-05-16 10:22:16 -07001/*
2 * Copyright (C) 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
Tyler Gunnef9f6f92014-09-12 22:16:17 -070017package android.telecom;
Ihab Awad542e0ea2014-05-16 10:22:16 -070018
Santos Cordon5c6fa952014-07-20 17:47:12 -070019import android.annotation.SdkConstant;
Sailesh Nepal2a46b902014-07-04 17:21:07 -070020import android.app.Service;
Santos Cordon52d8a152014-06-17 19:08:45 -070021import android.content.ComponentName;
Santos Cordon5c6fa952014-07-20 17:47:12 -070022import android.content.Intent;
Ihab Awad542e0ea2014-05-16 10:22:16 -070023import android.net.Uri;
Santos Cordon6b7f9552015-05-27 17:21:45 -070024import android.os.Bundle;
Santos Cordon52d8a152014-06-17 19:08:45 -070025import android.os.Handler;
26import android.os.IBinder;
27import android.os.Looper;
Sailesh Nepal2a46b902014-07-04 17:21:07 -070028import android.os.Message;
Andrew Lee14185762014-07-25 09:41:56 -070029
Sailesh Nepal2a46b902014-07-04 17:21:07 -070030import com.android.internal.os.SomeArgs;
Tyler Gunnef9f6f92014-09-12 22:16:17 -070031import com.android.internal.telecom.IConnectionService;
32import com.android.internal.telecom.IConnectionServiceAdapter;
33import com.android.internal.telecom.RemoteServiceCallback;
Santos Cordon52d8a152014-06-17 19:08:45 -070034
Ihab Awad5d0410f2014-07-30 10:07:40 -070035import java.util.ArrayList;
Santos Cordonb6939982014-06-04 20:20:58 -070036import java.util.Collection;
Santos Cordon7c7bc7f2014-07-28 18:15:48 -070037import java.util.Collections;
Santos Cordon52d8a152014-06-17 19:08:45 -070038import java.util.List;
Ihab Awad542e0ea2014-05-16 10:22:16 -070039import java.util.Map;
Santos Cordon823fd3c2014-08-07 18:35:18 -070040import java.util.UUID;
mike dooley95e80702014-09-18 14:07:52 -070041import java.util.concurrent.ConcurrentHashMap;
Ihab Awad542e0ea2014-05-16 10:22:16 -070042
43/**
Santos Cordon895d4b82015-06-25 16:41:48 -070044 * An abstract service that should be implemented by any apps which can make phone calls (VoIP or
45 * otherwise) and want those calls to be integrated into the built-in phone app.
Santos Cordona663f862014-10-29 13:49:58 -070046 * Once implemented, the {@code ConnectionService} needs two additional steps before it will be
47 * integrated into the phone app:
48 * <p>
49 * 1. <i>Registration in AndroidManifest.xml</i>
50 * <br/>
51 * <pre>
52 * &lt;service android:name="com.example.package.MyConnectionService"
53 * android:label="@string/some_label_for_my_connection_service"
Yorke Lee249c12e2015-05-13 15:59:29 -070054 * android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"&gt;
Santos Cordona663f862014-10-29 13:49:58 -070055 * &lt;intent-filter&gt;
56 * &lt;action android:name="android.telecom.ConnectionService" /&gt;
57 * &lt;/intent-filter&gt;
58 * &lt;/service&gt;
59 * </pre>
60 * <p>
61 * 2. <i> Registration of {@link PhoneAccount} with {@link TelecomManager}.</i>
62 * <br/>
63 * See {@link PhoneAccount} and {@link TelecomManager#registerPhoneAccount} for more information.
64 * <p>
Santos Cordon895d4b82015-06-25 16:41:48 -070065 * Once registered and enabled by the user in the phone app settings, telecom will bind to a
Santos Cordona663f862014-10-29 13:49:58 -070066 * {@code ConnectionService} implementation when it wants that {@code ConnectionService} to place
67 * a call or the service has indicated that is has an incoming call through
68 * {@link TelecomManager#addNewIncomingCall}. The {@code ConnectionService} can then expect a call
69 * to {@link #onCreateIncomingConnection} or {@link #onCreateOutgoingConnection} wherein it
70 * should provide a new instance of a {@link Connection} object. It is through this
71 * {@link Connection} object that telecom receives state updates and the {@code ConnectionService}
72 * receives call-commands such as answer, reject, hold and disconnect.
73 * <p>
74 * When there are no more live calls, telecom will unbind from the {@code ConnectionService}.
Ihab Awad542e0ea2014-05-16 10:22:16 -070075 */
Sailesh Nepal2a46b902014-07-04 17:21:07 -070076public abstract class ConnectionService extends Service {
Santos Cordon5c6fa952014-07-20 17:47:12 -070077 /**
78 * The {@link Intent} that must be declared as handled by the service.
79 */
Ihab Awadb19a0bc2014-08-07 19:46:01 -070080 @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
Tyler Gunnef9f6f92014-09-12 22:16:17 -070081 public static final String SERVICE_INTERFACE = "android.telecom.ConnectionService";
Santos Cordon5c6fa952014-07-20 17:47:12 -070082
Ihab Awad542e0ea2014-05-16 10:22:16 -070083 // Flag controlling whether PII is emitted into the logs
Ihab Awad60ac30b2014-05-20 22:32:12 -070084 private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
Ihab Awad542e0ea2014-05-16 10:22:16 -070085
Ihab Awad8aecfed2014-08-08 17:06:11 -070086 private static final int MSG_ADD_CONNECTION_SERVICE_ADAPTER = 1;
Sailesh Nepalc5b01572014-07-14 16:29:44 -070087 private static final int MSG_CREATE_CONNECTION = 2;
Sailesh Nepal2a46b902014-07-04 17:21:07 -070088 private static final int MSG_ABORT = 3;
Sailesh Nepalc5b01572014-07-14 16:29:44 -070089 private static final int MSG_ANSWER = 4;
90 private static final int MSG_REJECT = 5;
91 private static final int MSG_DISCONNECT = 6;
92 private static final int MSG_HOLD = 7;
93 private static final int MSG_UNHOLD = 8;
Yorke Lee4af59352015-05-13 14:14:54 -070094 private static final int MSG_ON_CALL_AUDIO_STATE_CHANGED = 9;
Sailesh Nepalc5b01572014-07-14 16:29:44 -070095 private static final int MSG_PLAY_DTMF_TONE = 10;
96 private static final int MSG_STOP_DTMF_TONE = 11;
97 private static final int MSG_CONFERENCE = 12;
98 private static final int MSG_SPLIT_FROM_CONFERENCE = 13;
Ihab Awadb19a0bc2014-08-07 19:46:01 -070099 private static final int MSG_ON_POST_DIAL_CONTINUE = 14;
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700100 private static final int MSG_REMOVE_CONNECTION_SERVICE_ADAPTER = 16;
Tyler Gunnbe74de02014-08-29 14:51:48 -0700101 private static final int MSG_ANSWER_VIDEO = 17;
Santos Cordona4868042014-09-04 17:39:22 -0700102 private static final int MSG_MERGE_CONFERENCE = 18;
103 private static final int MSG_SWAP_CONFERENCE = 19;
Santos Cordon7c7bc7f2014-07-28 18:15:48 -0700104
Sailesh Nepalcf7020b2014-08-20 10:07:19 -0700105 private static Connection sNullConnection;
106
mike dooley95e80702014-09-18 14:07:52 -0700107 private final Map<String, Connection> mConnectionById = new ConcurrentHashMap<>();
108 private final Map<Connection, String> mIdByConnection = new ConcurrentHashMap<>();
109 private final Map<String, Conference> mConferenceById = new ConcurrentHashMap<>();
110 private final Map<Conference, String> mIdByConference = new ConcurrentHashMap<>();
Ihab Awadb8e85c72014-08-23 20:34:57 -0700111 private final RemoteConnectionManager mRemoteConnectionManager =
112 new RemoteConnectionManager(this);
Ihab Awad5d0410f2014-07-30 10:07:40 -0700113 private final List<Runnable> mPreInitializationConnectionRequests = new ArrayList<>();
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700114 private final ConnectionServiceAdapter mAdapter = new ConnectionServiceAdapter();
Ihab Awad542e0ea2014-05-16 10:22:16 -0700115
Santos Cordon823fd3c2014-08-07 18:35:18 -0700116 private boolean mAreAccountsInitialized = false;
Santos Cordon0159ac02014-08-21 14:28:11 -0700117 private Conference sNullConference;
Tyler Gunnf0500bd2015-09-01 10:59:48 -0700118 private Object mIdSyncRoot = new Object();
119 private int mId = 0;
Santos Cordon823fd3c2014-08-07 18:35:18 -0700120
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700121 private final IBinder mBinder = new IConnectionService.Stub() {
122 @Override
123 public void addConnectionServiceAdapter(IConnectionServiceAdapter adapter) {
Ihab Awad8aecfed2014-08-08 17:06:11 -0700124 mHandler.obtainMessage(MSG_ADD_CONNECTION_SERVICE_ADAPTER, adapter).sendToTarget();
125 }
126
127 public void removeConnectionServiceAdapter(IConnectionServiceAdapter adapter) {
128 mHandler.obtainMessage(MSG_REMOVE_CONNECTION_SERVICE_ADAPTER, adapter).sendToTarget();
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700129 }
130
131 @Override
Ihab Awadf8b69882014-07-25 15:14:01 -0700132 public void createConnection(
133 PhoneAccountHandle connectionManagerPhoneAccount,
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700134 String id,
Ihab Awadf8b69882014-07-25 15:14:01 -0700135 ConnectionRequest request,
Yorke Leec3cf9822014-10-02 09:38:39 -0700136 boolean isIncoming,
137 boolean isUnknown) {
Ihab Awadf8b69882014-07-25 15:14:01 -0700138 SomeArgs args = SomeArgs.obtain();
139 args.arg1 = connectionManagerPhoneAccount;
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700140 args.arg2 = id;
141 args.arg3 = request;
Ihab Awadf8b69882014-07-25 15:14:01 -0700142 args.argi1 = isIncoming ? 1 : 0;
Yorke Leec3cf9822014-10-02 09:38:39 -0700143 args.argi2 = isUnknown ? 1 : 0;
Ihab Awadf8b69882014-07-25 15:14:01 -0700144 mHandler.obtainMessage(MSG_CREATE_CONNECTION, args).sendToTarget();
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700145 }
146
147 @Override
148 public void abort(String callId) {
149 mHandler.obtainMessage(MSG_ABORT, callId).sendToTarget();
150 }
151
152 @Override
Tyler Gunnbe74de02014-08-29 14:51:48 -0700153 public void answerVideo(String callId, int videoState) {
Andrew Lee8da4c3c2014-07-16 10:11:42 -0700154 SomeArgs args = SomeArgs.obtain();
155 args.arg1 = callId;
Evan Charltonbf11f982014-07-20 22:06:28 -0700156 args.argi1 = videoState;
Tyler Gunnbe74de02014-08-29 14:51:48 -0700157 mHandler.obtainMessage(MSG_ANSWER_VIDEO, args).sendToTarget();
158 }
159
160 @Override
161 public void answer(String callId) {
162 mHandler.obtainMessage(MSG_ANSWER, callId).sendToTarget();
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700163 }
164
165 @Override
166 public void reject(String callId) {
167 mHandler.obtainMessage(MSG_REJECT, callId).sendToTarget();
168 }
169
170 @Override
171 public void disconnect(String callId) {
172 mHandler.obtainMessage(MSG_DISCONNECT, callId).sendToTarget();
173 }
174
175 @Override
176 public void hold(String callId) {
177 mHandler.obtainMessage(MSG_HOLD, callId).sendToTarget();
178 }
179
180 @Override
181 public void unhold(String callId) {
182 mHandler.obtainMessage(MSG_UNHOLD, callId).sendToTarget();
183 }
184
185 @Override
Yorke Lee4af59352015-05-13 14:14:54 -0700186 public void onCallAudioStateChanged(String callId, CallAudioState callAudioState) {
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700187 SomeArgs args = SomeArgs.obtain();
188 args.arg1 = callId;
Yorke Lee4af59352015-05-13 14:14:54 -0700189 args.arg2 = callAudioState;
190 mHandler.obtainMessage(MSG_ON_CALL_AUDIO_STATE_CHANGED, args).sendToTarget();
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700191 }
192
193 @Override
194 public void playDtmfTone(String callId, char digit) {
195 mHandler.obtainMessage(MSG_PLAY_DTMF_TONE, digit, 0, callId).sendToTarget();
196 }
197
198 @Override
199 public void stopDtmfTone(String callId) {
200 mHandler.obtainMessage(MSG_STOP_DTMF_TONE, callId).sendToTarget();
201 }
202
203 @Override
Santos Cordon823fd3c2014-08-07 18:35:18 -0700204 public void conference(String callId1, String callId2) {
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700205 SomeArgs args = SomeArgs.obtain();
Santos Cordon823fd3c2014-08-07 18:35:18 -0700206 args.arg1 = callId1;
207 args.arg2 = callId2;
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700208 mHandler.obtainMessage(MSG_CONFERENCE, args).sendToTarget();
209 }
210
211 @Override
212 public void splitFromConference(String callId) {
213 mHandler.obtainMessage(MSG_SPLIT_FROM_CONFERENCE, callId).sendToTarget();
214 }
215
216 @Override
Santos Cordona4868042014-09-04 17:39:22 -0700217 public void mergeConference(String callId) {
218 mHandler.obtainMessage(MSG_MERGE_CONFERENCE, callId).sendToTarget();
219 }
220
221 @Override
222 public void swapConference(String callId) {
223 mHandler.obtainMessage(MSG_SWAP_CONFERENCE, callId).sendToTarget();
224 }
225
226 @Override
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700227 public void onPostDialContinue(String callId, boolean proceed) {
228 SomeArgs args = SomeArgs.obtain();
229 args.arg1 = callId;
230 args.argi1 = proceed ? 1 : 0;
231 mHandler.obtainMessage(MSG_ON_POST_DIAL_CONTINUE, args).sendToTarget();
232 }
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700233 };
234
235 private final Handler mHandler = new Handler(Looper.getMainLooper()) {
236 @Override
237 public void handleMessage(Message msg) {
238 switch (msg.what) {
Ihab Awad8aecfed2014-08-08 17:06:11 -0700239 case MSG_ADD_CONNECTION_SERVICE_ADAPTER:
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700240 mAdapter.addAdapter((IConnectionServiceAdapter) msg.obj);
241 onAdapterAttached();
242 break;
Ihab Awad8aecfed2014-08-08 17:06:11 -0700243 case MSG_REMOVE_CONNECTION_SERVICE_ADAPTER:
244 mAdapter.removeAdapter((IConnectionServiceAdapter) msg.obj);
245 break;
Ihab Awadf8b69882014-07-25 15:14:01 -0700246 case MSG_CREATE_CONNECTION: {
247 SomeArgs args = (SomeArgs) msg.obj;
248 try {
Ihab Awad5d0410f2014-07-30 10:07:40 -0700249 final PhoneAccountHandle connectionManagerPhoneAccount =
Ihab Awadf8b69882014-07-25 15:14:01 -0700250 (PhoneAccountHandle) args.arg1;
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700251 final String id = (String) args.arg2;
252 final ConnectionRequest request = (ConnectionRequest) args.arg3;
Ihab Awad5d0410f2014-07-30 10:07:40 -0700253 final boolean isIncoming = args.argi1 == 1;
Yorke Leec3cf9822014-10-02 09:38:39 -0700254 final boolean isUnknown = args.argi2 == 1;
Ihab Awad5d0410f2014-07-30 10:07:40 -0700255 if (!mAreAccountsInitialized) {
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700256 Log.d(this, "Enqueueing pre-init request %s", id);
Ihab Awad5d0410f2014-07-30 10:07:40 -0700257 mPreInitializationConnectionRequests.add(new Runnable() {
258 @Override
259 public void run() {
260 createConnection(
261 connectionManagerPhoneAccount,
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700262 id,
Ihab Awad5d0410f2014-07-30 10:07:40 -0700263 request,
Yorke Leec3cf9822014-10-02 09:38:39 -0700264 isIncoming,
265 isUnknown);
Ihab Awad5d0410f2014-07-30 10:07:40 -0700266 }
267 });
268 } else {
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700269 createConnection(
270 connectionManagerPhoneAccount,
271 id,
272 request,
Yorke Leec3cf9822014-10-02 09:38:39 -0700273 isIncoming,
274 isUnknown);
Ihab Awad5d0410f2014-07-30 10:07:40 -0700275 }
Ihab Awadf8b69882014-07-25 15:14:01 -0700276 } finally {
277 args.recycle();
278 }
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700279 break;
Ihab Awadf8b69882014-07-25 15:14:01 -0700280 }
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700281 case MSG_ABORT:
282 abort((String) msg.obj);
283 break;
Tyler Gunnbe74de02014-08-29 14:51:48 -0700284 case MSG_ANSWER:
285 answer((String) msg.obj);
286 break;
287 case MSG_ANSWER_VIDEO: {
Andrew Lee8da4c3c2014-07-16 10:11:42 -0700288 SomeArgs args = (SomeArgs) msg.obj;
289 try {
290 String callId = (String) args.arg1;
Evan Charltonbf11f982014-07-20 22:06:28 -0700291 int videoState = args.argi1;
Tyler Gunnbe74de02014-08-29 14:51:48 -0700292 answerVideo(callId, videoState);
Andrew Lee8da4c3c2014-07-16 10:11:42 -0700293 } finally {
294 args.recycle();
295 }
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700296 break;
Andrew Lee8da4c3c2014-07-16 10:11:42 -0700297 }
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700298 case MSG_REJECT:
299 reject((String) msg.obj);
300 break;
301 case MSG_DISCONNECT:
302 disconnect((String) msg.obj);
303 break;
304 case MSG_HOLD:
305 hold((String) msg.obj);
306 break;
307 case MSG_UNHOLD:
308 unhold((String) msg.obj);
309 break;
Yorke Lee4af59352015-05-13 14:14:54 -0700310 case MSG_ON_CALL_AUDIO_STATE_CHANGED: {
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700311 SomeArgs args = (SomeArgs) msg.obj;
312 try {
313 String callId = (String) args.arg1;
Yorke Lee4af59352015-05-13 14:14:54 -0700314 CallAudioState audioState = (CallAudioState) args.arg2;
315 onCallAudioStateChanged(callId, new CallAudioState(audioState));
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700316 } finally {
317 args.recycle();
318 }
319 break;
320 }
321 case MSG_PLAY_DTMF_TONE:
322 playDtmfTone((String) msg.obj, (char) msg.arg1);
323 break;
324 case MSG_STOP_DTMF_TONE:
325 stopDtmfTone((String) msg.obj);
326 break;
327 case MSG_CONFERENCE: {
328 SomeArgs args = (SomeArgs) msg.obj;
329 try {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700330 String callId1 = (String) args.arg1;
331 String callId2 = (String) args.arg2;
332 conference(callId1, callId2);
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700333 } finally {
334 args.recycle();
335 }
336 break;
337 }
338 case MSG_SPLIT_FROM_CONFERENCE:
339 splitFromConference((String) msg.obj);
340 break;
Santos Cordona4868042014-09-04 17:39:22 -0700341 case MSG_MERGE_CONFERENCE:
342 mergeConference((String) msg.obj);
343 break;
344 case MSG_SWAP_CONFERENCE:
345 swapConference((String) msg.obj);
346 break;
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700347 case MSG_ON_POST_DIAL_CONTINUE: {
348 SomeArgs args = (SomeArgs) msg.obj;
349 try {
350 String callId = (String) args.arg1;
351 boolean proceed = (args.argi1 == 1);
352 onPostDialContinue(callId, proceed);
353 } finally {
354 args.recycle();
355 }
356 break;
357 }
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700358 default:
359 break;
360 }
361 }
362 };
363
Santos Cordon823fd3c2014-08-07 18:35:18 -0700364 private final Conference.Listener mConferenceListener = new Conference.Listener() {
365 @Override
366 public void onStateChanged(Conference conference, int oldState, int newState) {
367 String id = mIdByConference.get(conference);
368 switch (newState) {
369 case Connection.STATE_ACTIVE:
370 mAdapter.setActive(id);
371 break;
372 case Connection.STATE_HOLDING:
373 mAdapter.setOnHold(id);
374 break;
375 case Connection.STATE_DISCONNECTED:
376 // handled by onDisconnected
377 break;
378 }
379 }
380
381 @Override
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700382 public void onDisconnected(Conference conference, DisconnectCause disconnectCause) {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700383 String id = mIdByConference.get(conference);
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700384 mAdapter.setDisconnected(id, disconnectCause);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700385 }
386
387 @Override
388 public void onConnectionAdded(Conference conference, Connection connection) {
389 }
390
391 @Override
392 public void onConnectionRemoved(Conference conference, Connection connection) {
393 }
394
395 @Override
Ihab Awad50e35062014-09-30 09:17:03 -0700396 public void onConferenceableConnectionsChanged(
397 Conference conference, List<Connection> conferenceableConnections) {
398 mAdapter.setConferenceableConnections(
399 mIdByConference.get(conference),
400 createConnectionIdList(conferenceableConnections));
401 }
402
403 @Override
Santos Cordon823fd3c2014-08-07 18:35:18 -0700404 public void onDestroyed(Conference conference) {
405 removeConference(conference);
406 }
407
408 @Override
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800409 public void onConnectionCapabilitiesChanged(
410 Conference conference,
411 int connectionCapabilities) {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700412 String id = mIdByConference.get(conference);
413 Log.d(this, "call capabilities: conference: %s",
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800414 Connection.capabilitiesToString(connectionCapabilities));
415 mAdapter.setConnectionCapabilities(id, connectionCapabilities);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700416 }
Rekha Kumar07366812015-03-24 16:42:31 -0700417
418 @Override
419 public void onVideoStateChanged(Conference c, int videoState) {
420 String id = mIdByConference.get(c);
421 Log.d(this, "onVideoStateChanged set video state %d", videoState);
422 mAdapter.setVideoState(id, videoState);
423 }
424
425 @Override
426 public void onVideoProviderChanged(Conference c, Connection.VideoProvider videoProvider) {
427 String id = mIdByConference.get(c);
428 Log.d(this, "onVideoProviderChanged: Connection: %s, VideoProvider: %s", c,
429 videoProvider);
430 mAdapter.setVideoProvider(id, videoProvider);
431 }
Andrew Lee0f51da32015-04-16 13:11:55 -0700432
433 @Override
Andrew Leeedc625f2015-04-14 13:38:12 -0700434 public void onStatusHintsChanged(Conference conference, StatusHints statusHints) {
435 String id = mIdByConference.get(conference);
436 mAdapter.setStatusHints(id, statusHints);
437 }
Santos Cordon6b7f9552015-05-27 17:21:45 -0700438
439 @Override
440 public void onExtrasChanged(Conference conference, Bundle extras) {
441 String id = mIdByConference.get(conference);
442 mAdapter.setExtras(id, extras);
443 }
Santos Cordon823fd3c2014-08-07 18:35:18 -0700444 };
445
Ihab Awad542e0ea2014-05-16 10:22:16 -0700446 private final Connection.Listener mConnectionListener = new Connection.Listener() {
447 @Override
448 public void onStateChanged(Connection c, int state) {
449 String id = mIdByConnection.get(c);
Ihab Awad42b30e12014-05-22 09:49:34 -0700450 Log.d(this, "Adapter set state %s %s", id, Connection.stateToString(state));
Ihab Awad542e0ea2014-05-16 10:22:16 -0700451 switch (state) {
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700452 case Connection.STATE_ACTIVE:
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700453 mAdapter.setActive(id);
Ihab Awad542e0ea2014-05-16 10:22:16 -0700454 break;
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700455 case Connection.STATE_DIALING:
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700456 mAdapter.setDialing(id);
Ihab Awad542e0ea2014-05-16 10:22:16 -0700457 break;
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700458 case Connection.STATE_DISCONNECTED:
Ihab Awad542e0ea2014-05-16 10:22:16 -0700459 // Handled in onDisconnected()
460 break;
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700461 case Connection.STATE_HOLDING:
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700462 mAdapter.setOnHold(id);
Ihab Awad542e0ea2014-05-16 10:22:16 -0700463 break;
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700464 case Connection.STATE_NEW:
Tyler Gunnef9f6f92014-09-12 22:16:17 -0700465 // Nothing to tell Telecom
Ihab Awad542e0ea2014-05-16 10:22:16 -0700466 break;
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700467 case Connection.STATE_RINGING:
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700468 mAdapter.setRinging(id);
Ihab Awad542e0ea2014-05-16 10:22:16 -0700469 break;
470 }
471 }
472
473 @Override
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700474 public void onDisconnected(Connection c, DisconnectCause disconnectCause) {
Ihab Awad542e0ea2014-05-16 10:22:16 -0700475 String id = mIdByConnection.get(c);
Andrew Lee26786392014-09-16 18:14:59 -0700476 Log.d(this, "Adapter set disconnected %s", disconnectCause);
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700477 mAdapter.setDisconnected(id, disconnectCause);
Ihab Awad542e0ea2014-05-16 10:22:16 -0700478 }
479
480 @Override
Tyler Gunnaa07df82014-07-17 07:50:22 -0700481 public void onVideoStateChanged(Connection c, int videoState) {
482 String id = mIdByConnection.get(c);
483 Log.d(this, "Adapter set video state %d", videoState);
484 mAdapter.setVideoState(id, videoState);
485 }
486
487 @Override
Andrew Lee100e2932014-09-08 15:34:24 -0700488 public void onAddressChanged(Connection c, Uri address, int presentation) {
Sailesh Nepal61203862014-07-11 14:50:13 -0700489 String id = mIdByConnection.get(c);
Andrew Lee100e2932014-09-08 15:34:24 -0700490 mAdapter.setAddress(id, address, presentation);
Sailesh Nepal61203862014-07-11 14:50:13 -0700491 }
492
493 @Override
494 public void onCallerDisplayNameChanged(
495 Connection c, String callerDisplayName, int presentation) {
496 String id = mIdByConnection.get(c);
497 mAdapter.setCallerDisplayName(id, callerDisplayName, presentation);
Ihab Awad542e0ea2014-05-16 10:22:16 -0700498 }
499
500 @Override
Ihab Awad542e0ea2014-05-16 10:22:16 -0700501 public void onDestroyed(Connection c) {
502 removeConnection(c);
503 }
Ihab Awadf8358972014-05-28 16:46:42 -0700504
505 @Override
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700506 public void onPostDialWait(Connection c, String remaining) {
Sailesh Nepal091768c2014-06-30 15:15:23 -0700507 String id = mIdByConnection.get(c);
508 Log.d(this, "Adapter onPostDialWait %s, %s", c, remaining);
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700509 mAdapter.onPostDialWait(id, remaining);
Sailesh Nepal091768c2014-06-30 15:15:23 -0700510 }
511
512 @Override
Nancy Chen27d1c2d2014-12-15 16:12:50 -0800513 public void onPostDialChar(Connection c, char nextChar) {
514 String id = mIdByConnection.get(c);
515 Log.d(this, "Adapter onPostDialChar %s, %s", c, nextChar);
516 mAdapter.onPostDialChar(id, nextChar);
517 }
518
519 @Override
Andrew Lee100e2932014-09-08 15:34:24 -0700520 public void onRingbackRequested(Connection c, boolean ringback) {
Ihab Awadf8358972014-05-28 16:46:42 -0700521 String id = mIdByConnection.get(c);
522 Log.d(this, "Adapter onRingback %b", ringback);
Andrew Lee100e2932014-09-08 15:34:24 -0700523 mAdapter.setRingbackRequested(id, ringback);
Ihab Awadf8358972014-05-28 16:46:42 -0700524 }
Santos Cordonb6939982014-06-04 20:20:58 -0700525
526 @Override
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800527 public void onConnectionCapabilitiesChanged(Connection c, int capabilities) {
Santos Cordonb6939982014-06-04 20:20:58 -0700528 String id = mIdByConnection.get(c);
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700529 Log.d(this, "capabilities: parcelableconnection: %s",
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800530 Connection.capabilitiesToString(capabilities));
531 mAdapter.setConnectionCapabilities(id, capabilities);
Santos Cordonb6939982014-06-04 20:20:58 -0700532 }
533
Santos Cordonb6939982014-06-04 20:20:58 -0700534 @Override
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700535 public void onVideoProviderChanged(Connection c, Connection.VideoProvider videoProvider) {
Andrew Lee5ffbe8b2014-06-20 16:29:33 -0700536 String id = mIdByConnection.get(c);
Rekha Kumar07366812015-03-24 16:42:31 -0700537 Log.d(this, "onVideoProviderChanged: Connection: %s, VideoProvider: %s", c,
538 videoProvider);
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700539 mAdapter.setVideoProvider(id, videoProvider);
Andrew Lee5ffbe8b2014-06-20 16:29:33 -0700540 }
Sailesh Nepal33aaae42014-07-07 22:49:44 -0700541
542 @Override
Sailesh Nepal001bbbb2014-07-15 14:40:39 -0700543 public void onAudioModeIsVoipChanged(Connection c, boolean isVoip) {
Sailesh Nepal33aaae42014-07-07 22:49:44 -0700544 String id = mIdByConnection.get(c);
Andrew Lee100e2932014-09-08 15:34:24 -0700545 mAdapter.setIsVoipAudioMode(id, isVoip);
Sailesh Nepal33aaae42014-07-07 22:49:44 -0700546 }
Sailesh Nepale7ef59a2014-07-08 21:48:22 -0700547
548 @Override
Sailesh Nepal001bbbb2014-07-15 14:40:39 -0700549 public void onStatusHintsChanged(Connection c, StatusHints statusHints) {
Sailesh Nepale7ef59a2014-07-08 21:48:22 -0700550 String id = mIdByConnection.get(c);
551 mAdapter.setStatusHints(id, statusHints);
552 }
Sailesh Nepal2ab88cc2014-07-18 14:49:18 -0700553
554 @Override
Tyler Gunn6d76ca02014-11-17 15:49:51 -0800555 public void onConferenceablesChanged(
Tyler Gunndf2cbc82015-04-20 09:13:01 -0700556 Connection connection, List<Conferenceable> conferenceables) {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700557 mAdapter.setConferenceableConnections(
558 mIdByConnection.get(connection),
Tyler Gunn6d76ca02014-11-17 15:49:51 -0800559 createIdList(conferenceables));
Santos Cordon7c7bc7f2014-07-28 18:15:48 -0700560 }
Santos Cordon823fd3c2014-08-07 18:35:18 -0700561
562 @Override
563 public void onConferenceChanged(Connection connection, Conference conference) {
564 String id = mIdByConnection.get(connection);
565 if (id != null) {
566 String conferenceId = null;
567 if (conference != null) {
568 conferenceId = mIdByConference.get(conference);
569 }
570 mAdapter.setIsConferenced(id, conferenceId);
571 }
572 }
Anthony Lee17455a32015-04-24 15:25:29 -0700573
574 @Override
575 public void onConferenceMergeFailed(Connection connection) {
576 String id = mIdByConnection.get(connection);
577 if (id != null) {
578 mAdapter.onConferenceMergeFailed(id);
579 }
580 }
Santos Cordon6b7f9552015-05-27 17:21:45 -0700581
582 @Override
583 public void onExtrasChanged(Connection connection, Bundle extras) {
584 String id = mIdByConnection.get(connection);
585 if (id != null) {
586 mAdapter.setExtras(id, extras);
587 }
588 }
Ihab Awad542e0ea2014-05-16 10:22:16 -0700589 };
590
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700591 /** {@inheritDoc} */
Ihab Awad542e0ea2014-05-16 10:22:16 -0700592 @Override
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700593 public final IBinder onBind(Intent intent) {
594 return mBinder;
595 }
596
Santos Cordon29f2f2e2014-09-11 19:50:24 -0700597 /** {@inheritDoc} */
598 @Override
599 public boolean onUnbind(Intent intent) {
600 endAllConnections();
601 return super.onUnbind(intent);
602 }
603
Sailesh Nepalc5b01572014-07-14 16:29:44 -0700604 /**
Tyler Gunnef9f6f92014-09-12 22:16:17 -0700605 * This can be used by telecom to either create a new outgoing call or attach to an existing
606 * incoming call. In either case, telecom will cycle through a set of services and call
Sailesh Nepalc5b01572014-07-14 16:29:44 -0700607 * createConnection util a connection service cancels the process or completes it successfully.
608 */
Ihab Awadf8b69882014-07-25 15:14:01 -0700609 private void createConnection(
610 final PhoneAccountHandle callManagerAccount,
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700611 final String callId,
Ihab Awadf8b69882014-07-25 15:14:01 -0700612 final ConnectionRequest request,
Yorke Leec3cf9822014-10-02 09:38:39 -0700613 boolean isIncoming,
614 boolean isUnknown) {
Sailesh Nepalcf7020b2014-08-20 10:07:19 -0700615 Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " +
Tyler Gunnf0500bd2015-09-01 10:59:48 -0700616 "isIncoming: %b, isUnknown: %b", callManagerAccount, callId, request,
617 isIncoming,
Yorke Leec3cf9822014-10-02 09:38:39 -0700618 isUnknown);
Ihab Awad542e0ea2014-05-16 10:22:16 -0700619
Yorke Leec3cf9822014-10-02 09:38:39 -0700620 Connection connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request)
621 : isIncoming ? onCreateIncomingConnection(callManagerAccount, request)
Ihab Awad6107bab2014-08-18 09:23:25 -0700622 : onCreateOutgoingConnection(callManagerAccount, request);
Sailesh Nepalcf7020b2014-08-20 10:07:19 -0700623 Log.d(this, "createConnection, connection: %s", connection);
624 if (connection == null) {
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700625 connection = Connection.createFailedConnection(
626 new DisconnectCause(DisconnectCause.ERROR));
Sailesh Nepalc5b01572014-07-14 16:29:44 -0700627 }
Ihab Awad542e0ea2014-05-16 10:22:16 -0700628
Tyler Gunnf0500bd2015-09-01 10:59:48 -0700629 connection.setTelecomCallId(callId);
Sailesh Nepalcf7020b2014-08-20 10:07:19 -0700630 if (connection.getState() != Connection.STATE_DISCONNECTED) {
Ihab Awad6107bab2014-08-18 09:23:25 -0700631 addConnection(callId, connection);
632 }
633
Andrew Lee100e2932014-09-08 15:34:24 -0700634 Uri address = connection.getAddress();
635 String number = address == null ? "null" : address.getSchemeSpecificPart();
Sailesh Nepalcf7020b2014-08-20 10:07:19 -0700636 Log.v(this, "createConnection, number: %s, state: %s, capabilities: %s",
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700637 Connection.toLogSafePhoneNumber(number),
Sailesh Nepalcf7020b2014-08-20 10:07:19 -0700638 Connection.stateToString(connection.getState()),
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800639 Connection.capabilitiesToString(connection.getConnectionCapabilities()));
Santos Cordon7c7bc7f2014-07-28 18:15:48 -0700640
Sailesh Nepalcf7020b2014-08-20 10:07:19 -0700641 Log.d(this, "createConnection, calling handleCreateConnectionSuccessful %s", callId);
Ihab Awad6107bab2014-08-18 09:23:25 -0700642 mAdapter.handleCreateConnectionComplete(
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700643 callId,
Evan Charltonbf11f982014-07-20 22:06:28 -0700644 request,
645 new ParcelableConnection(
646 request.getAccountHandle(),
647 connection.getState(),
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800648 connection.getConnectionCapabilities(),
Andrew Lee100e2932014-09-08 15:34:24 -0700649 connection.getAddress(),
650 connection.getAddressPresentation(),
Evan Charltonbf11f982014-07-20 22:06:28 -0700651 connection.getCallerDisplayName(),
652 connection.getCallerDisplayNamePresentation(),
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700653 connection.getVideoProvider() == null ?
654 null : connection.getVideoProvider().getInterface(),
Sailesh Nepal8b9d3ca2014-08-14 17:39:34 -0700655 connection.getVideoState(),
Andrew Lee100e2932014-09-08 15:34:24 -0700656 connection.isRingbackRequested(),
Sailesh Nepal8b9d3ca2014-08-14 17:39:34 -0700657 connection.getAudioModeIsVoip(),
Roshan Piuse927ec02015-07-15 15:47:21 -0700658 connection.getConnectTimeMillis(),
Ihab Awad6107bab2014-08-18 09:23:25 -0700659 connection.getStatusHints(),
Sailesh Nepalcf7020b2014-08-20 10:07:19 -0700660 connection.getDisconnectCause(),
Santos Cordon6b7f9552015-05-27 17:21:45 -0700661 createIdList(connection.getConferenceables()),
662 connection.getExtras()));
Shriram Ganesh6bf35ac2014-12-11 17:53:38 -0800663 if (isUnknown) {
664 triggerConferenceRecalculate();
665 }
Evan Charltonbf11f982014-07-20 22:06:28 -0700666 }
667
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700668 private void abort(String callId) {
Ihab Awad60ac30b2014-05-20 22:32:12 -0700669 Log.d(this, "abort %s", callId);
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700670 findConnectionForAction(callId, "abort").onAbort();
Ihab Awad542e0ea2014-05-16 10:22:16 -0700671 }
672
Tyler Gunnbe74de02014-08-29 14:51:48 -0700673 private void answerVideo(String callId, int videoState) {
674 Log.d(this, "answerVideo %s", callId);
Andrew Lee8da4c3c2014-07-16 10:11:42 -0700675 findConnectionForAction(callId, "answer").onAnswer(videoState);
Ihab Awad542e0ea2014-05-16 10:22:16 -0700676 }
677
Tyler Gunnbe74de02014-08-29 14:51:48 -0700678 private void answer(String callId) {
679 Log.d(this, "answer %s", callId);
680 findConnectionForAction(callId, "answer").onAnswer();
681 }
682
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700683 private void reject(String callId) {
Ihab Awad60ac30b2014-05-20 22:32:12 -0700684 Log.d(this, "reject %s", callId);
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700685 findConnectionForAction(callId, "reject").onReject();
Ihab Awad542e0ea2014-05-16 10:22:16 -0700686 }
687
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700688 private void disconnect(String callId) {
Ihab Awad60ac30b2014-05-20 22:32:12 -0700689 Log.d(this, "disconnect %s", callId);
Santos Cordon0159ac02014-08-21 14:28:11 -0700690 if (mConnectionById.containsKey(callId)) {
691 findConnectionForAction(callId, "disconnect").onDisconnect();
692 } else {
693 findConferenceForAction(callId, "disconnect").onDisconnect();
694 }
Ihab Awad542e0ea2014-05-16 10:22:16 -0700695 }
696
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700697 private void hold(String callId) {
Ihab Awad60ac30b2014-05-20 22:32:12 -0700698 Log.d(this, "hold %s", callId);
Santos Cordon0159ac02014-08-21 14:28:11 -0700699 if (mConnectionById.containsKey(callId)) {
700 findConnectionForAction(callId, "hold").onHold();
701 } else {
702 findConferenceForAction(callId, "hold").onHold();
703 }
Ihab Awad542e0ea2014-05-16 10:22:16 -0700704 }
705
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700706 private void unhold(String callId) {
Ihab Awad60ac30b2014-05-20 22:32:12 -0700707 Log.d(this, "unhold %s", callId);
Santos Cordon0159ac02014-08-21 14:28:11 -0700708 if (mConnectionById.containsKey(callId)) {
709 findConnectionForAction(callId, "unhold").onUnhold();
710 } else {
711 findConferenceForAction(callId, "unhold").onUnhold();
712 }
Ihab Awad542e0ea2014-05-16 10:22:16 -0700713 }
714
Yorke Lee4af59352015-05-13 14:14:54 -0700715 private void onCallAudioStateChanged(String callId, CallAudioState callAudioState) {
716 Log.d(this, "onAudioStateChanged %s %s", callId, callAudioState);
Yorke Leea0d3ca92014-09-15 19:18:13 -0700717 if (mConnectionById.containsKey(callId)) {
Yorke Lee4af59352015-05-13 14:14:54 -0700718 findConnectionForAction(callId, "onCallAudioStateChanged").setCallAudioState(
719 callAudioState);
Yorke Leea0d3ca92014-09-15 19:18:13 -0700720 } else {
Yorke Lee4af59352015-05-13 14:14:54 -0700721 findConferenceForAction(callId, "onCallAudioStateChanged").setCallAudioState(
722 callAudioState);
Yorke Leea0d3ca92014-09-15 19:18:13 -0700723 }
Ihab Awad542e0ea2014-05-16 10:22:16 -0700724 }
725
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700726 private void playDtmfTone(String callId, char digit) {
727 Log.d(this, "playDtmfTone %s %c", callId, digit);
Yorke Leea0d3ca92014-09-15 19:18:13 -0700728 if (mConnectionById.containsKey(callId)) {
729 findConnectionForAction(callId, "playDtmfTone").onPlayDtmfTone(digit);
730 } else {
731 findConferenceForAction(callId, "playDtmfTone").onPlayDtmfTone(digit);
732 }
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700733 }
734
735 private void stopDtmfTone(String callId) {
736 Log.d(this, "stopDtmfTone %s", callId);
Yorke Leea0d3ca92014-09-15 19:18:13 -0700737 if (mConnectionById.containsKey(callId)) {
738 findConnectionForAction(callId, "stopDtmfTone").onStopDtmfTone();
739 } else {
740 findConferenceForAction(callId, "stopDtmfTone").onStopDtmfTone();
741 }
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700742 }
743
Santos Cordon823fd3c2014-08-07 18:35:18 -0700744 private void conference(String callId1, String callId2) {
745 Log.d(this, "conference %s, %s", callId1, callId2);
Santos Cordon980acb92014-05-31 10:31:19 -0700746
Tyler Gunn6d76ca02014-11-17 15:49:51 -0800747 // Attempt to get second connection or conference.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700748 Connection connection2 = findConnectionForAction(callId2, "conference");
Tyler Gunn6d76ca02014-11-17 15:49:51 -0800749 Conference conference2 = getNullConference();
Sailesh Nepalcf7020b2014-08-20 10:07:19 -0700750 if (connection2 == getNullConnection()) {
Tyler Gunn6d76ca02014-11-17 15:49:51 -0800751 conference2 = findConferenceForAction(callId2, "conference");
752 if (conference2 == getNullConference()) {
753 Log.w(this, "Connection2 or Conference2 missing in conference request %s.",
754 callId2);
755 return;
756 }
Santos Cordon823fd3c2014-08-07 18:35:18 -0700757 }
Santos Cordonb6939982014-06-04 20:20:58 -0700758
Tyler Gunn6d76ca02014-11-17 15:49:51 -0800759 // Attempt to get first connection or conference and perform merge.
Ihab Awad50e35062014-09-30 09:17:03 -0700760 Connection connection1 = findConnectionForAction(callId1, "conference");
761 if (connection1 == getNullConnection()) {
762 Conference conference1 = findConferenceForAction(callId1, "addConnection");
763 if (conference1 == getNullConference()) {
764 Log.w(this,
765 "Connection1 or Conference1 missing in conference request %s.",
766 callId1);
767 } else {
Tyler Gunn6d76ca02014-11-17 15:49:51 -0800768 // Call 1 is a conference.
769 if (connection2 != getNullConnection()) {
770 // Call 2 is a connection so merge via call 1 (conference).
771 conference1.onMerge(connection2);
772 } else {
773 // Call 2 is ALSO a conference; this should never happen.
774 Log.wtf(this, "There can only be one conference and an attempt was made to " +
775 "merge two conferences.");
776 return;
777 }
Ihab Awad50e35062014-09-30 09:17:03 -0700778 }
779 } else {
Tyler Gunn6d76ca02014-11-17 15:49:51 -0800780 // Call 1 is a connection.
781 if (conference2 != getNullConference()) {
782 // Call 2 is a conference, so merge via call 2.
783 conference2.onMerge(connection1);
784 } else {
785 // Call 2 is a connection, so merge together.
786 onConference(connection1, connection2);
787 }
Ihab Awad50e35062014-09-30 09:17:03 -0700788 }
Santos Cordon980acb92014-05-31 10:31:19 -0700789 }
790
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700791 private void splitFromConference(String callId) {
Santos Cordonb6939982014-06-04 20:20:58 -0700792 Log.d(this, "splitFromConference(%s)", callId);
Santos Cordon980acb92014-05-31 10:31:19 -0700793
794 Connection connection = findConnectionForAction(callId, "splitFromConference");
Sailesh Nepalcf7020b2014-08-20 10:07:19 -0700795 if (connection == getNullConnection()) {
Santos Cordon980acb92014-05-31 10:31:19 -0700796 Log.w(this, "Connection missing in conference request %s.", callId);
797 return;
798 }
799
Santos Cordon0159ac02014-08-21 14:28:11 -0700800 Conference conference = connection.getConference();
801 if (conference != null) {
802 conference.onSeparate(connection);
803 }
Santos Cordon980acb92014-05-31 10:31:19 -0700804 }
805
Santos Cordona4868042014-09-04 17:39:22 -0700806 private void mergeConference(String callId) {
807 Log.d(this, "mergeConference(%s)", callId);
808 Conference conference = findConferenceForAction(callId, "mergeConference");
809 if (conference != null) {
810 conference.onMerge();
811 }
812 }
813
814 private void swapConference(String callId) {
815 Log.d(this, "swapConference(%s)", callId);
816 Conference conference = findConferenceForAction(callId, "swapConference");
817 if (conference != null) {
818 conference.onSwap();
819 }
820 }
821
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700822 private void onPostDialContinue(String callId, boolean proceed) {
Evan Charlton6dea4ac2014-06-03 14:07:13 -0700823 Log.d(this, "onPostDialContinue(%s)", callId);
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700824 findConnectionForAction(callId, "stopDtmfTone").onPostDialContinue(proceed);
Evan Charlton6dea4ac2014-06-03 14:07:13 -0700825 }
826
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700827 private void onAdapterAttached() {
Ihab Awad9c3f1882014-06-30 21:17:13 -0700828 if (mAreAccountsInitialized) {
Santos Cordon52d8a152014-06-17 19:08:45 -0700829 // No need to query again if we already did it.
830 return;
831 }
832
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700833 mAdapter.queryRemoteConnectionServices(new RemoteServiceCallback.Stub() {
Santos Cordon52d8a152014-06-17 19:08:45 -0700834 @Override
835 public void onResult(
836 final List<ComponentName> componentNames,
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700837 final List<IBinder> services) {
Santos Cordon52d8a152014-06-17 19:08:45 -0700838 mHandler.post(new Runnable() {
Ihab Awad6107bab2014-08-18 09:23:25 -0700839 @Override
840 public void run() {
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700841 for (int i = 0; i < componentNames.size() && i < services.size(); i++) {
Santos Cordon52d8a152014-06-17 19:08:45 -0700842 mRemoteConnectionManager.addConnectionService(
843 componentNames.get(i),
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700844 IConnectionService.Stub.asInterface(services.get(i)));
Santos Cordon52d8a152014-06-17 19:08:45 -0700845 }
Ihab Awad5d0410f2014-07-30 10:07:40 -0700846 onAccountsInitialized();
Sailesh Nepalc5b01572014-07-14 16:29:44 -0700847 Log.d(this, "remote connection services found: " + services);
Santos Cordon52d8a152014-06-17 19:08:45 -0700848 }
849 });
850 }
851
852 @Override
853 public void onError() {
854 mHandler.post(new Runnable() {
Ihab Awad6107bab2014-08-18 09:23:25 -0700855 @Override
856 public void run() {
Ihab Awad9c3f1882014-06-30 21:17:13 -0700857 mAreAccountsInitialized = true;
Santos Cordon52d8a152014-06-17 19:08:45 -0700858 }
859 });
860 }
861 });
862 }
863
Ihab Awadf8b69882014-07-25 15:14:01 -0700864 /**
865 * Ask some other {@code ConnectionService} to create a {@code RemoteConnection} given an
Santos Cordona663f862014-10-29 13:49:58 -0700866 * incoming request. This is used by {@code ConnectionService}s that are registered with
867 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} and want to be able to manage
868 * SIM-based incoming calls.
Ihab Awadf8b69882014-07-25 15:14:01 -0700869 *
870 * @param connectionManagerPhoneAccount See description at
871 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
872 * @param request Details about the incoming call.
873 * @return The {@code Connection} object to satisfy this call, or {@code null} to
874 * not handle the call.
875 */
876 public final RemoteConnection createRemoteIncomingConnection(
877 PhoneAccountHandle connectionManagerPhoneAccount,
878 ConnectionRequest request) {
879 return mRemoteConnectionManager.createRemoteConnection(
880 connectionManagerPhoneAccount, request, true);
Santos Cordon52d8a152014-06-17 19:08:45 -0700881 }
882
883 /**
Ihab Awadf8b69882014-07-25 15:14:01 -0700884 * Ask some other {@code ConnectionService} to create a {@code RemoteConnection} given an
Santos Cordona663f862014-10-29 13:49:58 -0700885 * outgoing request. This is used by {@code ConnectionService}s that are registered with
886 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} and want to be able to use the
887 * SIM-based {@code ConnectionService} to place its outgoing calls.
Ihab Awadf8b69882014-07-25 15:14:01 -0700888 *
889 * @param connectionManagerPhoneAccount See description at
890 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
891 * @param request Details about the incoming call.
892 * @return The {@code Connection} object to satisfy this call, or {@code null} to
893 * not handle the call.
894 */
895 public final RemoteConnection createRemoteOutgoingConnection(
896 PhoneAccountHandle connectionManagerPhoneAccount,
897 ConnectionRequest request) {
898 return mRemoteConnectionManager.createRemoteConnection(
899 connectionManagerPhoneAccount, request, false);
900 }
901
902 /**
Santos Cordona663f862014-10-29 13:49:58 -0700903 * Indicates to the relevant {@code RemoteConnectionService} that the specified
904 * {@link RemoteConnection}s should be merged into a conference call.
905 * <p>
906 * If the conference request is successful, the method {@link #onRemoteConferenceAdded} will
907 * be invoked.
908 *
909 * @param remoteConnection1 The first of the remote connections to conference.
910 * @param remoteConnection2 The second of the remote connections to conference.
Ihab Awadb8e85c72014-08-23 20:34:57 -0700911 */
912 public final void conferenceRemoteConnections(
Santos Cordona663f862014-10-29 13:49:58 -0700913 RemoteConnection remoteConnection1,
914 RemoteConnection remoteConnection2) {
915 mRemoteConnectionManager.conferenceRemoteConnections(remoteConnection1, remoteConnection2);
Ihab Awadb8e85c72014-08-23 20:34:57 -0700916 }
917
918 /**
Santos Cordon823fd3c2014-08-07 18:35:18 -0700919 * Adds a new conference call. When a conference call is created either as a result of an
920 * explicit request via {@link #onConference} or otherwise, the connection service should supply
921 * an instance of {@link Conference} by invoking this method. A conference call provided by this
922 * method will persist until {@link Conference#destroy} is invoked on the conference instance.
923 *
924 * @param conference The new conference object.
925 */
926 public final void addConference(Conference conference) {
Rekha Kumar07366812015-03-24 16:42:31 -0700927 Log.d(this, "addConference: conference=%s", conference);
928
Santos Cordon823fd3c2014-08-07 18:35:18 -0700929 String id = addConferenceInternal(conference);
930 if (id != null) {
931 List<String> connectionIds = new ArrayList<>(2);
932 for (Connection connection : conference.getConnections()) {
933 if (mIdByConnection.containsKey(connection)) {
934 connectionIds.add(mIdByConnection.get(connection));
935 }
936 }
Tyler Gunnf0500bd2015-09-01 10:59:48 -0700937 conference.setTelecomCallId(id);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700938 ParcelableConference parcelableConference = new ParcelableConference(
Nancy Chenea38cca2014-09-05 16:38:49 -0700939 conference.getPhoneAccountHandle(),
Santos Cordon823fd3c2014-08-07 18:35:18 -0700940 conference.getState(),
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800941 conference.getConnectionCapabilities(),
Tyler Gunncd5d33c2015-01-12 09:02:01 -0800942 connectionIds,
Rekha Kumar07366812015-03-24 16:42:31 -0700943 conference.getVideoProvider() == null ?
944 null : conference.getVideoProvider().getInterface(),
945 conference.getVideoState(),
Andrew Lee3e3e2f22015-04-16 13:48:43 -0700946 conference.getConnectTimeMillis(),
Santos Cordon6b7f9552015-05-27 17:21:45 -0700947 conference.getStatusHints(),
948 conference.getExtras());
Andrew Lee0f51da32015-04-16 13:11:55 -0700949
Santos Cordon823fd3c2014-08-07 18:35:18 -0700950 mAdapter.addConferenceCall(id, parcelableConference);
Rekha Kumar07366812015-03-24 16:42:31 -0700951 mAdapter.setVideoProvider(id, conference.getVideoProvider());
952 mAdapter.setVideoState(id, conference.getVideoState());
Santos Cordon823fd3c2014-08-07 18:35:18 -0700953
954 // Go through any child calls and set the parent.
955 for (Connection connection : conference.getConnections()) {
956 String connectionId = mIdByConnection.get(connection);
957 if (connectionId != null) {
958 mAdapter.setIsConferenced(connectionId, id);
959 }
960 }
961 }
962 }
963
964 /**
Tyler Gunn4a57b9b2014-10-30 14:27:48 -0700965 * Adds a connection created by the {@link ConnectionService} and informs telecom of the new
966 * connection.
967 *
968 * @param phoneAccountHandle The phone account handle for the connection.
969 * @param connection The connection to add.
970 */
971 public final void addExistingConnection(PhoneAccountHandle phoneAccountHandle,
972 Connection connection) {
973
Tyler Gunnf0500bd2015-09-01 10:59:48 -0700974 String id = addExistingConnectionInternal(phoneAccountHandle, connection);
Tyler Gunn4a57b9b2014-10-30 14:27:48 -0700975 if (id != null) {
976 List<String> emptyList = new ArrayList<>(0);
977
978 ParcelableConnection parcelableConnection = new ParcelableConnection(
979 phoneAccountHandle,
980 connection.getState(),
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800981 connection.getConnectionCapabilities(),
Tyler Gunn4a57b9b2014-10-30 14:27:48 -0700982 connection.getAddress(),
983 connection.getAddressPresentation(),
984 connection.getCallerDisplayName(),
985 connection.getCallerDisplayNamePresentation(),
986 connection.getVideoProvider() == null ?
987 null : connection.getVideoProvider().getInterface(),
988 connection.getVideoState(),
989 connection.isRingbackRequested(),
990 connection.getAudioModeIsVoip(),
Roshan Piuse927ec02015-07-15 15:47:21 -0700991 connection.getConnectTimeMillis(),
Tyler Gunn4a57b9b2014-10-30 14:27:48 -0700992 connection.getStatusHints(),
993 connection.getDisconnectCause(),
Santos Cordon6b7f9552015-05-27 17:21:45 -0700994 emptyList,
995 connection.getExtras());
Tyler Gunn4a57b9b2014-10-30 14:27:48 -0700996 mAdapter.addExistingConnection(id, parcelableConnection);
997 }
998 }
999
1000 /**
Ihab Awadf8b69882014-07-25 15:14:01 -07001001 * Returns all the active {@code Connection}s for which this {@code ConnectionService}
1002 * has taken responsibility.
1003 *
1004 * @return A collection of {@code Connection}s created by this {@code ConnectionService}.
Santos Cordonb6939982014-06-04 20:20:58 -07001005 */
Sailesh Nepal091768c2014-06-30 15:15:23 -07001006 public final Collection<Connection> getAllConnections() {
Santos Cordonb6939982014-06-04 20:20:58 -07001007 return mConnectionById.values();
1008 }
1009
1010 /**
Ihab Awadf8b69882014-07-25 15:14:01 -07001011 * Create a {@code Connection} given an incoming request. This is used to attach to existing
1012 * incoming calls.
Evan Charltonbf11f982014-07-20 22:06:28 -07001013 *
Ihab Awadf8b69882014-07-25 15:14:01 -07001014 * @param connectionManagerPhoneAccount See description at
1015 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
1016 * @param request Details about the incoming call.
1017 * @return The {@code Connection} object to satisfy this call, or {@code null} to
1018 * not handle the call.
Ihab Awad542e0ea2014-05-16 10:22:16 -07001019 */
Ihab Awadf8b69882014-07-25 15:14:01 -07001020 public Connection onCreateIncomingConnection(
1021 PhoneAccountHandle connectionManagerPhoneAccount,
1022 ConnectionRequest request) {
1023 return null;
1024 }
Sailesh Nepalc5b01572014-07-14 16:29:44 -07001025
1026 /**
Shriram Ganesh6bf35ac2014-12-11 17:53:38 -08001027 * Trigger recalculate functinality for conference calls. This is used when a Telephony
1028 * Connection is part of a conference controller but is not yet added to Connection
1029 * Service and hence cannot be added to the conference call.
1030 *
1031 * @hide
1032 */
1033 public void triggerConferenceRecalculate() {
1034 }
1035
1036 /**
Ihab Awadf8b69882014-07-25 15:14:01 -07001037 * Create a {@code Connection} given an outgoing request. This is used to initiate new
1038 * outgoing calls.
Sailesh Nepalc5b01572014-07-14 16:29:44 -07001039 *
Ihab Awadf8b69882014-07-25 15:14:01 -07001040 * @param connectionManagerPhoneAccount The connection manager account to use for managing
1041 * this call.
1042 * <p>
1043 * If this parameter is not {@code null}, it means that this {@code ConnectionService}
1044 * has registered one or more {@code PhoneAccount}s having
1045 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}. This parameter will contain
1046 * one of these {@code PhoneAccount}s, while the {@code request} will contain another
1047 * (usually but not always distinct) {@code PhoneAccount} to be used for actually
1048 * making the connection.
1049 * <p>
1050 * If this parameter is {@code null}, it means that this {@code ConnectionService} is
1051 * being asked to make a direct connection. The
1052 * {@link ConnectionRequest#getAccountHandle()} of parameter {@code request} will be
1053 * a {@code PhoneAccount} registered by this {@code ConnectionService} to use for
1054 * making the connection.
1055 * @param request Details about the outgoing call.
1056 * @return The {@code Connection} object to satisfy this call, or the result of an invocation
Andrew Lee7f3d41f2014-09-11 17:33:16 -07001057 * of {@link Connection#createFailedConnection(DisconnectCause)} to not handle the call.
Sailesh Nepalc5b01572014-07-14 16:29:44 -07001058 */
Ihab Awadf8b69882014-07-25 15:14:01 -07001059 public Connection onCreateOutgoingConnection(
1060 PhoneAccountHandle connectionManagerPhoneAccount,
1061 ConnectionRequest request) {
1062 return null;
1063 }
Ihab Awad542e0ea2014-05-16 10:22:16 -07001064
1065 /**
Yorke Leec3cf9822014-10-02 09:38:39 -07001066 * Create a {@code Connection} for a new unknown call. An unknown call is a call originating
1067 * from the ConnectionService that was neither a user-initiated outgoing call, nor an incoming
1068 * call created using
1069 * {@code TelecomManager#addNewIncomingCall(PhoneAccountHandle, android.os.Bundle)}.
1070 *
1071 * @param connectionManagerPhoneAccount
1072 * @param request
1073 * @return
Yorke Lee770ed6e2014-10-06 18:58:52 -07001074 *
1075 * @hide
Yorke Leec3cf9822014-10-02 09:38:39 -07001076 */
1077 public Connection onCreateUnknownConnection(PhoneAccountHandle connectionManagerPhoneAccount,
1078 ConnectionRequest request) {
1079 return null;
1080 }
1081
1082 /**
Santos Cordon823fd3c2014-08-07 18:35:18 -07001083 * Conference two specified connections. Invoked when the user has made a request to merge the
1084 * specified connections into a conference call. In response, the connection service should
1085 * create an instance of {@link Conference} and pass it into {@link #addConference}.
Santos Cordonb6939982014-06-04 20:20:58 -07001086 *
Santos Cordon823fd3c2014-08-07 18:35:18 -07001087 * @param connection1 A connection to merge into a conference call.
1088 * @param connection2 A connection to merge into a conference call.
Santos Cordonb6939982014-06-04 20:20:58 -07001089 */
Santos Cordon823fd3c2014-08-07 18:35:18 -07001090 public void onConference(Connection connection1, Connection connection2) {}
Santos Cordonb6939982014-06-04 20:20:58 -07001091
Santos Cordona663f862014-10-29 13:49:58 -07001092 /**
1093 * Indicates that a remote conference has been created for existing {@link RemoteConnection}s.
1094 * When this method is invoked, this {@link ConnectionService} should create its own
1095 * representation of the conference call and send it to telecom using {@link #addConference}.
1096 * <p>
1097 * This is only relevant to {@link ConnectionService}s which are registered with
1098 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}.
1099 *
1100 * @param conference The remote conference call.
1101 */
Ihab Awadb8e85c72014-08-23 20:34:57 -07001102 public void onRemoteConferenceAdded(RemoteConference conference) {}
1103
Santos Cordon823fd3c2014-08-07 18:35:18 -07001104 /**
Tyler Gunn4a57b9b2014-10-30 14:27:48 -07001105 * Called when an existing connection is added remotely.
1106 * @param connection The existing connection which was added.
1107 */
1108 public void onRemoteExistingConnectionAdded(RemoteConnection connection) {}
1109
1110 /**
Santos Cordon823fd3c2014-08-07 18:35:18 -07001111 * @hide
1112 */
1113 public boolean containsConference(Conference conference) {
1114 return mIdByConference.containsKey(conference);
1115 }
1116
Ihab Awadb8e85c72014-08-23 20:34:57 -07001117 /** {@hide} */
1118 void addRemoteConference(RemoteConference remoteConference) {
1119 onRemoteConferenceAdded(remoteConference);
1120 }
1121
Tyler Gunn4a57b9b2014-10-30 14:27:48 -07001122 /** {@hide} */
1123 void addRemoteExistingConnection(RemoteConnection remoteConnection) {
1124 onRemoteExistingConnectionAdded(remoteConnection);
1125 }
1126
Ihab Awad5d0410f2014-07-30 10:07:40 -07001127 private void onAccountsInitialized() {
1128 mAreAccountsInitialized = true;
1129 for (Runnable r : mPreInitializationConnectionRequests) {
1130 r.run();
1131 }
1132 mPreInitializationConnectionRequests.clear();
1133 }
1134
Tyler Gunn4a57b9b2014-10-30 14:27:48 -07001135 /**
Tyler Gunnf0500bd2015-09-01 10:59:48 -07001136 * Adds an existing connection to the list of connections, identified by a new call ID unique
1137 * to this connection service.
Tyler Gunn4a57b9b2014-10-30 14:27:48 -07001138 *
1139 * @param connection The connection.
Tyler Gunnf0500bd2015-09-01 10:59:48 -07001140 * @return The ID of the connection (e.g. the call-id).
Tyler Gunn4a57b9b2014-10-30 14:27:48 -07001141 */
Tyler Gunnf0500bd2015-09-01 10:59:48 -07001142 private String addExistingConnectionInternal(PhoneAccountHandle handle, Connection connection) {
1143 String id;
1144 if (handle == null) {
1145 // If no phone account handle was provided, we cannot be sure the call ID is unique,
1146 // so just use a random UUID.
1147 id = UUID.randomUUID().toString();
1148 } else {
1149 // Phone account handle was provided, so use the ConnectionService class name as a
1150 // prefix for a unique incremental call ID.
1151 id = handle.getComponentName().getClassName() + "@" + getNextCallId();
1152 }
Tyler Gunn4a57b9b2014-10-30 14:27:48 -07001153 addConnection(id, connection);
1154 return id;
1155 }
1156
Ihab Awad542e0ea2014-05-16 10:22:16 -07001157 private void addConnection(String callId, Connection connection) {
Tyler Gunnf0500bd2015-09-01 10:59:48 -07001158 connection.setTelecomCallId(callId);
Ihab Awad542e0ea2014-05-16 10:22:16 -07001159 mConnectionById.put(callId, connection);
1160 mIdByConnection.put(connection, callId);
1161 connection.addConnectionListener(mConnectionListener);
Santos Cordon823fd3c2014-08-07 18:35:18 -07001162 connection.setConnectionService(this);
Ihab Awad542e0ea2014-05-16 10:22:16 -07001163 }
1164
Anthony Lee30e65842014-11-06 16:30:53 -08001165 /** {@hide} */
1166 protected void removeConnection(Connection connection) {
Ihab Awad8aecfed2014-08-08 17:06:11 -07001167 String id = mIdByConnection.get(connection);
Santos Cordon823fd3c2014-08-07 18:35:18 -07001168 connection.unsetConnectionService(this);
Ihab Awad542e0ea2014-05-16 10:22:16 -07001169 connection.removeConnectionListener(mConnectionListener);
1170 mConnectionById.remove(mIdByConnection.get(connection));
1171 mIdByConnection.remove(connection);
Ihab Awad8aecfed2014-08-08 17:06:11 -07001172 mAdapter.removeCall(id);
Ihab Awad542e0ea2014-05-16 10:22:16 -07001173 }
1174
Santos Cordon823fd3c2014-08-07 18:35:18 -07001175 private String addConferenceInternal(Conference conference) {
1176 if (mIdByConference.containsKey(conference)) {
1177 Log.w(this, "Re-adding an existing conference: %s.", conference);
1178 } else if (conference != null) {
Tyler Gunnf0500bd2015-09-01 10:59:48 -07001179 // Conferences do not (yet) have a PhoneAccountHandle associated with them, so we
1180 // cannot determine a ConnectionService class name to associate with the ID, so use
1181 // a unique UUID (for now).
Santos Cordon823fd3c2014-08-07 18:35:18 -07001182 String id = UUID.randomUUID().toString();
1183 mConferenceById.put(id, conference);
1184 mIdByConference.put(conference, id);
1185 conference.addListener(mConferenceListener);
1186 return id;
1187 }
1188
1189 return null;
1190 }
1191
1192 private void removeConference(Conference conference) {
1193 if (mIdByConference.containsKey(conference)) {
1194 conference.removeListener(mConferenceListener);
1195
1196 String id = mIdByConference.get(conference);
1197 mConferenceById.remove(id);
1198 mIdByConference.remove(conference);
1199 mAdapter.removeCall(id);
1200 }
1201 }
1202
Ihab Awad542e0ea2014-05-16 10:22:16 -07001203 private Connection findConnectionForAction(String callId, String action) {
1204 if (mConnectionById.containsKey(callId)) {
1205 return mConnectionById.get(callId);
1206 }
Ihab Awad60ac30b2014-05-20 22:32:12 -07001207 Log.w(this, "%s - Cannot find Connection %s", action, callId);
Sailesh Nepalcf7020b2014-08-20 10:07:19 -07001208 return getNullConnection();
1209 }
1210
1211 static synchronized Connection getNullConnection() {
1212 if (sNullConnection == null) {
1213 sNullConnection = new Connection() {};
1214 }
1215 return sNullConnection;
Santos Cordon7c7bc7f2014-07-28 18:15:48 -07001216 }
Santos Cordon0159ac02014-08-21 14:28:11 -07001217
1218 private Conference findConferenceForAction(String conferenceId, String action) {
1219 if (mConferenceById.containsKey(conferenceId)) {
1220 return mConferenceById.get(conferenceId);
1221 }
1222 Log.w(this, "%s - Cannot find conference %s", action, conferenceId);
1223 return getNullConference();
1224 }
1225
Ihab Awadb8e85c72014-08-23 20:34:57 -07001226 private List<String> createConnectionIdList(List<Connection> connections) {
1227 List<String> ids = new ArrayList<>();
1228 for (Connection c : connections) {
1229 if (mIdByConnection.containsKey(c)) {
1230 ids.add(mIdByConnection.get(c));
1231 }
1232 }
1233 Collections.sort(ids);
1234 return ids;
1235 }
1236
Tyler Gunn6d76ca02014-11-17 15:49:51 -08001237 /**
1238 * Builds a list of {@link Connection} and {@link Conference} IDs based on the list of
Tyler Gunndf2cbc82015-04-20 09:13:01 -07001239 * {@link Conferenceable}s passed in.
Tyler Gunn6d76ca02014-11-17 15:49:51 -08001240 *
Tyler Gunndf2cbc82015-04-20 09:13:01 -07001241 * @param conferenceables The {@link Conferenceable} connections and conferences.
Tyler Gunn6d76ca02014-11-17 15:49:51 -08001242 * @return List of string conference and call Ids.
1243 */
Tyler Gunndf2cbc82015-04-20 09:13:01 -07001244 private List<String> createIdList(List<Conferenceable> conferenceables) {
Tyler Gunn6d76ca02014-11-17 15:49:51 -08001245 List<String> ids = new ArrayList<>();
Tyler Gunndf2cbc82015-04-20 09:13:01 -07001246 for (Conferenceable c : conferenceables) {
Tyler Gunn6d76ca02014-11-17 15:49:51 -08001247 // Only allow Connection and Conference conferenceables.
1248 if (c instanceof Connection) {
1249 Connection connection = (Connection) c;
1250 if (mIdByConnection.containsKey(connection)) {
1251 ids.add(mIdByConnection.get(connection));
1252 }
1253 } else if (c instanceof Conference) {
1254 Conference conference = (Conference) c;
1255 if (mIdByConference.containsKey(conference)) {
1256 ids.add(mIdByConference.get(conference));
1257 }
1258 }
1259 }
1260 Collections.sort(ids);
1261 return ids;
1262 }
1263
Santos Cordon0159ac02014-08-21 14:28:11 -07001264 private Conference getNullConference() {
1265 if (sNullConference == null) {
1266 sNullConference = new Conference(null) {};
1267 }
1268 return sNullConference;
1269 }
Santos Cordon29f2f2e2014-09-11 19:50:24 -07001270
1271 private void endAllConnections() {
1272 // Unbound from telecomm. We should end all connections and conferences.
1273 for (Connection connection : mIdByConnection.keySet()) {
1274 // only operate on top-level calls. Conference calls will be removed on their own.
1275 if (connection.getConference() == null) {
1276 connection.onDisconnect();
1277 }
1278 }
1279 for (Conference conference : mIdByConference.keySet()) {
1280 conference.onDisconnect();
1281 }
1282 }
Tyler Gunnf0500bd2015-09-01 10:59:48 -07001283
1284 /**
1285 * Retrieves the next call ID as maintainted by the connection service.
1286 *
1287 * @return The call ID.
1288 */
1289 private int getNextCallId() {
1290 synchronized(mIdSyncRoot) {
1291 return ++mId;
1292 }
1293 }
Santos Cordon980acb92014-05-31 10:31:19 -07001294}