blob: 62234958517d94ca89472ed6c1707629d0f4614e [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;
Bryce Lee81901682015-08-28 16:38:02 -0700104 private static final int MSG_REJECT_WITH_MESSAGE = 20;
Santos Cordon7c7bc7f2014-07-28 18:15:48 -0700105
Sailesh Nepalcf7020b2014-08-20 10:07:19 -0700106 private static Connection sNullConnection;
107
mike dooley95e80702014-09-18 14:07:52 -0700108 private final Map<String, Connection> mConnectionById = new ConcurrentHashMap<>();
109 private final Map<Connection, String> mIdByConnection = new ConcurrentHashMap<>();
110 private final Map<String, Conference> mConferenceById = new ConcurrentHashMap<>();
111 private final Map<Conference, String> mIdByConference = new ConcurrentHashMap<>();
Ihab Awadb8e85c72014-08-23 20:34:57 -0700112 private final RemoteConnectionManager mRemoteConnectionManager =
113 new RemoteConnectionManager(this);
Ihab Awad5d0410f2014-07-30 10:07:40 -0700114 private final List<Runnable> mPreInitializationConnectionRequests = new ArrayList<>();
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700115 private final ConnectionServiceAdapter mAdapter = new ConnectionServiceAdapter();
Ihab Awad542e0ea2014-05-16 10:22:16 -0700116
Santos Cordon823fd3c2014-08-07 18:35:18 -0700117 private boolean mAreAccountsInitialized = false;
Santos Cordon0159ac02014-08-21 14:28:11 -0700118 private Conference sNullConference;
Jack Yu67140302015-12-10 12:27:58 -0800119 private Object mIdSyncRoot = new Object();
120 private int mId = 0;
Santos Cordon823fd3c2014-08-07 18:35:18 -0700121
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700122 private final IBinder mBinder = new IConnectionService.Stub() {
123 @Override
124 public void addConnectionServiceAdapter(IConnectionServiceAdapter adapter) {
Ihab Awad8aecfed2014-08-08 17:06:11 -0700125 mHandler.obtainMessage(MSG_ADD_CONNECTION_SERVICE_ADAPTER, adapter).sendToTarget();
126 }
127
128 public void removeConnectionServiceAdapter(IConnectionServiceAdapter adapter) {
129 mHandler.obtainMessage(MSG_REMOVE_CONNECTION_SERVICE_ADAPTER, adapter).sendToTarget();
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700130 }
131
132 @Override
Ihab Awadf8b69882014-07-25 15:14:01 -0700133 public void createConnection(
134 PhoneAccountHandle connectionManagerPhoneAccount,
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700135 String id,
Ihab Awadf8b69882014-07-25 15:14:01 -0700136 ConnectionRequest request,
Yorke Leec3cf9822014-10-02 09:38:39 -0700137 boolean isIncoming,
138 boolean isUnknown) {
Ihab Awadf8b69882014-07-25 15:14:01 -0700139 SomeArgs args = SomeArgs.obtain();
140 args.arg1 = connectionManagerPhoneAccount;
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700141 args.arg2 = id;
142 args.arg3 = request;
Ihab Awadf8b69882014-07-25 15:14:01 -0700143 args.argi1 = isIncoming ? 1 : 0;
Yorke Leec3cf9822014-10-02 09:38:39 -0700144 args.argi2 = isUnknown ? 1 : 0;
Ihab Awadf8b69882014-07-25 15:14:01 -0700145 mHandler.obtainMessage(MSG_CREATE_CONNECTION, args).sendToTarget();
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700146 }
147
148 @Override
149 public void abort(String callId) {
150 mHandler.obtainMessage(MSG_ABORT, callId).sendToTarget();
151 }
152
153 @Override
Tyler Gunnbe74de02014-08-29 14:51:48 -0700154 public void answerVideo(String callId, int videoState) {
Andrew Lee8da4c3c2014-07-16 10:11:42 -0700155 SomeArgs args = SomeArgs.obtain();
156 args.arg1 = callId;
Evan Charltonbf11f982014-07-20 22:06:28 -0700157 args.argi1 = videoState;
Tyler Gunnbe74de02014-08-29 14:51:48 -0700158 mHandler.obtainMessage(MSG_ANSWER_VIDEO, args).sendToTarget();
159 }
160
161 @Override
162 public void answer(String callId) {
163 mHandler.obtainMessage(MSG_ANSWER, callId).sendToTarget();
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700164 }
165
166 @Override
167 public void reject(String callId) {
168 mHandler.obtainMessage(MSG_REJECT, callId).sendToTarget();
169 }
170
171 @Override
Bryce Lee81901682015-08-28 16:38:02 -0700172 public void rejectWithMessage(String callId, String message) {
173 SomeArgs args = SomeArgs.obtain();
174 args.arg1 = callId;
175 args.arg2 = message;
176 mHandler.obtainMessage(MSG_REJECT_WITH_MESSAGE, args).sendToTarget();
177 }
178
179 @Override
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700180 public void disconnect(String callId) {
181 mHandler.obtainMessage(MSG_DISCONNECT, callId).sendToTarget();
182 }
183
184 @Override
185 public void hold(String callId) {
186 mHandler.obtainMessage(MSG_HOLD, callId).sendToTarget();
187 }
188
189 @Override
190 public void unhold(String callId) {
191 mHandler.obtainMessage(MSG_UNHOLD, callId).sendToTarget();
192 }
193
194 @Override
Yorke Lee4af59352015-05-13 14:14:54 -0700195 public void onCallAudioStateChanged(String callId, CallAudioState callAudioState) {
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700196 SomeArgs args = SomeArgs.obtain();
197 args.arg1 = callId;
Yorke Lee4af59352015-05-13 14:14:54 -0700198 args.arg2 = callAudioState;
199 mHandler.obtainMessage(MSG_ON_CALL_AUDIO_STATE_CHANGED, args).sendToTarget();
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700200 }
201
202 @Override
203 public void playDtmfTone(String callId, char digit) {
204 mHandler.obtainMessage(MSG_PLAY_DTMF_TONE, digit, 0, callId).sendToTarget();
205 }
206
207 @Override
208 public void stopDtmfTone(String callId) {
209 mHandler.obtainMessage(MSG_STOP_DTMF_TONE, callId).sendToTarget();
210 }
211
212 @Override
Santos Cordon823fd3c2014-08-07 18:35:18 -0700213 public void conference(String callId1, String callId2) {
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700214 SomeArgs args = SomeArgs.obtain();
Santos Cordon823fd3c2014-08-07 18:35:18 -0700215 args.arg1 = callId1;
216 args.arg2 = callId2;
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700217 mHandler.obtainMessage(MSG_CONFERENCE, args).sendToTarget();
218 }
219
220 @Override
221 public void splitFromConference(String callId) {
222 mHandler.obtainMessage(MSG_SPLIT_FROM_CONFERENCE, callId).sendToTarget();
223 }
224
225 @Override
Santos Cordona4868042014-09-04 17:39:22 -0700226 public void mergeConference(String callId) {
227 mHandler.obtainMessage(MSG_MERGE_CONFERENCE, callId).sendToTarget();
228 }
229
230 @Override
231 public void swapConference(String callId) {
232 mHandler.obtainMessage(MSG_SWAP_CONFERENCE, callId).sendToTarget();
233 }
234
235 @Override
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700236 public void onPostDialContinue(String callId, boolean proceed) {
237 SomeArgs args = SomeArgs.obtain();
238 args.arg1 = callId;
239 args.argi1 = proceed ? 1 : 0;
240 mHandler.obtainMessage(MSG_ON_POST_DIAL_CONTINUE, args).sendToTarget();
241 }
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700242 };
243
244 private final Handler mHandler = new Handler(Looper.getMainLooper()) {
245 @Override
246 public void handleMessage(Message msg) {
247 switch (msg.what) {
Ihab Awad8aecfed2014-08-08 17:06:11 -0700248 case MSG_ADD_CONNECTION_SERVICE_ADAPTER:
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700249 mAdapter.addAdapter((IConnectionServiceAdapter) msg.obj);
250 onAdapterAttached();
251 break;
Ihab Awad8aecfed2014-08-08 17:06:11 -0700252 case MSG_REMOVE_CONNECTION_SERVICE_ADAPTER:
253 mAdapter.removeAdapter((IConnectionServiceAdapter) msg.obj);
254 break;
Ihab Awadf8b69882014-07-25 15:14:01 -0700255 case MSG_CREATE_CONNECTION: {
256 SomeArgs args = (SomeArgs) msg.obj;
257 try {
Ihab Awad5d0410f2014-07-30 10:07:40 -0700258 final PhoneAccountHandle connectionManagerPhoneAccount =
Ihab Awadf8b69882014-07-25 15:14:01 -0700259 (PhoneAccountHandle) args.arg1;
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700260 final String id = (String) args.arg2;
261 final ConnectionRequest request = (ConnectionRequest) args.arg3;
Ihab Awad5d0410f2014-07-30 10:07:40 -0700262 final boolean isIncoming = args.argi1 == 1;
Yorke Leec3cf9822014-10-02 09:38:39 -0700263 final boolean isUnknown = args.argi2 == 1;
Ihab Awad5d0410f2014-07-30 10:07:40 -0700264 if (!mAreAccountsInitialized) {
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700265 Log.d(this, "Enqueueing pre-init request %s", id);
Ihab Awad5d0410f2014-07-30 10:07:40 -0700266 mPreInitializationConnectionRequests.add(new Runnable() {
267 @Override
268 public void run() {
269 createConnection(
270 connectionManagerPhoneAccount,
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700271 id,
Ihab Awad5d0410f2014-07-30 10:07:40 -0700272 request,
Yorke Leec3cf9822014-10-02 09:38:39 -0700273 isIncoming,
274 isUnknown);
Ihab Awad5d0410f2014-07-30 10:07:40 -0700275 }
276 });
277 } else {
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700278 createConnection(
279 connectionManagerPhoneAccount,
280 id,
281 request,
Yorke Leec3cf9822014-10-02 09:38:39 -0700282 isIncoming,
283 isUnknown);
Ihab Awad5d0410f2014-07-30 10:07:40 -0700284 }
Ihab Awadf8b69882014-07-25 15:14:01 -0700285 } finally {
286 args.recycle();
287 }
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700288 break;
Ihab Awadf8b69882014-07-25 15:14:01 -0700289 }
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700290 case MSG_ABORT:
291 abort((String) msg.obj);
292 break;
Tyler Gunnbe74de02014-08-29 14:51:48 -0700293 case MSG_ANSWER:
294 answer((String) msg.obj);
295 break;
296 case MSG_ANSWER_VIDEO: {
Andrew Lee8da4c3c2014-07-16 10:11:42 -0700297 SomeArgs args = (SomeArgs) msg.obj;
298 try {
299 String callId = (String) args.arg1;
Evan Charltonbf11f982014-07-20 22:06:28 -0700300 int videoState = args.argi1;
Tyler Gunnbe74de02014-08-29 14:51:48 -0700301 answerVideo(callId, videoState);
Andrew Lee8da4c3c2014-07-16 10:11:42 -0700302 } finally {
303 args.recycle();
304 }
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700305 break;
Andrew Lee8da4c3c2014-07-16 10:11:42 -0700306 }
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700307 case MSG_REJECT:
308 reject((String) msg.obj);
309 break;
Bryce Lee81901682015-08-28 16:38:02 -0700310 case MSG_REJECT_WITH_MESSAGE: {
311 SomeArgs args = (SomeArgs) msg.obj;
312 try {
313 reject((String) args.arg1, (String) args.arg2);
314 } finally {
315 args.recycle();
316 }
317 break;
318 }
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700319 case MSG_DISCONNECT:
320 disconnect((String) msg.obj);
321 break;
322 case MSG_HOLD:
323 hold((String) msg.obj);
324 break;
325 case MSG_UNHOLD:
326 unhold((String) msg.obj);
327 break;
Yorke Lee4af59352015-05-13 14:14:54 -0700328 case MSG_ON_CALL_AUDIO_STATE_CHANGED: {
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700329 SomeArgs args = (SomeArgs) msg.obj;
330 try {
331 String callId = (String) args.arg1;
Yorke Lee4af59352015-05-13 14:14:54 -0700332 CallAudioState audioState = (CallAudioState) args.arg2;
333 onCallAudioStateChanged(callId, new CallAudioState(audioState));
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700334 } finally {
335 args.recycle();
336 }
337 break;
338 }
339 case MSG_PLAY_DTMF_TONE:
340 playDtmfTone((String) msg.obj, (char) msg.arg1);
341 break;
342 case MSG_STOP_DTMF_TONE:
343 stopDtmfTone((String) msg.obj);
344 break;
345 case MSG_CONFERENCE: {
346 SomeArgs args = (SomeArgs) msg.obj;
347 try {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700348 String callId1 = (String) args.arg1;
349 String callId2 = (String) args.arg2;
350 conference(callId1, callId2);
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700351 } finally {
352 args.recycle();
353 }
354 break;
355 }
356 case MSG_SPLIT_FROM_CONFERENCE:
357 splitFromConference((String) msg.obj);
358 break;
Santos Cordona4868042014-09-04 17:39:22 -0700359 case MSG_MERGE_CONFERENCE:
360 mergeConference((String) msg.obj);
361 break;
362 case MSG_SWAP_CONFERENCE:
363 swapConference((String) msg.obj);
364 break;
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700365 case MSG_ON_POST_DIAL_CONTINUE: {
366 SomeArgs args = (SomeArgs) msg.obj;
367 try {
368 String callId = (String) args.arg1;
369 boolean proceed = (args.argi1 == 1);
370 onPostDialContinue(callId, proceed);
371 } finally {
372 args.recycle();
373 }
374 break;
375 }
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700376 default:
377 break;
378 }
379 }
380 };
381
Santos Cordon823fd3c2014-08-07 18:35:18 -0700382 private final Conference.Listener mConferenceListener = new Conference.Listener() {
383 @Override
384 public void onStateChanged(Conference conference, int oldState, int newState) {
385 String id = mIdByConference.get(conference);
386 switch (newState) {
387 case Connection.STATE_ACTIVE:
388 mAdapter.setActive(id);
389 break;
390 case Connection.STATE_HOLDING:
391 mAdapter.setOnHold(id);
392 break;
393 case Connection.STATE_DISCONNECTED:
394 // handled by onDisconnected
395 break;
396 }
397 }
398
399 @Override
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700400 public void onDisconnected(Conference conference, DisconnectCause disconnectCause) {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700401 String id = mIdByConference.get(conference);
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700402 mAdapter.setDisconnected(id, disconnectCause);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700403 }
404
405 @Override
406 public void onConnectionAdded(Conference conference, Connection connection) {
407 }
408
409 @Override
410 public void onConnectionRemoved(Conference conference, Connection connection) {
411 }
412
413 @Override
Ihab Awad50e35062014-09-30 09:17:03 -0700414 public void onConferenceableConnectionsChanged(
415 Conference conference, List<Connection> conferenceableConnections) {
416 mAdapter.setConferenceableConnections(
417 mIdByConference.get(conference),
418 createConnectionIdList(conferenceableConnections));
419 }
420
421 @Override
Santos Cordon823fd3c2014-08-07 18:35:18 -0700422 public void onDestroyed(Conference conference) {
423 removeConference(conference);
424 }
425
426 @Override
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800427 public void onConnectionCapabilitiesChanged(
428 Conference conference,
429 int connectionCapabilities) {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700430 String id = mIdByConference.get(conference);
431 Log.d(this, "call capabilities: conference: %s",
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800432 Connection.capabilitiesToString(connectionCapabilities));
433 mAdapter.setConnectionCapabilities(id, connectionCapabilities);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700434 }
Rekha Kumar07366812015-03-24 16:42:31 -0700435
436 @Override
437 public void onVideoStateChanged(Conference c, int videoState) {
438 String id = mIdByConference.get(c);
439 Log.d(this, "onVideoStateChanged set video state %d", videoState);
440 mAdapter.setVideoState(id, videoState);
441 }
442
443 @Override
444 public void onVideoProviderChanged(Conference c, Connection.VideoProvider videoProvider) {
445 String id = mIdByConference.get(c);
446 Log.d(this, "onVideoProviderChanged: Connection: %s, VideoProvider: %s", c,
447 videoProvider);
448 mAdapter.setVideoProvider(id, videoProvider);
449 }
Andrew Lee0f51da32015-04-16 13:11:55 -0700450
451 @Override
Andrew Leeedc625f2015-04-14 13:38:12 -0700452 public void onStatusHintsChanged(Conference conference, StatusHints statusHints) {
453 String id = mIdByConference.get(conference);
454 mAdapter.setStatusHints(id, statusHints);
455 }
Santos Cordon6b7f9552015-05-27 17:21:45 -0700456
457 @Override
458 public void onExtrasChanged(Conference conference, Bundle extras) {
459 String id = mIdByConference.get(conference);
460 mAdapter.setExtras(id, extras);
461 }
Santos Cordon823fd3c2014-08-07 18:35:18 -0700462 };
463
Ihab Awad542e0ea2014-05-16 10:22:16 -0700464 private final Connection.Listener mConnectionListener = new Connection.Listener() {
465 @Override
466 public void onStateChanged(Connection c, int state) {
467 String id = mIdByConnection.get(c);
Ihab Awad42b30e12014-05-22 09:49:34 -0700468 Log.d(this, "Adapter set state %s %s", id, Connection.stateToString(state));
Ihab Awad542e0ea2014-05-16 10:22:16 -0700469 switch (state) {
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700470 case Connection.STATE_ACTIVE:
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700471 mAdapter.setActive(id);
Ihab Awad542e0ea2014-05-16 10:22:16 -0700472 break;
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700473 case Connection.STATE_DIALING:
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700474 mAdapter.setDialing(id);
Ihab Awad542e0ea2014-05-16 10:22:16 -0700475 break;
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700476 case Connection.STATE_DISCONNECTED:
Ihab Awad542e0ea2014-05-16 10:22:16 -0700477 // Handled in onDisconnected()
478 break;
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700479 case Connection.STATE_HOLDING:
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700480 mAdapter.setOnHold(id);
Ihab Awad542e0ea2014-05-16 10:22:16 -0700481 break;
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700482 case Connection.STATE_NEW:
Tyler Gunnef9f6f92014-09-12 22:16:17 -0700483 // Nothing to tell Telecom
Ihab Awad542e0ea2014-05-16 10:22:16 -0700484 break;
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700485 case Connection.STATE_RINGING:
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700486 mAdapter.setRinging(id);
Ihab Awad542e0ea2014-05-16 10:22:16 -0700487 break;
488 }
489 }
490
491 @Override
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700492 public void onDisconnected(Connection c, DisconnectCause disconnectCause) {
Ihab Awad542e0ea2014-05-16 10:22:16 -0700493 String id = mIdByConnection.get(c);
Andrew Lee26786392014-09-16 18:14:59 -0700494 Log.d(this, "Adapter set disconnected %s", disconnectCause);
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700495 mAdapter.setDisconnected(id, disconnectCause);
Ihab Awad542e0ea2014-05-16 10:22:16 -0700496 }
497
498 @Override
Tyler Gunnaa07df82014-07-17 07:50:22 -0700499 public void onVideoStateChanged(Connection c, int videoState) {
500 String id = mIdByConnection.get(c);
501 Log.d(this, "Adapter set video state %d", videoState);
502 mAdapter.setVideoState(id, videoState);
503 }
504
505 @Override
Andrew Lee100e2932014-09-08 15:34:24 -0700506 public void onAddressChanged(Connection c, Uri address, int presentation) {
Sailesh Nepal61203862014-07-11 14:50:13 -0700507 String id = mIdByConnection.get(c);
Andrew Lee100e2932014-09-08 15:34:24 -0700508 mAdapter.setAddress(id, address, presentation);
Sailesh Nepal61203862014-07-11 14:50:13 -0700509 }
510
511 @Override
512 public void onCallerDisplayNameChanged(
513 Connection c, String callerDisplayName, int presentation) {
514 String id = mIdByConnection.get(c);
515 mAdapter.setCallerDisplayName(id, callerDisplayName, presentation);
Ihab Awad542e0ea2014-05-16 10:22:16 -0700516 }
517
518 @Override
Ihab Awad542e0ea2014-05-16 10:22:16 -0700519 public void onDestroyed(Connection c) {
520 removeConnection(c);
521 }
Ihab Awadf8358972014-05-28 16:46:42 -0700522
523 @Override
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700524 public void onPostDialWait(Connection c, String remaining) {
Sailesh Nepal091768c2014-06-30 15:15:23 -0700525 String id = mIdByConnection.get(c);
526 Log.d(this, "Adapter onPostDialWait %s, %s", c, remaining);
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700527 mAdapter.onPostDialWait(id, remaining);
Sailesh Nepal091768c2014-06-30 15:15:23 -0700528 }
529
530 @Override
Nancy Chen27d1c2d2014-12-15 16:12:50 -0800531 public void onPostDialChar(Connection c, char nextChar) {
532 String id = mIdByConnection.get(c);
533 Log.d(this, "Adapter onPostDialChar %s, %s", c, nextChar);
534 mAdapter.onPostDialChar(id, nextChar);
535 }
536
537 @Override
Andrew Lee100e2932014-09-08 15:34:24 -0700538 public void onRingbackRequested(Connection c, boolean ringback) {
Ihab Awadf8358972014-05-28 16:46:42 -0700539 String id = mIdByConnection.get(c);
540 Log.d(this, "Adapter onRingback %b", ringback);
Andrew Lee100e2932014-09-08 15:34:24 -0700541 mAdapter.setRingbackRequested(id, ringback);
Ihab Awadf8358972014-05-28 16:46:42 -0700542 }
Santos Cordonb6939982014-06-04 20:20:58 -0700543
544 @Override
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800545 public void onConnectionCapabilitiesChanged(Connection c, int capabilities) {
Santos Cordonb6939982014-06-04 20:20:58 -0700546 String id = mIdByConnection.get(c);
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700547 Log.d(this, "capabilities: parcelableconnection: %s",
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800548 Connection.capabilitiesToString(capabilities));
549 mAdapter.setConnectionCapabilities(id, capabilities);
Santos Cordonb6939982014-06-04 20:20:58 -0700550 }
551
Santos Cordonb6939982014-06-04 20:20:58 -0700552 @Override
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700553 public void onVideoProviderChanged(Connection c, Connection.VideoProvider videoProvider) {
Andrew Lee5ffbe8b82014-06-20 16:29:33 -0700554 String id = mIdByConnection.get(c);
Rekha Kumar07366812015-03-24 16:42:31 -0700555 Log.d(this, "onVideoProviderChanged: Connection: %s, VideoProvider: %s", c,
556 videoProvider);
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700557 mAdapter.setVideoProvider(id, videoProvider);
Andrew Lee5ffbe8b82014-06-20 16:29:33 -0700558 }
Sailesh Nepal33aaae42014-07-07 22:49:44 -0700559
560 @Override
Sailesh Nepal001bbbb2014-07-15 14:40:39 -0700561 public void onAudioModeIsVoipChanged(Connection c, boolean isVoip) {
Sailesh Nepal33aaae42014-07-07 22:49:44 -0700562 String id = mIdByConnection.get(c);
Andrew Lee100e2932014-09-08 15:34:24 -0700563 mAdapter.setIsVoipAudioMode(id, isVoip);
Sailesh Nepal33aaae42014-07-07 22:49:44 -0700564 }
Sailesh Nepale7ef59a2014-07-08 21:48:22 -0700565
566 @Override
Sailesh Nepal001bbbb2014-07-15 14:40:39 -0700567 public void onStatusHintsChanged(Connection c, StatusHints statusHints) {
Sailesh Nepale7ef59a2014-07-08 21:48:22 -0700568 String id = mIdByConnection.get(c);
569 mAdapter.setStatusHints(id, statusHints);
570 }
Sailesh Nepal2ab88cc2014-07-18 14:49:18 -0700571
572 @Override
Tyler Gunn6d76ca02014-11-17 15:49:51 -0800573 public void onConferenceablesChanged(
Tyler Gunndf2cbc82015-04-20 09:13:01 -0700574 Connection connection, List<Conferenceable> conferenceables) {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700575 mAdapter.setConferenceableConnections(
576 mIdByConnection.get(connection),
Tyler Gunn6d76ca02014-11-17 15:49:51 -0800577 createIdList(conferenceables));
Santos Cordon7c7bc7f2014-07-28 18:15:48 -0700578 }
Santos Cordon823fd3c2014-08-07 18:35:18 -0700579
580 @Override
581 public void onConferenceChanged(Connection connection, Conference conference) {
582 String id = mIdByConnection.get(connection);
583 if (id != null) {
584 String conferenceId = null;
585 if (conference != null) {
586 conferenceId = mIdByConference.get(conference);
587 }
588 mAdapter.setIsConferenced(id, conferenceId);
589 }
590 }
Anthony Lee17455a32015-04-24 15:25:29 -0700591
592 @Override
593 public void onConferenceMergeFailed(Connection connection) {
594 String id = mIdByConnection.get(connection);
595 if (id != null) {
596 mAdapter.onConferenceMergeFailed(id);
597 }
598 }
Santos Cordon6b7f9552015-05-27 17:21:45 -0700599
600 @Override
601 public void onExtrasChanged(Connection connection, Bundle extras) {
602 String id = mIdByConnection.get(connection);
603 if (id != null) {
604 mAdapter.setExtras(id, extras);
605 }
606 }
Ihab Awad542e0ea2014-05-16 10:22:16 -0700607 };
608
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700609 /** {@inheritDoc} */
Ihab Awad542e0ea2014-05-16 10:22:16 -0700610 @Override
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700611 public final IBinder onBind(Intent intent) {
612 return mBinder;
613 }
614
Santos Cordon29f2f2e2014-09-11 19:50:24 -0700615 /** {@inheritDoc} */
616 @Override
617 public boolean onUnbind(Intent intent) {
618 endAllConnections();
619 return super.onUnbind(intent);
620 }
621
Sailesh Nepalc5b01572014-07-14 16:29:44 -0700622 /**
Tyler Gunnef9f6f92014-09-12 22:16:17 -0700623 * This can be used by telecom to either create a new outgoing call or attach to an existing
624 * incoming call. In either case, telecom will cycle through a set of services and call
Sailesh Nepalc5b01572014-07-14 16:29:44 -0700625 * createConnection util a connection service cancels the process or completes it successfully.
626 */
Ihab Awadf8b69882014-07-25 15:14:01 -0700627 private void createConnection(
628 final PhoneAccountHandle callManagerAccount,
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700629 final String callId,
Ihab Awadf8b69882014-07-25 15:14:01 -0700630 final ConnectionRequest request,
Yorke Leec3cf9822014-10-02 09:38:39 -0700631 boolean isIncoming,
632 boolean isUnknown) {
Sailesh Nepalcf7020b2014-08-20 10:07:19 -0700633 Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " +
Jack Yu67140302015-12-10 12:27:58 -0800634 "isIncoming: %b, isUnknown: %b", callManagerAccount, callId, request,
635 isIncoming,
Yorke Leec3cf9822014-10-02 09:38:39 -0700636 isUnknown);
Ihab Awad542e0ea2014-05-16 10:22:16 -0700637
Yorke Leec3cf9822014-10-02 09:38:39 -0700638 Connection connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request)
639 : isIncoming ? onCreateIncomingConnection(callManagerAccount, request)
Ihab Awad6107bab2014-08-18 09:23:25 -0700640 : onCreateOutgoingConnection(callManagerAccount, request);
Sailesh Nepalcf7020b2014-08-20 10:07:19 -0700641 Log.d(this, "createConnection, connection: %s", connection);
642 if (connection == null) {
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700643 connection = Connection.createFailedConnection(
644 new DisconnectCause(DisconnectCause.ERROR));
Sailesh Nepalc5b01572014-07-14 16:29:44 -0700645 }
Ihab Awad542e0ea2014-05-16 10:22:16 -0700646
Jack Yu67140302015-12-10 12:27:58 -0800647 connection.setTelecomCallId(callId);
Sailesh Nepalcf7020b2014-08-20 10:07:19 -0700648 if (connection.getState() != Connection.STATE_DISCONNECTED) {
Ihab Awad6107bab2014-08-18 09:23:25 -0700649 addConnection(callId, connection);
650 }
651
Andrew Lee100e2932014-09-08 15:34:24 -0700652 Uri address = connection.getAddress();
653 String number = address == null ? "null" : address.getSchemeSpecificPart();
Sailesh Nepalcf7020b2014-08-20 10:07:19 -0700654 Log.v(this, "createConnection, number: %s, state: %s, capabilities: %s",
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700655 Connection.toLogSafePhoneNumber(number),
Sailesh Nepalcf7020b2014-08-20 10:07:19 -0700656 Connection.stateToString(connection.getState()),
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800657 Connection.capabilitiesToString(connection.getConnectionCapabilities()));
Santos Cordon7c7bc7f2014-07-28 18:15:48 -0700658
Sailesh Nepalcf7020b2014-08-20 10:07:19 -0700659 Log.d(this, "createConnection, calling handleCreateConnectionSuccessful %s", callId);
Ihab Awad6107bab2014-08-18 09:23:25 -0700660 mAdapter.handleCreateConnectionComplete(
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700661 callId,
Evan Charltonbf11f982014-07-20 22:06:28 -0700662 request,
663 new ParcelableConnection(
664 request.getAccountHandle(),
665 connection.getState(),
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800666 connection.getConnectionCapabilities(),
Andrew Lee100e2932014-09-08 15:34:24 -0700667 connection.getAddress(),
668 connection.getAddressPresentation(),
Evan Charltonbf11f982014-07-20 22:06:28 -0700669 connection.getCallerDisplayName(),
670 connection.getCallerDisplayNamePresentation(),
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700671 connection.getVideoProvider() == null ?
672 null : connection.getVideoProvider().getInterface(),
Sailesh Nepal8b9d3ca2014-08-14 17:39:34 -0700673 connection.getVideoState(),
Andrew Lee100e2932014-09-08 15:34:24 -0700674 connection.isRingbackRequested(),
Sailesh Nepal8b9d3ca2014-08-14 17:39:34 -0700675 connection.getAudioModeIsVoip(),
Roshan Piuse927ec02015-07-15 15:47:21 -0700676 connection.getConnectTimeMillis(),
Ihab Awad6107bab2014-08-18 09:23:25 -0700677 connection.getStatusHints(),
Sailesh Nepalcf7020b2014-08-20 10:07:19 -0700678 connection.getDisconnectCause(),
Santos Cordon6b7f9552015-05-27 17:21:45 -0700679 createIdList(connection.getConferenceables()),
680 connection.getExtras()));
Shriram Ganesh6bf35ac2014-12-11 17:53:38 -0800681 if (isUnknown) {
682 triggerConferenceRecalculate();
683 }
Evan Charltonbf11f982014-07-20 22:06:28 -0700684 }
685
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700686 private void abort(String callId) {
Ihab Awad60ac30b2014-05-20 22:32:12 -0700687 Log.d(this, "abort %s", callId);
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700688 findConnectionForAction(callId, "abort").onAbort();
Ihab Awad542e0ea2014-05-16 10:22:16 -0700689 }
690
Tyler Gunnbe74de02014-08-29 14:51:48 -0700691 private void answerVideo(String callId, int videoState) {
692 Log.d(this, "answerVideo %s", callId);
Andrew Lee8da4c3c2014-07-16 10:11:42 -0700693 findConnectionForAction(callId, "answer").onAnswer(videoState);
Ihab Awad542e0ea2014-05-16 10:22:16 -0700694 }
695
Tyler Gunnbe74de02014-08-29 14:51:48 -0700696 private void answer(String callId) {
697 Log.d(this, "answer %s", callId);
698 findConnectionForAction(callId, "answer").onAnswer();
699 }
700
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700701 private void reject(String callId) {
Ihab Awad60ac30b2014-05-20 22:32:12 -0700702 Log.d(this, "reject %s", callId);
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700703 findConnectionForAction(callId, "reject").onReject();
Ihab Awad542e0ea2014-05-16 10:22:16 -0700704 }
705
Bryce Lee81901682015-08-28 16:38:02 -0700706 private void reject(String callId, String rejectWithMessage) {
707 Log.d(this, "reject %s with message", callId);
708 findConnectionForAction(callId, "reject").onReject(rejectWithMessage);
709 }
710
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700711 private void disconnect(String callId) {
Ihab Awad60ac30b2014-05-20 22:32:12 -0700712 Log.d(this, "disconnect %s", callId);
Santos Cordon0159ac02014-08-21 14:28:11 -0700713 if (mConnectionById.containsKey(callId)) {
714 findConnectionForAction(callId, "disconnect").onDisconnect();
715 } else {
716 findConferenceForAction(callId, "disconnect").onDisconnect();
717 }
Ihab Awad542e0ea2014-05-16 10:22:16 -0700718 }
719
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700720 private void hold(String callId) {
Ihab Awad60ac30b2014-05-20 22:32:12 -0700721 Log.d(this, "hold %s", callId);
Santos Cordon0159ac02014-08-21 14:28:11 -0700722 if (mConnectionById.containsKey(callId)) {
723 findConnectionForAction(callId, "hold").onHold();
724 } else {
725 findConferenceForAction(callId, "hold").onHold();
726 }
Ihab Awad542e0ea2014-05-16 10:22:16 -0700727 }
728
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700729 private void unhold(String callId) {
Ihab Awad60ac30b2014-05-20 22:32:12 -0700730 Log.d(this, "unhold %s", callId);
Santos Cordon0159ac02014-08-21 14:28:11 -0700731 if (mConnectionById.containsKey(callId)) {
732 findConnectionForAction(callId, "unhold").onUnhold();
733 } else {
734 findConferenceForAction(callId, "unhold").onUnhold();
735 }
Ihab Awad542e0ea2014-05-16 10:22:16 -0700736 }
737
Yorke Lee4af59352015-05-13 14:14:54 -0700738 private void onCallAudioStateChanged(String callId, CallAudioState callAudioState) {
739 Log.d(this, "onAudioStateChanged %s %s", callId, callAudioState);
Yorke Leea0d3ca92014-09-15 19:18:13 -0700740 if (mConnectionById.containsKey(callId)) {
Yorke Lee4af59352015-05-13 14:14:54 -0700741 findConnectionForAction(callId, "onCallAudioStateChanged").setCallAudioState(
742 callAudioState);
Yorke Leea0d3ca92014-09-15 19:18:13 -0700743 } else {
Yorke Lee4af59352015-05-13 14:14:54 -0700744 findConferenceForAction(callId, "onCallAudioStateChanged").setCallAudioState(
745 callAudioState);
Yorke Leea0d3ca92014-09-15 19:18:13 -0700746 }
Ihab Awad542e0ea2014-05-16 10:22:16 -0700747 }
748
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700749 private void playDtmfTone(String callId, char digit) {
750 Log.d(this, "playDtmfTone %s %c", callId, digit);
Yorke Leea0d3ca92014-09-15 19:18:13 -0700751 if (mConnectionById.containsKey(callId)) {
752 findConnectionForAction(callId, "playDtmfTone").onPlayDtmfTone(digit);
753 } else {
754 findConferenceForAction(callId, "playDtmfTone").onPlayDtmfTone(digit);
755 }
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700756 }
757
758 private void stopDtmfTone(String callId) {
759 Log.d(this, "stopDtmfTone %s", callId);
Yorke Leea0d3ca92014-09-15 19:18:13 -0700760 if (mConnectionById.containsKey(callId)) {
761 findConnectionForAction(callId, "stopDtmfTone").onStopDtmfTone();
762 } else {
763 findConferenceForAction(callId, "stopDtmfTone").onStopDtmfTone();
764 }
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700765 }
766
Santos Cordon823fd3c2014-08-07 18:35:18 -0700767 private void conference(String callId1, String callId2) {
768 Log.d(this, "conference %s, %s", callId1, callId2);
Santos Cordon980acb92014-05-31 10:31:19 -0700769
Tyler Gunn6d76ca02014-11-17 15:49:51 -0800770 // Attempt to get second connection or conference.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700771 Connection connection2 = findConnectionForAction(callId2, "conference");
Tyler Gunn6d76ca02014-11-17 15:49:51 -0800772 Conference conference2 = getNullConference();
Sailesh Nepalcf7020b2014-08-20 10:07:19 -0700773 if (connection2 == getNullConnection()) {
Tyler Gunn6d76ca02014-11-17 15:49:51 -0800774 conference2 = findConferenceForAction(callId2, "conference");
775 if (conference2 == getNullConference()) {
776 Log.w(this, "Connection2 or Conference2 missing in conference request %s.",
777 callId2);
778 return;
779 }
Santos Cordon823fd3c2014-08-07 18:35:18 -0700780 }
Santos Cordonb6939982014-06-04 20:20:58 -0700781
Tyler Gunn6d76ca02014-11-17 15:49:51 -0800782 // Attempt to get first connection or conference and perform merge.
Ihab Awad50e35062014-09-30 09:17:03 -0700783 Connection connection1 = findConnectionForAction(callId1, "conference");
784 if (connection1 == getNullConnection()) {
785 Conference conference1 = findConferenceForAction(callId1, "addConnection");
786 if (conference1 == getNullConference()) {
787 Log.w(this,
788 "Connection1 or Conference1 missing in conference request %s.",
789 callId1);
790 } else {
Tyler Gunn6d76ca02014-11-17 15:49:51 -0800791 // Call 1 is a conference.
792 if (connection2 != getNullConnection()) {
793 // Call 2 is a connection so merge via call 1 (conference).
794 conference1.onMerge(connection2);
795 } else {
796 // Call 2 is ALSO a conference; this should never happen.
797 Log.wtf(this, "There can only be one conference and an attempt was made to " +
798 "merge two conferences.");
799 return;
800 }
Ihab Awad50e35062014-09-30 09:17:03 -0700801 }
802 } else {
Tyler Gunn6d76ca02014-11-17 15:49:51 -0800803 // Call 1 is a connection.
804 if (conference2 != getNullConference()) {
805 // Call 2 is a conference, so merge via call 2.
806 conference2.onMerge(connection1);
807 } else {
808 // Call 2 is a connection, so merge together.
809 onConference(connection1, connection2);
810 }
Ihab Awad50e35062014-09-30 09:17:03 -0700811 }
Santos Cordon980acb92014-05-31 10:31:19 -0700812 }
813
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700814 private void splitFromConference(String callId) {
Santos Cordonb6939982014-06-04 20:20:58 -0700815 Log.d(this, "splitFromConference(%s)", callId);
Santos Cordon980acb92014-05-31 10:31:19 -0700816
817 Connection connection = findConnectionForAction(callId, "splitFromConference");
Sailesh Nepalcf7020b2014-08-20 10:07:19 -0700818 if (connection == getNullConnection()) {
Santos Cordon980acb92014-05-31 10:31:19 -0700819 Log.w(this, "Connection missing in conference request %s.", callId);
820 return;
821 }
822
Santos Cordon0159ac02014-08-21 14:28:11 -0700823 Conference conference = connection.getConference();
824 if (conference != null) {
825 conference.onSeparate(connection);
826 }
Santos Cordon980acb92014-05-31 10:31:19 -0700827 }
828
Santos Cordona4868042014-09-04 17:39:22 -0700829 private void mergeConference(String callId) {
830 Log.d(this, "mergeConference(%s)", callId);
831 Conference conference = findConferenceForAction(callId, "mergeConference");
832 if (conference != null) {
833 conference.onMerge();
834 }
835 }
836
837 private void swapConference(String callId) {
838 Log.d(this, "swapConference(%s)", callId);
839 Conference conference = findConferenceForAction(callId, "swapConference");
840 if (conference != null) {
841 conference.onSwap();
842 }
843 }
844
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700845 private void onPostDialContinue(String callId, boolean proceed) {
Evan Charlton6dea4ac2014-06-03 14:07:13 -0700846 Log.d(this, "onPostDialContinue(%s)", callId);
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700847 findConnectionForAction(callId, "stopDtmfTone").onPostDialContinue(proceed);
Evan Charlton6dea4ac2014-06-03 14:07:13 -0700848 }
849
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700850 private void onAdapterAttached() {
Ihab Awad9c3f1882014-06-30 21:17:13 -0700851 if (mAreAccountsInitialized) {
Santos Cordon52d8a152014-06-17 19:08:45 -0700852 // No need to query again if we already did it.
853 return;
854 }
855
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700856 mAdapter.queryRemoteConnectionServices(new RemoteServiceCallback.Stub() {
Santos Cordon52d8a152014-06-17 19:08:45 -0700857 @Override
858 public void onResult(
859 final List<ComponentName> componentNames,
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700860 final List<IBinder> services) {
Santos Cordon52d8a152014-06-17 19:08:45 -0700861 mHandler.post(new Runnable() {
Ihab Awad6107bab2014-08-18 09:23:25 -0700862 @Override
863 public void run() {
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700864 for (int i = 0; i < componentNames.size() && i < services.size(); i++) {
Santos Cordon52d8a152014-06-17 19:08:45 -0700865 mRemoteConnectionManager.addConnectionService(
866 componentNames.get(i),
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700867 IConnectionService.Stub.asInterface(services.get(i)));
Santos Cordon52d8a152014-06-17 19:08:45 -0700868 }
Ihab Awad5d0410f2014-07-30 10:07:40 -0700869 onAccountsInitialized();
Sailesh Nepalc5b01572014-07-14 16:29:44 -0700870 Log.d(this, "remote connection services found: " + services);
Santos Cordon52d8a152014-06-17 19:08:45 -0700871 }
872 });
873 }
874
875 @Override
876 public void onError() {
877 mHandler.post(new Runnable() {
Ihab Awad6107bab2014-08-18 09:23:25 -0700878 @Override
879 public void run() {
Ihab Awad9c3f1882014-06-30 21:17:13 -0700880 mAreAccountsInitialized = true;
Santos Cordon52d8a152014-06-17 19:08:45 -0700881 }
882 });
883 }
884 });
885 }
886
Ihab Awadf8b69882014-07-25 15:14:01 -0700887 /**
888 * Ask some other {@code ConnectionService} to create a {@code RemoteConnection} given an
Santos Cordona663f862014-10-29 13:49:58 -0700889 * incoming request. This is used by {@code ConnectionService}s that are registered with
890 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} and want to be able to manage
891 * SIM-based incoming calls.
Ihab Awadf8b69882014-07-25 15:14:01 -0700892 *
893 * @param connectionManagerPhoneAccount See description at
894 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
895 * @param request Details about the incoming call.
896 * @return The {@code Connection} object to satisfy this call, or {@code null} to
897 * not handle the call.
898 */
899 public final RemoteConnection createRemoteIncomingConnection(
900 PhoneAccountHandle connectionManagerPhoneAccount,
901 ConnectionRequest request) {
902 return mRemoteConnectionManager.createRemoteConnection(
903 connectionManagerPhoneAccount, request, true);
Santos Cordon52d8a152014-06-17 19:08:45 -0700904 }
905
906 /**
Ihab Awadf8b69882014-07-25 15:14:01 -0700907 * Ask some other {@code ConnectionService} to create a {@code RemoteConnection} given an
Santos Cordona663f862014-10-29 13:49:58 -0700908 * outgoing request. This is used by {@code ConnectionService}s that are registered with
909 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} and want to be able to use the
910 * SIM-based {@code ConnectionService} to place its outgoing calls.
Ihab Awadf8b69882014-07-25 15:14:01 -0700911 *
912 * @param connectionManagerPhoneAccount See description at
913 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
914 * @param request Details about the incoming call.
915 * @return The {@code Connection} object to satisfy this call, or {@code null} to
916 * not handle the call.
917 */
918 public final RemoteConnection createRemoteOutgoingConnection(
919 PhoneAccountHandle connectionManagerPhoneAccount,
920 ConnectionRequest request) {
921 return mRemoteConnectionManager.createRemoteConnection(
922 connectionManagerPhoneAccount, request, false);
923 }
924
925 /**
Santos Cordona663f862014-10-29 13:49:58 -0700926 * Indicates to the relevant {@code RemoteConnectionService} that the specified
927 * {@link RemoteConnection}s should be merged into a conference call.
928 * <p>
929 * If the conference request is successful, the method {@link #onRemoteConferenceAdded} will
930 * be invoked.
931 *
932 * @param remoteConnection1 The first of the remote connections to conference.
933 * @param remoteConnection2 The second of the remote connections to conference.
Ihab Awadb8e85c72014-08-23 20:34:57 -0700934 */
935 public final void conferenceRemoteConnections(
Santos Cordona663f862014-10-29 13:49:58 -0700936 RemoteConnection remoteConnection1,
937 RemoteConnection remoteConnection2) {
938 mRemoteConnectionManager.conferenceRemoteConnections(remoteConnection1, remoteConnection2);
Ihab Awadb8e85c72014-08-23 20:34:57 -0700939 }
940
941 /**
Santos Cordon823fd3c2014-08-07 18:35:18 -0700942 * Adds a new conference call. When a conference call is created either as a result of an
943 * explicit request via {@link #onConference} or otherwise, the connection service should supply
944 * an instance of {@link Conference} by invoking this method. A conference call provided by this
945 * method will persist until {@link Conference#destroy} is invoked on the conference instance.
946 *
947 * @param conference The new conference object.
948 */
949 public final void addConference(Conference conference) {
Rekha Kumar07366812015-03-24 16:42:31 -0700950 Log.d(this, "addConference: conference=%s", conference);
951
Santos Cordon823fd3c2014-08-07 18:35:18 -0700952 String id = addConferenceInternal(conference);
953 if (id != null) {
954 List<String> connectionIds = new ArrayList<>(2);
955 for (Connection connection : conference.getConnections()) {
956 if (mIdByConnection.containsKey(connection)) {
957 connectionIds.add(mIdByConnection.get(connection));
958 }
959 }
Jack Yu67140302015-12-10 12:27:58 -0800960 conference.setTelecomCallId(id);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700961 ParcelableConference parcelableConference = new ParcelableConference(
Nancy Chenea38cca2014-09-05 16:38:49 -0700962 conference.getPhoneAccountHandle(),
Santos Cordon823fd3c2014-08-07 18:35:18 -0700963 conference.getState(),
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800964 conference.getConnectionCapabilities(),
Tyler Gunncd5d33c2015-01-12 09:02:01 -0800965 connectionIds,
Rekha Kumar07366812015-03-24 16:42:31 -0700966 conference.getVideoProvider() == null ?
967 null : conference.getVideoProvider().getInterface(),
968 conference.getVideoState(),
Andrew Lee3e3e2f22015-04-16 13:48:43 -0700969 conference.getConnectTimeMillis(),
Santos Cordon6b7f9552015-05-27 17:21:45 -0700970 conference.getStatusHints(),
971 conference.getExtras());
Andrew Lee0f51da32015-04-16 13:11:55 -0700972
Santos Cordon823fd3c2014-08-07 18:35:18 -0700973 mAdapter.addConferenceCall(id, parcelableConference);
Rekha Kumar07366812015-03-24 16:42:31 -0700974 mAdapter.setVideoProvider(id, conference.getVideoProvider());
975 mAdapter.setVideoState(id, conference.getVideoState());
Santos Cordon823fd3c2014-08-07 18:35:18 -0700976
977 // Go through any child calls and set the parent.
978 for (Connection connection : conference.getConnections()) {
979 String connectionId = mIdByConnection.get(connection);
980 if (connectionId != null) {
981 mAdapter.setIsConferenced(connectionId, id);
982 }
983 }
984 }
985 }
986
987 /**
Tyler Gunn4a57b9b2014-10-30 14:27:48 -0700988 * Adds a connection created by the {@link ConnectionService} and informs telecom of the new
989 * connection.
990 *
991 * @param phoneAccountHandle The phone account handle for the connection.
992 * @param connection The connection to add.
993 */
994 public final void addExistingConnection(PhoneAccountHandle phoneAccountHandle,
995 Connection connection) {
996
Jack Yu67140302015-12-10 12:27:58 -0800997 String id = addExistingConnectionInternal(phoneAccountHandle, connection);
Tyler Gunn4a57b9b2014-10-30 14:27:48 -0700998 if (id != null) {
999 List<String> emptyList = new ArrayList<>(0);
1000
1001 ParcelableConnection parcelableConnection = new ParcelableConnection(
1002 phoneAccountHandle,
1003 connection.getState(),
Ihab Awad5c9c86e2014-11-12 13:41:16 -08001004 connection.getConnectionCapabilities(),
Tyler Gunn4a57b9b2014-10-30 14:27:48 -07001005 connection.getAddress(),
1006 connection.getAddressPresentation(),
1007 connection.getCallerDisplayName(),
1008 connection.getCallerDisplayNamePresentation(),
1009 connection.getVideoProvider() == null ?
1010 null : connection.getVideoProvider().getInterface(),
1011 connection.getVideoState(),
1012 connection.isRingbackRequested(),
1013 connection.getAudioModeIsVoip(),
Roshan Piuse927ec02015-07-15 15:47:21 -07001014 connection.getConnectTimeMillis(),
Tyler Gunn4a57b9b2014-10-30 14:27:48 -07001015 connection.getStatusHints(),
1016 connection.getDisconnectCause(),
Santos Cordon6b7f9552015-05-27 17:21:45 -07001017 emptyList,
1018 connection.getExtras());
Tyler Gunn4a57b9b2014-10-30 14:27:48 -07001019 mAdapter.addExistingConnection(id, parcelableConnection);
1020 }
1021 }
1022
1023 /**
Ihab Awadf8b69882014-07-25 15:14:01 -07001024 * Returns all the active {@code Connection}s for which this {@code ConnectionService}
1025 * has taken responsibility.
1026 *
1027 * @return A collection of {@code Connection}s created by this {@code ConnectionService}.
Santos Cordonb6939982014-06-04 20:20:58 -07001028 */
Sailesh Nepal091768c2014-06-30 15:15:23 -07001029 public final Collection<Connection> getAllConnections() {
Santos Cordonb6939982014-06-04 20:20:58 -07001030 return mConnectionById.values();
1031 }
1032
1033 /**
Ihab Awadf8b69882014-07-25 15:14:01 -07001034 * Create a {@code Connection} given an incoming request. This is used to attach to existing
1035 * incoming calls.
Evan Charltonbf11f982014-07-20 22:06:28 -07001036 *
Ihab Awadf8b69882014-07-25 15:14:01 -07001037 * @param connectionManagerPhoneAccount See description at
1038 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
1039 * @param request Details about the incoming call.
1040 * @return The {@code Connection} object to satisfy this call, or {@code null} to
1041 * not handle the call.
Ihab Awad542e0ea2014-05-16 10:22:16 -07001042 */
Ihab Awadf8b69882014-07-25 15:14:01 -07001043 public Connection onCreateIncomingConnection(
1044 PhoneAccountHandle connectionManagerPhoneAccount,
1045 ConnectionRequest request) {
1046 return null;
1047 }
Sailesh Nepalc5b01572014-07-14 16:29:44 -07001048
1049 /**
Shriram Ganesh6bf35ac2014-12-11 17:53:38 -08001050 * Trigger recalculate functinality for conference calls. This is used when a Telephony
1051 * Connection is part of a conference controller but is not yet added to Connection
1052 * Service and hence cannot be added to the conference call.
1053 *
1054 * @hide
1055 */
1056 public void triggerConferenceRecalculate() {
1057 }
1058
1059 /**
Ihab Awadf8b69882014-07-25 15:14:01 -07001060 * Create a {@code Connection} given an outgoing request. This is used to initiate new
1061 * outgoing calls.
Sailesh Nepalc5b01572014-07-14 16:29:44 -07001062 *
Ihab Awadf8b69882014-07-25 15:14:01 -07001063 * @param connectionManagerPhoneAccount The connection manager account to use for managing
1064 * this call.
1065 * <p>
1066 * If this parameter is not {@code null}, it means that this {@code ConnectionService}
1067 * has registered one or more {@code PhoneAccount}s having
1068 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}. This parameter will contain
1069 * one of these {@code PhoneAccount}s, while the {@code request} will contain another
1070 * (usually but not always distinct) {@code PhoneAccount} to be used for actually
1071 * making the connection.
1072 * <p>
1073 * If this parameter is {@code null}, it means that this {@code ConnectionService} is
1074 * being asked to make a direct connection. The
1075 * {@link ConnectionRequest#getAccountHandle()} of parameter {@code request} will be
1076 * a {@code PhoneAccount} registered by this {@code ConnectionService} to use for
1077 * making the connection.
1078 * @param request Details about the outgoing call.
1079 * @return The {@code Connection} object to satisfy this call, or the result of an invocation
Andrew Lee7f3d41f2014-09-11 17:33:16 -07001080 * of {@link Connection#createFailedConnection(DisconnectCause)} to not handle the call.
Sailesh Nepalc5b01572014-07-14 16:29:44 -07001081 */
Ihab Awadf8b69882014-07-25 15:14:01 -07001082 public Connection onCreateOutgoingConnection(
1083 PhoneAccountHandle connectionManagerPhoneAccount,
1084 ConnectionRequest request) {
1085 return null;
1086 }
Ihab Awad542e0ea2014-05-16 10:22:16 -07001087
1088 /**
Yorke Leec3cf9822014-10-02 09:38:39 -07001089 * Create a {@code Connection} for a new unknown call. An unknown call is a call originating
1090 * from the ConnectionService that was neither a user-initiated outgoing call, nor an incoming
1091 * call created using
1092 * {@code TelecomManager#addNewIncomingCall(PhoneAccountHandle, android.os.Bundle)}.
1093 *
1094 * @param connectionManagerPhoneAccount
1095 * @param request
1096 * @return
Yorke Lee770ed6e2014-10-06 18:58:52 -07001097 *
1098 * @hide
Yorke Leec3cf9822014-10-02 09:38:39 -07001099 */
1100 public Connection onCreateUnknownConnection(PhoneAccountHandle connectionManagerPhoneAccount,
1101 ConnectionRequest request) {
1102 return null;
1103 }
1104
1105 /**
Santos Cordon823fd3c2014-08-07 18:35:18 -07001106 * Conference two specified connections. Invoked when the user has made a request to merge the
1107 * specified connections into a conference call. In response, the connection service should
1108 * create an instance of {@link Conference} and pass it into {@link #addConference}.
Santos Cordonb6939982014-06-04 20:20:58 -07001109 *
Santos Cordon823fd3c2014-08-07 18:35:18 -07001110 * @param connection1 A connection to merge into a conference call.
1111 * @param connection2 A connection to merge into a conference call.
Santos Cordonb6939982014-06-04 20:20:58 -07001112 */
Santos Cordon823fd3c2014-08-07 18:35:18 -07001113 public void onConference(Connection connection1, Connection connection2) {}
Santos Cordonb6939982014-06-04 20:20:58 -07001114
Santos Cordona663f862014-10-29 13:49:58 -07001115 /**
1116 * Indicates that a remote conference has been created for existing {@link RemoteConnection}s.
1117 * When this method is invoked, this {@link ConnectionService} should create its own
1118 * representation of the conference call and send it to telecom using {@link #addConference}.
1119 * <p>
1120 * This is only relevant to {@link ConnectionService}s which are registered with
1121 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}.
1122 *
1123 * @param conference The remote conference call.
1124 */
Ihab Awadb8e85c72014-08-23 20:34:57 -07001125 public void onRemoteConferenceAdded(RemoteConference conference) {}
1126
Santos Cordon823fd3c2014-08-07 18:35:18 -07001127 /**
Tyler Gunn4a57b9b2014-10-30 14:27:48 -07001128 * Called when an existing connection is added remotely.
1129 * @param connection The existing connection which was added.
1130 */
1131 public void onRemoteExistingConnectionAdded(RemoteConnection connection) {}
1132
1133 /**
Santos Cordon823fd3c2014-08-07 18:35:18 -07001134 * @hide
1135 */
1136 public boolean containsConference(Conference conference) {
1137 return mIdByConference.containsKey(conference);
1138 }
1139
Ihab Awadb8e85c72014-08-23 20:34:57 -07001140 /** {@hide} */
1141 void addRemoteConference(RemoteConference remoteConference) {
1142 onRemoteConferenceAdded(remoteConference);
1143 }
1144
Tyler Gunn4a57b9b2014-10-30 14:27:48 -07001145 /** {@hide} */
1146 void addRemoteExistingConnection(RemoteConnection remoteConnection) {
1147 onRemoteExistingConnectionAdded(remoteConnection);
1148 }
1149
Ihab Awad5d0410f2014-07-30 10:07:40 -07001150 private void onAccountsInitialized() {
1151 mAreAccountsInitialized = true;
1152 for (Runnable r : mPreInitializationConnectionRequests) {
1153 r.run();
1154 }
1155 mPreInitializationConnectionRequests.clear();
1156 }
1157
Tyler Gunn4a57b9b2014-10-30 14:27:48 -07001158 /**
Jack Yu67140302015-12-10 12:27:58 -08001159 * Adds an existing connection to the list of connections, identified by a new call ID unique
1160 * to this connection service.
Tyler Gunn4a57b9b2014-10-30 14:27:48 -07001161 *
1162 * @param connection The connection.
Jack Yu67140302015-12-10 12:27:58 -08001163 * @return The ID of the connection (e.g. the call-id).
Tyler Gunn4a57b9b2014-10-30 14:27:48 -07001164 */
Jack Yu67140302015-12-10 12:27:58 -08001165 private String addExistingConnectionInternal(PhoneAccountHandle handle, Connection connection) {
1166 String id;
1167 if (handle == null) {
1168 // If no phone account handle was provided, we cannot be sure the call ID is unique,
1169 // so just use a random UUID.
1170 id = UUID.randomUUID().toString();
1171 } else {
1172 // Phone account handle was provided, so use the ConnectionService class name as a
1173 // prefix for a unique incremental call ID.
1174 id = handle.getComponentName().getClassName() + "@" + getNextCallId();
1175 }
Tyler Gunn4a57b9b2014-10-30 14:27:48 -07001176 addConnection(id, connection);
1177 return id;
1178 }
1179
Ihab Awad542e0ea2014-05-16 10:22:16 -07001180 private void addConnection(String callId, Connection connection) {
Jack Yu67140302015-12-10 12:27:58 -08001181 connection.setTelecomCallId(callId);
Ihab Awad542e0ea2014-05-16 10:22:16 -07001182 mConnectionById.put(callId, connection);
1183 mIdByConnection.put(connection, callId);
1184 connection.addConnectionListener(mConnectionListener);
Santos Cordon823fd3c2014-08-07 18:35:18 -07001185 connection.setConnectionService(this);
Ihab Awad542e0ea2014-05-16 10:22:16 -07001186 }
1187
Anthony Lee30e65842014-11-06 16:30:53 -08001188 /** {@hide} */
1189 protected void removeConnection(Connection connection) {
Ihab Awad8aecfed2014-08-08 17:06:11 -07001190 String id = mIdByConnection.get(connection);
Santos Cordon823fd3c2014-08-07 18:35:18 -07001191 connection.unsetConnectionService(this);
Ihab Awad542e0ea2014-05-16 10:22:16 -07001192 connection.removeConnectionListener(mConnectionListener);
1193 mConnectionById.remove(mIdByConnection.get(connection));
1194 mIdByConnection.remove(connection);
Ihab Awad8aecfed2014-08-08 17:06:11 -07001195 mAdapter.removeCall(id);
Ihab Awad542e0ea2014-05-16 10:22:16 -07001196 }
1197
Santos Cordon823fd3c2014-08-07 18:35:18 -07001198 private String addConferenceInternal(Conference conference) {
1199 if (mIdByConference.containsKey(conference)) {
1200 Log.w(this, "Re-adding an existing conference: %s.", conference);
1201 } else if (conference != null) {
Jack Yu67140302015-12-10 12:27:58 -08001202 // Conferences do not (yet) have a PhoneAccountHandle associated with them, so we
1203 // cannot determine a ConnectionService class name to associate with the ID, so use
1204 // a unique UUID (for now).
Santos Cordon823fd3c2014-08-07 18:35:18 -07001205 String id = UUID.randomUUID().toString();
1206 mConferenceById.put(id, conference);
1207 mIdByConference.put(conference, id);
1208 conference.addListener(mConferenceListener);
1209 return id;
1210 }
1211
1212 return null;
1213 }
1214
1215 private void removeConference(Conference conference) {
1216 if (mIdByConference.containsKey(conference)) {
1217 conference.removeListener(mConferenceListener);
1218
1219 String id = mIdByConference.get(conference);
1220 mConferenceById.remove(id);
1221 mIdByConference.remove(conference);
1222 mAdapter.removeCall(id);
1223 }
1224 }
1225
Ihab Awad542e0ea2014-05-16 10:22:16 -07001226 private Connection findConnectionForAction(String callId, String action) {
1227 if (mConnectionById.containsKey(callId)) {
1228 return mConnectionById.get(callId);
1229 }
Ihab Awad60ac30b2014-05-20 22:32:12 -07001230 Log.w(this, "%s - Cannot find Connection %s", action, callId);
Sailesh Nepalcf7020b2014-08-20 10:07:19 -07001231 return getNullConnection();
1232 }
1233
1234 static synchronized Connection getNullConnection() {
1235 if (sNullConnection == null) {
1236 sNullConnection = new Connection() {};
1237 }
1238 return sNullConnection;
Santos Cordon7c7bc7f2014-07-28 18:15:48 -07001239 }
Santos Cordon0159ac02014-08-21 14:28:11 -07001240
1241 private Conference findConferenceForAction(String conferenceId, String action) {
1242 if (mConferenceById.containsKey(conferenceId)) {
1243 return mConferenceById.get(conferenceId);
1244 }
1245 Log.w(this, "%s - Cannot find conference %s", action, conferenceId);
1246 return getNullConference();
1247 }
1248
Ihab Awadb8e85c72014-08-23 20:34:57 -07001249 private List<String> createConnectionIdList(List<Connection> connections) {
1250 List<String> ids = new ArrayList<>();
1251 for (Connection c : connections) {
1252 if (mIdByConnection.containsKey(c)) {
1253 ids.add(mIdByConnection.get(c));
1254 }
1255 }
1256 Collections.sort(ids);
1257 return ids;
1258 }
1259
Tyler Gunn6d76ca02014-11-17 15:49:51 -08001260 /**
1261 * Builds a list of {@link Connection} and {@link Conference} IDs based on the list of
Tyler Gunndf2cbc82015-04-20 09:13:01 -07001262 * {@link Conferenceable}s passed in.
Tyler Gunn6d76ca02014-11-17 15:49:51 -08001263 *
Tyler Gunndf2cbc82015-04-20 09:13:01 -07001264 * @param conferenceables The {@link Conferenceable} connections and conferences.
Tyler Gunn6d76ca02014-11-17 15:49:51 -08001265 * @return List of string conference and call Ids.
1266 */
Tyler Gunndf2cbc82015-04-20 09:13:01 -07001267 private List<String> createIdList(List<Conferenceable> conferenceables) {
Tyler Gunn6d76ca02014-11-17 15:49:51 -08001268 List<String> ids = new ArrayList<>();
Tyler Gunndf2cbc82015-04-20 09:13:01 -07001269 for (Conferenceable c : conferenceables) {
Tyler Gunn6d76ca02014-11-17 15:49:51 -08001270 // Only allow Connection and Conference conferenceables.
1271 if (c instanceof Connection) {
1272 Connection connection = (Connection) c;
1273 if (mIdByConnection.containsKey(connection)) {
1274 ids.add(mIdByConnection.get(connection));
1275 }
1276 } else if (c instanceof Conference) {
1277 Conference conference = (Conference) c;
1278 if (mIdByConference.containsKey(conference)) {
1279 ids.add(mIdByConference.get(conference));
1280 }
1281 }
1282 }
1283 Collections.sort(ids);
1284 return ids;
1285 }
1286
Santos Cordon0159ac02014-08-21 14:28:11 -07001287 private Conference getNullConference() {
1288 if (sNullConference == null) {
1289 sNullConference = new Conference(null) {};
1290 }
1291 return sNullConference;
1292 }
Santos Cordon29f2f2e2014-09-11 19:50:24 -07001293
1294 private void endAllConnections() {
1295 // Unbound from telecomm. We should end all connections and conferences.
1296 for (Connection connection : mIdByConnection.keySet()) {
1297 // only operate on top-level calls. Conference calls will be removed on their own.
1298 if (connection.getConference() == null) {
1299 connection.onDisconnect();
1300 }
1301 }
1302 for (Conference conference : mIdByConference.keySet()) {
1303 conference.onDisconnect();
1304 }
1305 }
Jack Yu67140302015-12-10 12:27:58 -08001306
1307 /**
1308 * Retrieves the next call ID as maintainted by the connection service.
1309 *
1310 * @return The call ID.
1311 */
1312 private int getNextCallId() {
1313 synchronized(mIdSyncRoot) {
1314 return ++mId;
1315 }
1316 }
Santos Cordon980acb92014-05-31 10:31:19 -07001317}