blob: a39e885204b7fa7c7753ac0632632a94744995d4 [file] [log] [blame]
Santos Cordon823fd3c2014-08-07 18:35:18 -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;
Santos Cordon823fd3c2014-08-07 18:35:18 -070018
Tyler Gunndee56a82016-03-23 16:06:34 -070019import android.annotation.NonNull;
Santos Cordon6b7f9552015-05-27 17:21:45 -070020import android.annotation.Nullable;
Santos Cordon5d2e4f22015-05-12 12:32:51 -070021import android.annotation.SystemApi;
Santos Cordon6b7f9552015-05-27 17:21:45 -070022import android.os.Bundle;
Tyler Gunn3fa819c2017-08-04 09:27:26 -070023import android.os.SystemClock;
Rekha Kumar07366812015-03-24 16:42:31 -070024import android.telecom.Connection.VideoProvider;
Wei Huang7f7f72e2018-05-30 19:21:36 +080025import android.telephony.ServiceState;
26import android.telephony.TelephonyManager;
Tyler Gunndee56a82016-03-23 16:06:34 -070027import android.util.ArraySet;
Evan Charlton0e094d92014-11-08 15:49:16 -080028
Ihab Awad50e35062014-09-30 09:17:03 -070029import java.util.ArrayList;
Tyler Gunn071be6f2016-05-10 14:52:33 -070030import java.util.Arrays;
Santos Cordon823fd3c2014-08-07 18:35:18 -070031import java.util.Collections;
Santos Cordon823fd3c2014-08-07 18:35:18 -070032import java.util.List;
Rekha Kumar07366812015-03-24 16:42:31 -070033import java.util.Locale;
Santos Cordon823fd3c2014-08-07 18:35:18 -070034import java.util.Set;
35import java.util.concurrent.CopyOnWriteArrayList;
36import java.util.concurrent.CopyOnWriteArraySet;
37
38/**
39 * Represents a conference call which can contain any number of {@link Connection} objects.
40 */
Yorke Leeabfcfdc2015-05-13 18:55:18 -070041public abstract class Conference extends Conferenceable {
Santos Cordon823fd3c2014-08-07 18:35:18 -070042
Tyler Gunncd5d33c2015-01-12 09:02:01 -080043 /**
44 * Used to indicate that the conference connection time is not specified. If not specified,
45 * Telecom will set the connect time.
46 */
Jay Shrauner164a0ac2015-04-14 18:16:10 -070047 public static final long CONNECT_TIME_NOT_SPECIFIED = 0;
Tyler Gunncd5d33c2015-01-12 09:02:01 -080048
Santos Cordon823fd3c2014-08-07 18:35:18 -070049 /** @hide */
50 public abstract static class Listener {
51 public void onStateChanged(Conference conference, int oldState, int newState) {}
Andrew Lee7f3d41f2014-09-11 17:33:16 -070052 public void onDisconnected(Conference conference, DisconnectCause disconnectCause) {}
Santos Cordon823fd3c2014-08-07 18:35:18 -070053 public void onConnectionAdded(Conference conference, Connection connection) {}
54 public void onConnectionRemoved(Conference conference, Connection connection) {}
Ihab Awad50e35062014-09-30 09:17:03 -070055 public void onConferenceableConnectionsChanged(
56 Conference conference, List<Connection> conferenceableConnections) {}
Santos Cordon823fd3c2014-08-07 18:35:18 -070057 public void onDestroyed(Conference conference) {}
Ihab Awad5c9c86e2014-11-12 13:41:16 -080058 public void onConnectionCapabilitiesChanged(
59 Conference conference, int connectionCapabilities) {}
Tyler Gunn720c6642016-03-22 09:02:47 -070060 public void onConnectionPropertiesChanged(
61 Conference conference, int connectionProperties) {}
Rekha Kumar07366812015-03-24 16:42:31 -070062 public void onVideoStateChanged(Conference c, int videoState) { }
63 public void onVideoProviderChanged(Conference c, Connection.VideoProvider videoProvider) {}
Andrew Leeedc625f2015-04-14 13:38:12 -070064 public void onStatusHintsChanged(Conference conference, StatusHints statusHints) {}
Tyler Gunndee56a82016-03-23 16:06:34 -070065 public void onExtrasChanged(Conference c, Bundle extras) {}
66 public void onExtrasRemoved(Conference c, List<String> keys) {}
Santos Cordon823fd3c2014-08-07 18:35:18 -070067 }
68
69 private final Set<Listener> mListeners = new CopyOnWriteArraySet<>();
70 private final List<Connection> mChildConnections = new CopyOnWriteArrayList<>();
Ihab Awadb8e85c72014-08-23 20:34:57 -070071 private final List<Connection> mUnmodifiableChildConnections =
Santos Cordon823fd3c2014-08-07 18:35:18 -070072 Collections.unmodifiableList(mChildConnections);
Ihab Awad50e35062014-09-30 09:17:03 -070073 private final List<Connection> mConferenceableConnections = new ArrayList<>();
74 private final List<Connection> mUnmodifiableConferenceableConnections =
75 Collections.unmodifiableList(mConferenceableConnections);
Santos Cordon823fd3c2014-08-07 18:35:18 -070076
Jack Yu67140302015-12-10 12:27:58 -080077 private String mTelecomCallId;
Jay Shrauner164a0ac2015-04-14 18:16:10 -070078 private PhoneAccountHandle mPhoneAccount;
Yorke Lee4af59352015-05-13 14:14:54 -070079 private CallAudioState mCallAudioState;
Santos Cordon823fd3c2014-08-07 18:35:18 -070080 private int mState = Connection.STATE_NEW;
Andrew Lee7f3d41f2014-09-11 17:33:16 -070081 private DisconnectCause mDisconnectCause;
Ihab Awad5c9c86e2014-11-12 13:41:16 -080082 private int mConnectionCapabilities;
Tyler Gunn720c6642016-03-22 09:02:47 -070083 private int mConnectionProperties;
Santos Cordon823fd3c2014-08-07 18:35:18 -070084 private String mDisconnectMessage;
Tyler Gunncd5d33c2015-01-12 09:02:01 -080085 private long mConnectTimeMillis = CONNECT_TIME_NOT_SPECIFIED;
Tyler Gunn17541392018-02-01 08:58:38 -080086 private long mConnectionStartElapsedRealTime = CONNECT_TIME_NOT_SPECIFIED;
Andrew Leeedc625f2015-04-14 13:38:12 -070087 private StatusHints mStatusHints;
Santos Cordon6b7f9552015-05-27 17:21:45 -070088 private Bundle mExtras;
Tyler Gunndee56a82016-03-23 16:06:34 -070089 private Set<String> mPreviousExtraKeys;
Brad Ebinger4fa6a012016-06-14 17:04:01 -070090 private final Object mExtrasLock = new Object();
Santos Cordon823fd3c2014-08-07 18:35:18 -070091
Ihab Awad50e35062014-09-30 09:17:03 -070092 private final Connection.Listener mConnectionDeathListener = new Connection.Listener() {
93 @Override
94 public void onDestroyed(Connection c) {
95 if (mConferenceableConnections.remove(c)) {
96 fireOnConferenceableConnectionsChanged();
97 }
98 }
99 };
100
Nancy Chen56fc25d2014-09-09 12:24:51 -0700101 /**
102 * Constructs a new Conference with a mandatory {@link PhoneAccountHandle}
103 *
104 * @param phoneAccount The {@code PhoneAccountHandle} associated with the conference.
105 */
Santos Cordon823fd3c2014-08-07 18:35:18 -0700106 public Conference(PhoneAccountHandle phoneAccount) {
107 mPhoneAccount = phoneAccount;
108 }
109
Nancy Chen56fc25d2014-09-09 12:24:51 -0700110 /**
Jack Yu67140302015-12-10 12:27:58 -0800111 * Returns the telecom internal call ID associated with this conference.
112 *
113 * @return The telecom call ID.
114 * @hide
115 */
116 public final String getTelecomCallId() {
117 return mTelecomCallId;
118 }
119
120 /**
121 * Sets the telecom internal call ID associated with this conference.
122 *
123 * @param telecomCallId The telecom call ID.
124 * @hide
125 */
126 public final void setTelecomCallId(String telecomCallId) {
127 mTelecomCallId = telecomCallId;
128 }
129
130 /**
Nancy Chen56fc25d2014-09-09 12:24:51 -0700131 * Returns the {@link PhoneAccountHandle} the conference call is being placed through.
132 *
133 * @return A {@code PhoneAccountHandle} object representing the PhoneAccount of the conference.
134 */
Nancy Chenea38cca2014-09-05 16:38:49 -0700135 public final PhoneAccountHandle getPhoneAccountHandle() {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700136 return mPhoneAccount;
137 }
138
Nancy Chen56fc25d2014-09-09 12:24:51 -0700139 /**
140 * Returns the list of connections currently associated with the conference call.
141 *
142 * @return A list of {@code Connection} objects which represent the children of the conference.
143 */
Santos Cordon823fd3c2014-08-07 18:35:18 -0700144 public final List<Connection> getConnections() {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700145 return mUnmodifiableChildConnections;
Santos Cordon823fd3c2014-08-07 18:35:18 -0700146 }
147
Nancy Chen56fc25d2014-09-09 12:24:51 -0700148 /**
149 * Gets the state of the conference call. See {@link Connection} for valid values.
150 *
151 * @return A constant representing the state the conference call is currently in.
152 */
Santos Cordon823fd3c2014-08-07 18:35:18 -0700153 public final int getState() {
154 return mState;
155 }
156
Nancy Chen56fc25d2014-09-09 12:24:51 -0700157 /**
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700158 * Returns the capabilities of the conference. See {@code CAPABILITY_*} constants in class
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800159 * {@link Connection} for valid values.
Nancy Chen56fc25d2014-09-09 12:24:51 -0700160 *
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800161 * @return A bitmask of the capabilities of the conference call.
Nancy Chen56fc25d2014-09-09 12:24:51 -0700162 */
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800163 public final int getConnectionCapabilities() {
164 return mConnectionCapabilities;
165 }
166
167 /**
Tyler Gunn720c6642016-03-22 09:02:47 -0700168 * Returns the properties of the conference. See {@code PROPERTY_*} constants in class
169 * {@link Connection} for valid values.
170 *
171 * @return A bitmask of the properties of the conference call.
172 */
173 public final int getConnectionProperties() {
174 return mConnectionProperties;
175 }
176
177 /**
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800178 * Whether the given capabilities support the specified capability.
179 *
180 * @param capabilities A capability bit field.
181 * @param capability The capability to check capabilities for.
182 * @return Whether the specified capability is supported.
183 * @hide
184 */
185 public static boolean can(int capabilities, int capability) {
186 return (capabilities & capability) != 0;
187 }
188
189 /**
190 * Whether the capabilities of this {@code Connection} supports the specified capability.
191 *
192 * @param capability The capability to check capabilities for.
193 * @return Whether the specified capability is supported.
194 * @hide
195 */
196 public boolean can(int capability) {
197 return can(mConnectionCapabilities, capability);
198 }
199
200 /**
201 * Removes the specified capability from the set of capabilities of this {@code Conference}.
202 *
203 * @param capability The capability to remove from the set.
204 * @hide
205 */
206 public void removeCapability(int capability) {
Omkar Kolangadea0f46a92015-03-23 17:51:16 -0700207 int newCapabilities = mConnectionCapabilities;
208 newCapabilities &= ~capability;
209
210 setConnectionCapabilities(newCapabilities);
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800211 }
212
213 /**
214 * Adds the specified capability to the set of capabilities of this {@code Conference}.
215 *
216 * @param capability The capability to add to the set.
217 * @hide
218 */
219 public void addCapability(int capability) {
Omkar Kolangadea0f46a92015-03-23 17:51:16 -0700220 int newCapabilities = mConnectionCapabilities;
221 newCapabilities |= capability;
222
223 setConnectionCapabilities(newCapabilities);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700224 }
225
226 /**
Yorke Leea0d3ca92014-09-15 19:18:13 -0700227 * @return The audio state of the conference, describing how its audio is currently
228 * being routed by the system. This is {@code null} if this Conference
229 * does not directly know about its audio state.
Yorke Lee4af59352015-05-13 14:14:54 -0700230 * @deprecated Use {@link #getCallAudioState()} instead.
231 * @hide
Yorke Leea0d3ca92014-09-15 19:18:13 -0700232 */
Yorke Lee4af59352015-05-13 14:14:54 -0700233 @Deprecated
234 @SystemApi
Yorke Leea0d3ca92014-09-15 19:18:13 -0700235 public final AudioState getAudioState() {
Yorke Lee4af59352015-05-13 14:14:54 -0700236 return new AudioState(mCallAudioState);
237 }
238
239 /**
240 * @return The audio state of the conference, describing how its audio is currently
241 * being routed by the system. This is {@code null} if this Conference
242 * does not directly know about its audio state.
243 */
244 public final CallAudioState getCallAudioState() {
245 return mCallAudioState;
Yorke Leea0d3ca92014-09-15 19:18:13 -0700246 }
247
248 /**
Rekha Kumar07366812015-03-24 16:42:31 -0700249 * Returns VideoProvider of the primary call. This can be null.
Rekha Kumar07366812015-03-24 16:42:31 -0700250 */
251 public VideoProvider getVideoProvider() {
252 return null;
253 }
254
255 /**
256 * Returns video state of the primary call.
Rekha Kumar07366812015-03-24 16:42:31 -0700257 */
258 public int getVideoState() {
Tyler Gunn87b73f32015-06-03 10:09:59 -0700259 return VideoProfile.STATE_AUDIO_ONLY;
Rekha Kumar07366812015-03-24 16:42:31 -0700260 }
261
262 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700263 * Notifies the {@link Conference} when the Conference and all it's {@link Connection}s should
264 * be disconnected.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700265 */
266 public void onDisconnect() {}
267
268 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700269 * Notifies the {@link Conference} when the specified {@link Connection} should be separated
270 * from the conference call.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700271 *
272 * @param connection The connection to separate.
273 */
274 public void onSeparate(Connection connection) {}
275
276 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700277 * Notifies the {@link Conference} when the specified {@link Connection} should merged with the
278 * conference call.
Ihab Awad50e35062014-09-30 09:17:03 -0700279 *
280 * @param connection The {@code Connection} to merge.
281 */
282 public void onMerge(Connection connection) {}
283
284 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700285 * Notifies the {@link Conference} when it should be put on hold.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700286 */
287 public void onHold() {}
288
289 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700290 * Notifies the {@link Conference} when it should be moved from a held to active state.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700291 */
292 public void onUnhold() {}
293
294 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700295 * Notifies the {@link Conference} when the child calls should be merged. Only invoked if the
296 * conference contains the capability {@link Connection#CAPABILITY_MERGE_CONFERENCE}.
Santos Cordona4868042014-09-04 17:39:22 -0700297 */
298 public void onMerge() {}
299
300 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700301 * Notifies the {@link Conference} when the child calls should be swapped. Only invoked if the
302 * conference contains the capability {@link Connection#CAPABILITY_SWAP_CONFERENCE}.
Santos Cordona4868042014-09-04 17:39:22 -0700303 */
304 public void onSwap() {}
305
306 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700307 * Notifies the {@link Conference} of a request to play a DTMF tone.
Yorke Leea0d3ca92014-09-15 19:18:13 -0700308 *
309 * @param c A DTMF character.
310 */
311 public void onPlayDtmfTone(char c) {}
312
313 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700314 * Notifies the {@link Conference} of a request to stop any currently playing DTMF tones.
Yorke Leea0d3ca92014-09-15 19:18:13 -0700315 */
316 public void onStopDtmfTone() {}
317
318 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700319 * Notifies the {@link Conference} that the {@link #getAudioState()} property has a new value.
Yorke Leea0d3ca92014-09-15 19:18:13 -0700320 *
321 * @param state The new call audio state.
Yorke Lee4af59352015-05-13 14:14:54 -0700322 * @deprecated Use {@link #onCallAudioStateChanged(CallAudioState)} instead.
323 * @hide
Yorke Leea0d3ca92014-09-15 19:18:13 -0700324 */
Yorke Lee4af59352015-05-13 14:14:54 -0700325 @SystemApi
326 @Deprecated
Yorke Leea0d3ca92014-09-15 19:18:13 -0700327 public void onAudioStateChanged(AudioState state) {}
328
329 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700330 * Notifies the {@link Conference} that the {@link #getCallAudioState()} property has a new
331 * value.
Yorke Lee4af59352015-05-13 14:14:54 -0700332 *
333 * @param state The new call audio state.
334 */
335 public void onCallAudioStateChanged(CallAudioState state) {}
336
337 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700338 * Notifies the {@link Conference} that a {@link Connection} has been added to it.
Andrew Lee46f7f5d2014-11-06 17:00:25 -0800339 *
340 * @param connection The newly added connection.
341 */
342 public void onConnectionAdded(Connection connection) {}
343
344 /**
Santos Cordon823fd3c2014-08-07 18:35:18 -0700345 * Sets state to be on hold.
346 */
347 public final void setOnHold() {
348 setState(Connection.STATE_HOLDING);
349 }
350
351 /**
Tyler Gunnd46595a2015-06-01 14:29:11 -0700352 * Sets state to be dialing.
353 */
354 public final void setDialing() {
355 setState(Connection.STATE_DIALING);
356 }
357
358 /**
Santos Cordon823fd3c2014-08-07 18:35:18 -0700359 * Sets state to be active.
360 */
361 public final void setActive() {
362 setState(Connection.STATE_ACTIVE);
363 }
364
365 /**
366 * Sets state to disconnected.
367 *
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700368 * @param disconnectCause The reason for the disconnection, as described by
369 * {@link android.telecom.DisconnectCause}.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700370 */
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700371 public final void setDisconnected(DisconnectCause disconnectCause) {
372 mDisconnectCause = disconnectCause;;
Santos Cordon823fd3c2014-08-07 18:35:18 -0700373 setState(Connection.STATE_DISCONNECTED);
374 for (Listener l : mListeners) {
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700375 l.onDisconnected(this, mDisconnectCause);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700376 }
377 }
378
379 /**
mike dooley1cf14ac2014-11-04 10:59:53 -0800380 * @return The {@link DisconnectCause} for this connection.
381 */
382 public final DisconnectCause getDisconnectCause() {
383 return mDisconnectCause;
384 }
385
386 /**
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800387 * Sets the capabilities of a conference. See {@code CAPABILITY_*} constants of class
388 * {@link Connection} for valid values.
Nancy Chen56fc25d2014-09-09 12:24:51 -0700389 *
Tyler Gunn720c6642016-03-22 09:02:47 -0700390 * @param connectionCapabilities A bitmask of the {@code Capabilities} of the conference call.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700391 */
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800392 public final void setConnectionCapabilities(int connectionCapabilities) {
393 if (connectionCapabilities != mConnectionCapabilities) {
394 mConnectionCapabilities = connectionCapabilities;
Santos Cordon823fd3c2014-08-07 18:35:18 -0700395
396 for (Listener l : mListeners) {
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800397 l.onConnectionCapabilitiesChanged(this, mConnectionCapabilities);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700398 }
399 }
400 }
401
402 /**
Tyler Gunn720c6642016-03-22 09:02:47 -0700403 * Sets the properties of a conference. See {@code PROPERTY_*} constants of class
404 * {@link Connection} for valid values.
405 *
406 * @param connectionProperties A bitmask of the {@code Properties} of the conference call.
407 */
408 public final void setConnectionProperties(int connectionProperties) {
409 if (connectionProperties != mConnectionProperties) {
410 mConnectionProperties = connectionProperties;
411
412 for (Listener l : mListeners) {
413 l.onConnectionPropertiesChanged(this, mConnectionProperties);
414 }
415 }
416 }
417
418 /**
Santos Cordon823fd3c2014-08-07 18:35:18 -0700419 * Adds the specified connection as a child of this conference.
420 *
421 * @param connection The connection to add.
422 * @return True if the connection was successfully added.
423 */
Santos Cordona4868042014-09-04 17:39:22 -0700424 public final boolean addConnection(Connection connection) {
Rekha Kumar07366812015-03-24 16:42:31 -0700425 Log.d(this, "Connection=%s, connection=", connection);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700426 if (connection != null && !mChildConnections.contains(connection)) {
427 if (connection.setConference(this)) {
428 mChildConnections.add(connection);
Andrew Lee46f7f5d2014-11-06 17:00:25 -0800429 onConnectionAdded(connection);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700430 for (Listener l : mListeners) {
431 l.onConnectionAdded(this, connection);
432 }
433 return true;
434 }
435 }
436 return false;
437 }
438
439 /**
440 * Removes the specified connection as a child of this conference.
441 *
442 * @param connection The connection to remove.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700443 */
Santos Cordona4868042014-09-04 17:39:22 -0700444 public final void removeConnection(Connection connection) {
Santos Cordon0159ac02014-08-21 14:28:11 -0700445 Log.d(this, "removing %s from %s", connection, mChildConnections);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700446 if (connection != null && mChildConnections.remove(connection)) {
447 connection.resetConference();
448 for (Listener l : mListeners) {
449 l.onConnectionRemoved(this, connection);
450 }
451 }
452 }
453
454 /**
Ihab Awad50e35062014-09-30 09:17:03 -0700455 * Sets the connections with which this connection can be conferenced.
456 *
457 * @param conferenceableConnections The set of connections this connection can conference with.
458 */
459 public final void setConferenceableConnections(List<Connection> conferenceableConnections) {
460 clearConferenceableList();
461 for (Connection c : conferenceableConnections) {
462 // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a
463 // small amount of items here.
464 if (!mConferenceableConnections.contains(c)) {
465 c.addConnectionListener(mConnectionDeathListener);
466 mConferenceableConnections.add(c);
467 }
468 }
469 fireOnConferenceableConnectionsChanged();
470 }
471
Rekha Kumar07366812015-03-24 16:42:31 -0700472 /**
473 * Set the video state for the conference.
Yorke Lee32f24732015-05-12 16:18:03 -0700474 * Valid values: {@link VideoProfile#STATE_AUDIO_ONLY},
475 * {@link VideoProfile#STATE_BIDIRECTIONAL},
476 * {@link VideoProfile#STATE_TX_ENABLED},
477 * {@link VideoProfile#STATE_RX_ENABLED}.
Rekha Kumar07366812015-03-24 16:42:31 -0700478 *
479 * @param videoState The new video state.
Rekha Kumar07366812015-03-24 16:42:31 -0700480 */
481 public final void setVideoState(Connection c, int videoState) {
482 Log.d(this, "setVideoState Conference: %s Connection: %s VideoState: %s",
483 this, c, videoState);
484 for (Listener l : mListeners) {
485 l.onVideoStateChanged(this, videoState);
486 }
487 }
488
489 /**
490 * Sets the video connection provider.
491 *
492 * @param videoProvider The video provider.
Rekha Kumar07366812015-03-24 16:42:31 -0700493 */
494 public final void setVideoProvider(Connection c, Connection.VideoProvider videoProvider) {
495 Log.d(this, "setVideoProvider Conference: %s Connection: %s VideoState: %s",
496 this, c, videoProvider);
497 for (Listener l : mListeners) {
498 l.onVideoProviderChanged(this, videoProvider);
499 }
500 }
501
Ihab Awad50e35062014-09-30 09:17:03 -0700502 private final void fireOnConferenceableConnectionsChanged() {
503 for (Listener l : mListeners) {
504 l.onConferenceableConnectionsChanged(this, getConferenceableConnections());
505 }
506 }
507
508 /**
509 * Returns the connections with which this connection can be conferenced.
510 */
511 public final List<Connection> getConferenceableConnections() {
512 return mUnmodifiableConferenceableConnections;
513 }
514
515 /**
Nancy Chenea38cca2014-09-05 16:38:49 -0700516 * Tears down the conference object and any of its current connections.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700517 */
Santos Cordona4868042014-09-04 17:39:22 -0700518 public final void destroy() {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700519 Log.d(this, "destroying conference : %s", this);
520 // Tear down the children.
Santos Cordon0159ac02014-08-21 14:28:11 -0700521 for (Connection connection : mChildConnections) {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700522 Log.d(this, "removing connection %s", connection);
523 removeConnection(connection);
524 }
525
526 // If not yet disconnected, set the conference call as disconnected first.
527 if (mState != Connection.STATE_DISCONNECTED) {
528 Log.d(this, "setting to disconnected");
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700529 setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
Santos Cordon823fd3c2014-08-07 18:35:18 -0700530 }
531
532 // ...and notify.
533 for (Listener l : mListeners) {
534 l.onDestroyed(this);
535 }
536 }
537
538 /**
539 * Add a listener to be notified of a state change.
540 *
541 * @param listener The new listener.
542 * @return This conference.
543 * @hide
544 */
545 public final Conference addListener(Listener listener) {
546 mListeners.add(listener);
547 return this;
548 }
549
550 /**
551 * Removes the specified listener.
552 *
553 * @param listener The listener to remove.
554 * @return This conference.
555 * @hide
556 */
557 public final Conference removeListener(Listener listener) {
558 mListeners.remove(listener);
559 return this;
560 }
561
Yorke Leea0d3ca92014-09-15 19:18:13 -0700562 /**
Tyler Gunn4a57b9b2014-10-30 14:27:48 -0700563 * Retrieves the primary connection associated with the conference. The primary connection is
564 * the connection from which the conference will retrieve its current state.
565 *
566 * @return The primary connection.
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700567 * @hide
Tyler Gunn4a57b9b2014-10-30 14:27:48 -0700568 */
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700569 @SystemApi
Santos Cordon4055d642015-05-12 14:19:24 -0700570 public Connection getPrimaryConnection() {
Tyler Gunn4a57b9b2014-10-30 14:27:48 -0700571 if (mUnmodifiableChildConnections == null || mUnmodifiableChildConnections.isEmpty()) {
572 return null;
573 }
574 return mUnmodifiableChildConnections.get(0);
575 }
576
577 /**
Wei Huang7f7f72e2018-05-30 19:21:36 +0800578 * Updates RIL voice radio technology used for current conference after its creation.
579 *
580 * @hide
581 */
582 public void updateCallRadioTechAfterCreation() {
583 final Connection primaryConnection = getPrimaryConnection();
584 if (primaryConnection != null) {
585 setCallRadioTech(primaryConnection.getCallRadioTech());
586 } else {
587 Log.w(this, "No primary connection found while updateCallRadioTechAfterCreation");
588 }
589 }
590
591 /**
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700592 * @hide
593 * @deprecated Use {@link #setConnectionTime}.
Tyler Gunncd5d33c2015-01-12 09:02:01 -0800594 */
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700595 @Deprecated
596 @SystemApi
597 public final void setConnectTimeMillis(long connectTimeMillis) {
598 setConnectionTime(connectTimeMillis);
Tyler Gunncd5d33c2015-01-12 09:02:01 -0800599 }
600
601 /**
Tyler Gunn17541392018-02-01 08:58:38 -0800602 * Sets the connection start time of the {@code Conference}. This is used in the call log to
603 * indicate the date and time when the conference took place.
604 * <p>
605 * Should be specified in wall-clock time returned by {@link System#currentTimeMillis()}.
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700606 * <p>
607 * When setting the connection time, you should always set the connection elapsed time via
Tyler Gunn17541392018-02-01 08:58:38 -0800608 * {@link #setConnectionStartElapsedRealTime(long)} to ensure the duration is reflected.
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700609 *
Tyler Gunn17541392018-02-01 08:58:38 -0800610 * @param connectionTimeMillis The connection time, in milliseconds, as returned by
611 * {@link System#currentTimeMillis()}.
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700612 */
613 public final void setConnectionTime(long connectionTimeMillis) {
614 mConnectTimeMillis = connectionTimeMillis;
615 }
616
617 /**
Tyler Gunn17541392018-02-01 08:58:38 -0800618 * Sets the start time of the {@link Conference} which is the basis for the determining the
619 * duration of the {@link Conference}.
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700620 * <p>
Tyler Gunn17541392018-02-01 08:58:38 -0800621 * You should use a value returned by {@link SystemClock#elapsedRealtime()} to ensure that time
622 * zone changes do not impact the conference duration.
623 * <p>
624 * When setting this, you should also set the connection time via
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700625 * {@link #setConnectionTime(long)}.
626 *
Tyler Gunn17541392018-02-01 08:58:38 -0800627 * @param connectionStartElapsedRealTime The connection time, as measured by
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700628 * {@link SystemClock#elapsedRealtime()}.
629 */
Tyler Gunn17541392018-02-01 08:58:38 -0800630 public final void setConnectionStartElapsedRealTime(long connectionStartElapsedRealTime) {
631 mConnectionStartElapsedRealTime = connectionStartElapsedRealTime;
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700632 }
633
634 /**
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700635 * @hide
636 * @deprecated Use {@link #getConnectionTime}.
637 */
638 @Deprecated
639 @SystemApi
640 public final long getConnectTimeMillis() {
641 return getConnectionTime();
642 }
643
644 /**
645 * Retrieves the connection start time of the {@code Conference}, if specified. A value of
Tyler Gunncd5d33c2015-01-12 09:02:01 -0800646 * {@link #CONNECT_TIME_NOT_SPECIFIED} indicates that Telecom should determine the start time
647 * of the conference.
648 *
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700649 * @return The time at which the {@code Conference} was connected.
Tyler Gunncd5d33c2015-01-12 09:02:01 -0800650 */
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700651 public final long getConnectionTime() {
Tyler Gunncd5d33c2015-01-12 09:02:01 -0800652 return mConnectTimeMillis;
653 }
654
655 /**
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700656 * Retrieves the connection start time of the {@link Conference}, if specified. A value of
657 * {@link #CONNECT_TIME_NOT_SPECIFIED} indicates that Telecom should determine the start time
658 * of the conference.
659 *
660 * This is based on the value of {@link SystemClock#elapsedRealtime()} to ensure that it is not
661 * impacted by wall clock changes (user initiated, network initiated, time zone change, etc).
662 *
663 * @return The elapsed time at which the {@link Conference} was connected.
664 * @hide
665 */
Tyler Gunn17541392018-02-01 08:58:38 -0800666 public final long getConnectionStartElapsedRealTime() {
667 return mConnectionStartElapsedRealTime;
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700668 }
669
670 /**
Wei Huang7f7f72e2018-05-30 19:21:36 +0800671 * Sets RIL voice radio technology used for current conference.
672 *
673 * @param vrat the RIL voice radio technology used for current conference,
674 * see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}.
675 *
676 * @hide
677 */
678 public final void setCallRadioTech(@ServiceState.RilRadioTechnology int vrat) {
679 putExtra(TelecomManager.EXTRA_CALL_NETWORK_TYPE,
680 ServiceState.rilRadioTechnologyToNetworkType(vrat));
681 }
682
683 /**
684 * Returns RIL voice radio technology used for current conference.
685 *
686 * @return the RIL voice radio technology used for current conference,
687 * see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}.
688 *
689 * @hide
690 */
691 public final @ServiceState.RilRadioTechnology int getCallRadioTech() {
692 int voiceNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
693 Bundle extras = getExtras();
694 if (extras != null) {
695 voiceNetworkType = extras.getInt(TelecomManager.EXTRA_CALL_NETWORK_TYPE,
696 TelephonyManager.NETWORK_TYPE_UNKNOWN);
697 }
698 return ServiceState.networkTypeToRilRadioTechnology(voiceNetworkType);
699 }
700
701 /**
Yorke Leea0d3ca92014-09-15 19:18:13 -0700702 * Inform this Conference that the state of its audio output has been changed externally.
703 *
704 * @param state The new audio state.
705 * @hide
706 */
Yorke Lee4af59352015-05-13 14:14:54 -0700707 final void setCallAudioState(CallAudioState state) {
708 Log.d(this, "setCallAudioState %s", state);
709 mCallAudioState = state;
710 onAudioStateChanged(getAudioState());
711 onCallAudioStateChanged(state);
Yorke Leea0d3ca92014-09-15 19:18:13 -0700712 }
713
Santos Cordon823fd3c2014-08-07 18:35:18 -0700714 private void setState(int newState) {
715 if (newState != Connection.STATE_ACTIVE &&
716 newState != Connection.STATE_HOLDING &&
717 newState != Connection.STATE_DISCONNECTED) {
718 Log.w(this, "Unsupported state transition for Conference call.",
719 Connection.stateToString(newState));
720 return;
721 }
722
723 if (mState != newState) {
724 int oldState = mState;
725 mState = newState;
726 for (Listener l : mListeners) {
727 l.onStateChanged(this, oldState, newState);
728 }
729 }
730 }
Ihab Awad50e35062014-09-30 09:17:03 -0700731
732 private final void clearConferenceableList() {
733 for (Connection c : mConferenceableConnections) {
734 c.removeConnectionListener(mConnectionDeathListener);
735 }
736 mConferenceableConnections.clear();
737 }
Rekha Kumar07366812015-03-24 16:42:31 -0700738
739 @Override
740 public String toString() {
741 return String.format(Locale.US,
742 "[State: %s,Capabilites: %s, VideoState: %s, VideoProvider: %s, ThisObject %s]",
743 Connection.stateToString(mState),
744 Call.Details.capabilitiesToString(mConnectionCapabilities),
745 getVideoState(),
746 getVideoProvider(),
747 super.toString());
748 }
Andrew Lee0f51da32015-04-16 13:11:55 -0700749
Andrew Leeedc625f2015-04-14 13:38:12 -0700750 /**
751 * Sets the label and icon status to display in the InCall UI.
752 *
753 * @param statusHints The status label and icon to set.
754 */
755 public final void setStatusHints(StatusHints statusHints) {
756 mStatusHints = statusHints;
757 for (Listener l : mListeners) {
758 l.onStatusHintsChanged(this, statusHints);
759 }
760 }
761
762 /**
763 * @return The status hints for this conference.
764 */
765 public final StatusHints getStatusHints() {
766 return mStatusHints;
767 }
Santos Cordon6b7f9552015-05-27 17:21:45 -0700768
769 /**
Tyler Gunndee56a82016-03-23 16:06:34 -0700770 * Replaces all the extras associated with this {@code Conference}.
771 * <p>
772 * New or existing keys are replaced in the {@code Conference} extras. Keys which are no longer
773 * in the new extras, but were present the last time {@code setExtras} was called are removed.
774 * <p>
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700775 * Alternatively you may use the {@link #putExtras(Bundle)}, and
776 * {@link #removeExtras(String...)} methods to modify the extras.
777 * <p>
Tyler Gunndee56a82016-03-23 16:06:34 -0700778 * No assumptions should be made as to how an In-Call UI or service will handle these extras.
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700779 * Keys should be fully qualified (e.g., com.example.extras.MY_EXTRA) to avoid conflicts.
Santos Cordon6b7f9552015-05-27 17:21:45 -0700780 *
Tyler Gunndee56a82016-03-23 16:06:34 -0700781 * @param extras The extras associated with this {@code Conference}.
Santos Cordon6b7f9552015-05-27 17:21:45 -0700782 */
783 public final void setExtras(@Nullable Bundle extras) {
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700784 // Keeping putExtras and removeExtras in the same lock so that this operation happens as a
785 // block instead of letting other threads put/remove while this method is running.
786 synchronized (mExtrasLock) {
787 // Add/replace any new or changed extras values.
788 putExtras(extras);
789 // If we have used "setExtras" in the past, compare the key set from the last invocation
790 // to the current one and remove any keys that went away.
791 if (mPreviousExtraKeys != null) {
792 List<String> toRemove = new ArrayList<String>();
793 for (String oldKey : mPreviousExtraKeys) {
794 if (extras == null || !extras.containsKey(oldKey)) {
795 toRemove.add(oldKey);
796 }
797 }
Tyler Gunndee56a82016-03-23 16:06:34 -0700798
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700799 if (!toRemove.isEmpty()) {
800 removeExtras(toRemove);
Tyler Gunndee56a82016-03-23 16:06:34 -0700801 }
802 }
803
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700804 // Track the keys the last time set called setExtras. This way, the next time setExtras
805 // is called we can see if the caller has removed any extras values.
806 if (mPreviousExtraKeys == null) {
807 mPreviousExtraKeys = new ArraySet<String>();
Tyler Gunndee56a82016-03-23 16:06:34 -0700808 }
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700809 mPreviousExtraKeys.clear();
810 if (extras != null) {
811 mPreviousExtraKeys.addAll(extras.keySet());
812 }
Tyler Gunna8fb8ab2016-03-29 10:24:22 -0700813 }
Tyler Gunndee56a82016-03-23 16:06:34 -0700814 }
815
816 /**
817 * Adds some extras to this {@link Conference}. Existing keys are replaced and new ones are
818 * added.
819 * <p>
820 * No assumptions should be made as to how an In-Call UI or service will handle these extras.
821 * Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
822 *
823 * @param extras The extras to add.
824 */
825 public final void putExtras(@NonNull Bundle extras) {
826 if (extras == null) {
827 return;
828 }
829
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700830 // Creating a Bundle clone so we don't have to synchronize on mExtrasLock while calling
831 // onExtrasChanged.
832 Bundle listenersBundle;
833 synchronized (mExtrasLock) {
834 if (mExtras == null) {
835 mExtras = new Bundle();
836 }
837 mExtras.putAll(extras);
838 listenersBundle = new Bundle(mExtras);
Tyler Gunndee56a82016-03-23 16:06:34 -0700839 }
Tyler Gunndee56a82016-03-23 16:06:34 -0700840
Santos Cordon6b7f9552015-05-27 17:21:45 -0700841 for (Listener l : mListeners) {
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700842 l.onExtrasChanged(this, new Bundle(listenersBundle));
Santos Cordon6b7f9552015-05-27 17:21:45 -0700843 }
844 }
845
846 /**
Tyler Gunndee56a82016-03-23 16:06:34 -0700847 * Adds a boolean extra to this {@link Conference}.
848 *
849 * @param key The extra key.
850 * @param value The value.
851 * @hide
852 */
853 public final void putExtra(String key, boolean value) {
854 Bundle newExtras = new Bundle();
855 newExtras.putBoolean(key, value);
856 putExtras(newExtras);
857 }
858
859 /**
860 * Adds an integer extra to this {@link Conference}.
861 *
862 * @param key The extra key.
863 * @param value The value.
864 * @hide
865 */
866 public final void putExtra(String key, int value) {
867 Bundle newExtras = new Bundle();
868 newExtras.putInt(key, value);
869 putExtras(newExtras);
870 }
871
872 /**
873 * Adds a string extra to this {@link Conference}.
874 *
875 * @param key The extra key.
876 * @param value The value.
877 * @hide
878 */
879 public final void putExtra(String key, String value) {
880 Bundle newExtras = new Bundle();
881 newExtras.putString(key, value);
882 putExtras(newExtras);
883 }
884
885 /**
Tyler Gunn071be6f2016-05-10 14:52:33 -0700886 * Removes extras from this {@link Conference}.
Tyler Gunndee56a82016-03-23 16:06:34 -0700887 *
Tyler Gunn071be6f2016-05-10 14:52:33 -0700888 * @param keys The keys of the extras to remove.
Tyler Gunndee56a82016-03-23 16:06:34 -0700889 */
890 public final void removeExtras(List<String> keys) {
891 if (keys == null || keys.isEmpty()) {
892 return;
893 }
894
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700895 synchronized (mExtrasLock) {
896 if (mExtras != null) {
897 for (String key : keys) {
898 mExtras.remove(key);
899 }
Tyler Gunndee56a82016-03-23 16:06:34 -0700900 }
901 }
902
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700903 List<String> unmodifiableKeys = Collections.unmodifiableList(keys);
Tyler Gunndee56a82016-03-23 16:06:34 -0700904 for (Listener l : mListeners) {
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700905 l.onExtrasRemoved(this, unmodifiableKeys);
Tyler Gunndee56a82016-03-23 16:06:34 -0700906 }
907 }
908
909 /**
Tyler Gunn071be6f2016-05-10 14:52:33 -0700910 * Removes extras from this {@link Conference}.
911 *
912 * @param keys The keys of the extras to remove.
913 */
914 public final void removeExtras(String ... keys) {
915 removeExtras(Arrays.asList(keys));
916 }
917
918 /**
Tyler Gunndee56a82016-03-23 16:06:34 -0700919 * Returns the extras associated with this conference.
Tyler Gunn2cbe2b52016-05-04 15:48:10 +0000920 * <p>
921 * Extras should be updated using {@link #putExtras(Bundle)} and {@link #removeExtras(List)}.
922 * <p>
923 * Telecom or an {@link InCallService} can also update the extras via
924 * {@link android.telecom.Call#putExtras(Bundle)}, and
925 * {@link Call#removeExtras(List)}.
926 * <p>
927 * The conference is notified of changes to the extras made by Telecom or an
928 * {@link InCallService} by {@link #onExtrasChanged(Bundle)}.
Tyler Gunndee56a82016-03-23 16:06:34 -0700929 *
930 * @return The extras associated with this connection.
Santos Cordon6b7f9552015-05-27 17:21:45 -0700931 */
932 public final Bundle getExtras() {
933 return mExtras;
934 }
Tyler Gunndee56a82016-03-23 16:06:34 -0700935
936 /**
937 * Notifies this {@link Conference} of a change to the extras made outside the
938 * {@link ConnectionService}.
939 * <p>
940 * These extras changes can originate from Telecom itself, or from an {@link InCallService} via
941 * {@link android.telecom.Call#putExtras(Bundle)}, and
942 * {@link Call#removeExtras(List)}.
943 *
944 * @param extras The new extras bundle.
945 */
946 public void onExtrasChanged(Bundle extras) {}
947
948 /**
949 * Handles a change to extras received from Telecom.
950 *
951 * @param extras The new extras.
952 * @hide
953 */
954 final void handleExtrasChanged(Bundle extras) {
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700955 Bundle b = null;
956 synchronized (mExtrasLock) {
957 mExtras = extras;
958 if (mExtras != null) {
959 b = new Bundle(mExtras);
960 }
961 }
962 onExtrasChanged(b);
Tyler Gunndee56a82016-03-23 16:06:34 -0700963 }
Santos Cordon823fd3c2014-08-07 18:35:18 -0700964}