blob: 177759e6751f747d2880398ce8614837fba04fb9 [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;
Rekha Kumar07366812015-03-24 16:42:31 -070023import android.telecom.Connection.VideoProvider;
Tyler Gunndee56a82016-03-23 16:06:34 -070024import android.util.ArraySet;
Evan Charlton0e094d92014-11-08 15:49:16 -080025
Ihab Awad50e35062014-09-30 09:17:03 -070026import java.util.ArrayList;
Tyler Gunn071be6f2016-05-10 14:52:33 -070027import java.util.Arrays;
Santos Cordon823fd3c2014-08-07 18:35:18 -070028import java.util.Collections;
Santos Cordon823fd3c2014-08-07 18:35:18 -070029import java.util.List;
Rekha Kumar07366812015-03-24 16:42:31 -070030import java.util.Locale;
Tyler Gunnec5b6e32016-12-01 19:40:30 -080031import java.util.Objects;
Santos Cordon823fd3c2014-08-07 18:35:18 -070032import java.util.Set;
33import java.util.concurrent.CopyOnWriteArrayList;
34import java.util.concurrent.CopyOnWriteArraySet;
35
36/**
37 * Represents a conference call which can contain any number of {@link Connection} objects.
38 */
Yorke Leeabfcfdc2015-05-13 18:55:18 -070039public abstract class Conference extends Conferenceable {
Santos Cordon823fd3c2014-08-07 18:35:18 -070040
Tyler Gunncd5d33c2015-01-12 09:02:01 -080041 /**
42 * Used to indicate that the conference connection time is not specified. If not specified,
43 * Telecom will set the connect time.
44 */
Jay Shrauner164a0ac2015-04-14 18:16:10 -070045 public static final long CONNECT_TIME_NOT_SPECIFIED = 0;
Tyler Gunncd5d33c2015-01-12 09:02:01 -080046
Santos Cordon823fd3c2014-08-07 18:35:18 -070047 /** @hide */
48 public abstract static class Listener {
49 public void onStateChanged(Conference conference, int oldState, int newState) {}
Andrew Lee7f3d41f2014-09-11 17:33:16 -070050 public void onDisconnected(Conference conference, DisconnectCause disconnectCause) {}
Santos Cordon823fd3c2014-08-07 18:35:18 -070051 public void onConnectionAdded(Conference conference, Connection connection) {}
52 public void onConnectionRemoved(Conference conference, Connection connection) {}
Ihab Awad50e35062014-09-30 09:17:03 -070053 public void onConferenceableConnectionsChanged(
54 Conference conference, List<Connection> conferenceableConnections) {}
Santos Cordon823fd3c2014-08-07 18:35:18 -070055 public void onDestroyed(Conference conference) {}
Ihab Awad5c9c86e2014-11-12 13:41:16 -080056 public void onConnectionCapabilitiesChanged(
57 Conference conference, int connectionCapabilities) {}
Tyler Gunn720c6642016-03-22 09:02:47 -070058 public void onConnectionPropertiesChanged(
59 Conference conference, int connectionProperties) {}
Rekha Kumar07366812015-03-24 16:42:31 -070060 public void onVideoStateChanged(Conference c, int videoState) { }
61 public void onVideoProviderChanged(Conference c, Connection.VideoProvider videoProvider) {}
Andrew Leeedc625f2015-04-14 13:38:12 -070062 public void onStatusHintsChanged(Conference conference, StatusHints statusHints) {}
Tyler Gunndee56a82016-03-23 16:06:34 -070063 public void onExtrasChanged(Conference c, Bundle extras) {}
64 public void onExtrasRemoved(Conference c, List<String> keys) {}
Santos Cordon823fd3c2014-08-07 18:35:18 -070065 }
66
67 private final Set<Listener> mListeners = new CopyOnWriteArraySet<>();
68 private final List<Connection> mChildConnections = new CopyOnWriteArrayList<>();
Ihab Awadb8e85c72014-08-23 20:34:57 -070069 private final List<Connection> mUnmodifiableChildConnections =
Santos Cordon823fd3c2014-08-07 18:35:18 -070070 Collections.unmodifiableList(mChildConnections);
Ihab Awad50e35062014-09-30 09:17:03 -070071 private final List<Connection> mConferenceableConnections = new ArrayList<>();
72 private final List<Connection> mUnmodifiableConferenceableConnections =
73 Collections.unmodifiableList(mConferenceableConnections);
Santos Cordon823fd3c2014-08-07 18:35:18 -070074
Jack Yu67140302015-12-10 12:27:58 -080075 private String mTelecomCallId;
Jay Shrauner164a0ac2015-04-14 18:16:10 -070076 private PhoneAccountHandle mPhoneAccount;
Yorke Lee4af59352015-05-13 14:14:54 -070077 private CallAudioState mCallAudioState;
Santos Cordon823fd3c2014-08-07 18:35:18 -070078 private int mState = Connection.STATE_NEW;
Andrew Lee7f3d41f2014-09-11 17:33:16 -070079 private DisconnectCause mDisconnectCause;
Ihab Awad5c9c86e2014-11-12 13:41:16 -080080 private int mConnectionCapabilities;
Tyler Gunn720c6642016-03-22 09:02:47 -070081 private int mConnectionProperties;
Santos Cordon823fd3c2014-08-07 18:35:18 -070082 private String mDisconnectMessage;
Tyler Gunncd5d33c2015-01-12 09:02:01 -080083 private long mConnectTimeMillis = CONNECT_TIME_NOT_SPECIFIED;
Andrew Leeedc625f2015-04-14 13:38:12 -070084 private StatusHints mStatusHints;
Santos Cordon6b7f9552015-05-27 17:21:45 -070085 private Bundle mExtras;
Tyler Gunndee56a82016-03-23 16:06:34 -070086 private Set<String> mPreviousExtraKeys;
Brad Ebinger4fa6a012016-06-14 17:04:01 -070087 private final Object mExtrasLock = new Object();
Santos Cordon823fd3c2014-08-07 18:35:18 -070088
Ihab Awad50e35062014-09-30 09:17:03 -070089 private final Connection.Listener mConnectionDeathListener = new Connection.Listener() {
90 @Override
91 public void onDestroyed(Connection c) {
92 if (mConferenceableConnections.remove(c)) {
93 fireOnConferenceableConnectionsChanged();
94 }
95 }
96 };
97
Nancy Chen56fc25d2014-09-09 12:24:51 -070098 /**
99 * Constructs a new Conference with a mandatory {@link PhoneAccountHandle}
100 *
101 * @param phoneAccount The {@code PhoneAccountHandle} associated with the conference.
102 */
Santos Cordon823fd3c2014-08-07 18:35:18 -0700103 public Conference(PhoneAccountHandle phoneAccount) {
104 mPhoneAccount = phoneAccount;
105 }
106
Nancy Chen56fc25d2014-09-09 12:24:51 -0700107 /**
Jack Yu67140302015-12-10 12:27:58 -0800108 * Returns the telecom internal call ID associated with this conference.
109 *
110 * @return The telecom call ID.
111 * @hide
112 */
113 public final String getTelecomCallId() {
114 return mTelecomCallId;
115 }
116
117 /**
118 * Sets the telecom internal call ID associated with this conference.
119 *
120 * @param telecomCallId The telecom call ID.
121 * @hide
122 */
123 public final void setTelecomCallId(String telecomCallId) {
124 mTelecomCallId = telecomCallId;
125 }
126
127 /**
Nancy Chen56fc25d2014-09-09 12:24:51 -0700128 * Returns the {@link PhoneAccountHandle} the conference call is being placed through.
129 *
130 * @return A {@code PhoneAccountHandle} object representing the PhoneAccount of the conference.
131 */
Nancy Chenea38cca2014-09-05 16:38:49 -0700132 public final PhoneAccountHandle getPhoneAccountHandle() {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700133 return mPhoneAccount;
134 }
135
Nancy Chen56fc25d2014-09-09 12:24:51 -0700136 /**
137 * Returns the list of connections currently associated with the conference call.
138 *
139 * @return A list of {@code Connection} objects which represent the children of the conference.
140 */
Santos Cordon823fd3c2014-08-07 18:35:18 -0700141 public final List<Connection> getConnections() {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700142 return mUnmodifiableChildConnections;
Santos Cordon823fd3c2014-08-07 18:35:18 -0700143 }
144
Nancy Chen56fc25d2014-09-09 12:24:51 -0700145 /**
146 * Gets the state of the conference call. See {@link Connection} for valid values.
147 *
148 * @return A constant representing the state the conference call is currently in.
149 */
Santos Cordon823fd3c2014-08-07 18:35:18 -0700150 public final int getState() {
151 return mState;
152 }
153
Nancy Chen56fc25d2014-09-09 12:24:51 -0700154 /**
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700155 * Returns the capabilities of the conference. See {@code CAPABILITY_*} constants in class
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800156 * {@link Connection} for valid values.
Nancy Chen56fc25d2014-09-09 12:24:51 -0700157 *
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800158 * @return A bitmask of the capabilities of the conference call.
Nancy Chen56fc25d2014-09-09 12:24:51 -0700159 */
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800160 public final int getConnectionCapabilities() {
161 return mConnectionCapabilities;
162 }
163
164 /**
Tyler Gunn720c6642016-03-22 09:02:47 -0700165 * Returns the properties of the conference. See {@code PROPERTY_*} constants in class
166 * {@link Connection} for valid values.
167 *
168 * @return A bitmask of the properties of the conference call.
169 */
170 public final int getConnectionProperties() {
171 return mConnectionProperties;
172 }
173
174 /**
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800175 * Whether the given capabilities support the specified capability.
176 *
177 * @param capabilities A capability bit field.
178 * @param capability The capability to check capabilities for.
179 * @return Whether the specified capability is supported.
180 * @hide
181 */
182 public static boolean can(int capabilities, int capability) {
183 return (capabilities & capability) != 0;
184 }
185
186 /**
187 * Whether the capabilities of this {@code Connection} supports the specified capability.
188 *
189 * @param capability The capability to check capabilities for.
190 * @return Whether the specified capability is supported.
191 * @hide
192 */
193 public boolean can(int capability) {
194 return can(mConnectionCapabilities, capability);
195 }
196
197 /**
198 * Removes the specified capability from the set of capabilities of this {@code Conference}.
199 *
200 * @param capability The capability to remove from the set.
201 * @hide
202 */
203 public void removeCapability(int capability) {
Omkar Kolangadea0f46a92015-03-23 17:51:16 -0700204 int newCapabilities = mConnectionCapabilities;
205 newCapabilities &= ~capability;
206
207 setConnectionCapabilities(newCapabilities);
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800208 }
209
210 /**
211 * Adds the specified capability to the set of capabilities of this {@code Conference}.
212 *
213 * @param capability The capability to add to the set.
214 * @hide
215 */
216 public void addCapability(int capability) {
Omkar Kolangadea0f46a92015-03-23 17:51:16 -0700217 int newCapabilities = mConnectionCapabilities;
218 newCapabilities |= capability;
219
220 setConnectionCapabilities(newCapabilities);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700221 }
222
223 /**
Yorke Leea0d3ca92014-09-15 19:18:13 -0700224 * @return The audio state of the conference, describing how its audio is currently
225 * being routed by the system. This is {@code null} if this Conference
226 * does not directly know about its audio state.
Yorke Lee4af59352015-05-13 14:14:54 -0700227 * @deprecated Use {@link #getCallAudioState()} instead.
228 * @hide
Yorke Leea0d3ca92014-09-15 19:18:13 -0700229 */
Yorke Lee4af59352015-05-13 14:14:54 -0700230 @Deprecated
231 @SystemApi
Yorke Leea0d3ca92014-09-15 19:18:13 -0700232 public final AudioState getAudioState() {
Yorke Lee4af59352015-05-13 14:14:54 -0700233 return new AudioState(mCallAudioState);
234 }
235
236 /**
237 * @return The audio state of the conference, describing how its audio is currently
238 * being routed by the system. This is {@code null} if this Conference
239 * does not directly know about its audio state.
240 */
241 public final CallAudioState getCallAudioState() {
242 return mCallAudioState;
Yorke Leea0d3ca92014-09-15 19:18:13 -0700243 }
244
245 /**
Rekha Kumar07366812015-03-24 16:42:31 -0700246 * Returns VideoProvider of the primary call. This can be null.
Rekha Kumar07366812015-03-24 16:42:31 -0700247 */
248 public VideoProvider getVideoProvider() {
249 return null;
250 }
251
252 /**
253 * Returns video state of the primary call.
Rekha Kumar07366812015-03-24 16:42:31 -0700254 */
255 public int getVideoState() {
Tyler Gunn87b73f32015-06-03 10:09:59 -0700256 return VideoProfile.STATE_AUDIO_ONLY;
Rekha Kumar07366812015-03-24 16:42:31 -0700257 }
258
259 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700260 * Notifies the {@link Conference} when the Conference and all it's {@link Connection}s should
261 * be disconnected.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700262 */
263 public void onDisconnect() {}
264
265 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700266 * Notifies the {@link Conference} when the specified {@link Connection} should be separated
267 * from the conference call.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700268 *
269 * @param connection The connection to separate.
270 */
271 public void onSeparate(Connection connection) {}
272
273 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700274 * Notifies the {@link Conference} when the specified {@link Connection} should merged with the
275 * conference call.
Ihab Awad50e35062014-09-30 09:17:03 -0700276 *
277 * @param connection The {@code Connection} to merge.
278 */
279 public void onMerge(Connection connection) {}
280
281 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700282 * Notifies the {@link Conference} when it should be put on hold.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700283 */
284 public void onHold() {}
285
286 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700287 * Notifies the {@link Conference} when it should be moved from a held to active state.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700288 */
289 public void onUnhold() {}
290
291 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700292 * Notifies the {@link Conference} when the child calls should be merged. Only invoked if the
293 * conference contains the capability {@link Connection#CAPABILITY_MERGE_CONFERENCE}.
Santos Cordona4868042014-09-04 17:39:22 -0700294 */
295 public void onMerge() {}
296
297 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700298 * Notifies the {@link Conference} when the child calls should be swapped. Only invoked if the
299 * conference contains the capability {@link Connection#CAPABILITY_SWAP_CONFERENCE}.
Santos Cordona4868042014-09-04 17:39:22 -0700300 */
301 public void onSwap() {}
302
303 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700304 * Notifies the {@link Conference} of a request to play a DTMF tone.
Yorke Leea0d3ca92014-09-15 19:18:13 -0700305 *
306 * @param c A DTMF character.
307 */
308 public void onPlayDtmfTone(char c) {}
309
310 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700311 * Notifies the {@link Conference} of a request to stop any currently playing DTMF tones.
Yorke Leea0d3ca92014-09-15 19:18:13 -0700312 */
313 public void onStopDtmfTone() {}
314
315 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700316 * Notifies the {@link Conference} that the {@link #getAudioState()} property has a new value.
Yorke Leea0d3ca92014-09-15 19:18:13 -0700317 *
318 * @param state The new call audio state.
Yorke Lee4af59352015-05-13 14:14:54 -0700319 * @deprecated Use {@link #onCallAudioStateChanged(CallAudioState)} instead.
320 * @hide
Yorke Leea0d3ca92014-09-15 19:18:13 -0700321 */
Yorke Lee4af59352015-05-13 14:14:54 -0700322 @SystemApi
323 @Deprecated
Yorke Leea0d3ca92014-09-15 19:18:13 -0700324 public void onAudioStateChanged(AudioState state) {}
325
326 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700327 * Notifies the {@link Conference} that the {@link #getCallAudioState()} property has a new
328 * value.
Yorke Lee4af59352015-05-13 14:14:54 -0700329 *
330 * @param state The new call audio state.
331 */
332 public void onCallAudioStateChanged(CallAudioState state) {}
333
334 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700335 * Notifies the {@link Conference} that a {@link Connection} has been added to it.
Andrew Lee46f7f5d2014-11-06 17:00:25 -0800336 *
337 * @param connection The newly added connection.
338 */
339 public void onConnectionAdded(Connection connection) {}
340
341 /**
Santos Cordon823fd3c2014-08-07 18:35:18 -0700342 * Sets state to be on hold.
343 */
344 public final void setOnHold() {
345 setState(Connection.STATE_HOLDING);
346 }
347
348 /**
Tyler Gunnd46595a2015-06-01 14:29:11 -0700349 * Sets state to be dialing.
350 */
351 public final void setDialing() {
352 setState(Connection.STATE_DIALING);
353 }
354
355 /**
Santos Cordon823fd3c2014-08-07 18:35:18 -0700356 * Sets state to be active.
357 */
358 public final void setActive() {
359 setState(Connection.STATE_ACTIVE);
360 }
361
362 /**
363 * Sets state to disconnected.
364 *
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700365 * @param disconnectCause The reason for the disconnection, as described by
366 * {@link android.telecom.DisconnectCause}.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700367 */
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700368 public final void setDisconnected(DisconnectCause disconnectCause) {
369 mDisconnectCause = disconnectCause;;
Santos Cordon823fd3c2014-08-07 18:35:18 -0700370 setState(Connection.STATE_DISCONNECTED);
371 for (Listener l : mListeners) {
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700372 l.onDisconnected(this, mDisconnectCause);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700373 }
374 }
375
376 /**
mike dooley1cf14ac2014-11-04 10:59:53 -0800377 * @return The {@link DisconnectCause} for this connection.
378 */
379 public final DisconnectCause getDisconnectCause() {
380 return mDisconnectCause;
381 }
382
383 /**
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800384 * Sets the capabilities of a conference. See {@code CAPABILITY_*} constants of class
385 * {@link Connection} for valid values.
Nancy Chen56fc25d2014-09-09 12:24:51 -0700386 *
Tyler Gunn720c6642016-03-22 09:02:47 -0700387 * @param connectionCapabilities A bitmask of the {@code Capabilities} of the conference call.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700388 */
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800389 public final void setConnectionCapabilities(int connectionCapabilities) {
390 if (connectionCapabilities != mConnectionCapabilities) {
391 mConnectionCapabilities = connectionCapabilities;
Santos Cordon823fd3c2014-08-07 18:35:18 -0700392
393 for (Listener l : mListeners) {
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800394 l.onConnectionCapabilitiesChanged(this, mConnectionCapabilities);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700395 }
396 }
397 }
398
399 /**
Tyler Gunn720c6642016-03-22 09:02:47 -0700400 * Sets the properties of a conference. See {@code PROPERTY_*} constants of class
401 * {@link Connection} for valid values.
402 *
403 * @param connectionProperties A bitmask of the {@code Properties} of the conference call.
404 */
405 public final void setConnectionProperties(int connectionProperties) {
406 if (connectionProperties != mConnectionProperties) {
407 mConnectionProperties = connectionProperties;
408
409 for (Listener l : mListeners) {
410 l.onConnectionPropertiesChanged(this, mConnectionProperties);
411 }
412 }
413 }
414
415 /**
Santos Cordon823fd3c2014-08-07 18:35:18 -0700416 * Adds the specified connection as a child of this conference.
417 *
418 * @param connection The connection to add.
419 * @return True if the connection was successfully added.
420 */
Santos Cordona4868042014-09-04 17:39:22 -0700421 public final boolean addConnection(Connection connection) {
Rekha Kumar07366812015-03-24 16:42:31 -0700422 Log.d(this, "Connection=%s, connection=", connection);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700423 if (connection != null && !mChildConnections.contains(connection)) {
424 if (connection.setConference(this)) {
425 mChildConnections.add(connection);
Andrew Lee46f7f5d2014-11-06 17:00:25 -0800426 onConnectionAdded(connection);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700427 for (Listener l : mListeners) {
428 l.onConnectionAdded(this, connection);
429 }
430 return true;
431 }
432 }
433 return false;
434 }
435
436 /**
437 * Removes the specified connection as a child of this conference.
438 *
439 * @param connection The connection to remove.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700440 */
Santos Cordona4868042014-09-04 17:39:22 -0700441 public final void removeConnection(Connection connection) {
Santos Cordon0159ac02014-08-21 14:28:11 -0700442 Log.d(this, "removing %s from %s", connection, mChildConnections);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700443 if (connection != null && mChildConnections.remove(connection)) {
444 connection.resetConference();
445 for (Listener l : mListeners) {
446 l.onConnectionRemoved(this, connection);
447 }
448 }
449 }
450
451 /**
Ihab Awad50e35062014-09-30 09:17:03 -0700452 * Sets the connections with which this connection can be conferenced.
453 *
454 * @param conferenceableConnections The set of connections this connection can conference with.
455 */
456 public final void setConferenceableConnections(List<Connection> conferenceableConnections) {
Tyler Gunnec5b6e32016-12-01 19:40:30 -0800457 if (Objects.equals(mConferenceableConnections, conferenceableConnections)) {
458 return;
459 }
460
Ihab Awad50e35062014-09-30 09:17:03 -0700461 clearConferenceableList();
462 for (Connection c : conferenceableConnections) {
463 // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a
464 // small amount of items here.
465 if (!mConferenceableConnections.contains(c)) {
466 c.addConnectionListener(mConnectionDeathListener);
467 mConferenceableConnections.add(c);
468 }
469 }
470 fireOnConferenceableConnectionsChanged();
471 }
472
Rekha Kumar07366812015-03-24 16:42:31 -0700473 /**
474 * Set the video state for the conference.
Yorke Lee32f24732015-05-12 16:18:03 -0700475 * Valid values: {@link VideoProfile#STATE_AUDIO_ONLY},
476 * {@link VideoProfile#STATE_BIDIRECTIONAL},
477 * {@link VideoProfile#STATE_TX_ENABLED},
478 * {@link VideoProfile#STATE_RX_ENABLED}.
Rekha Kumar07366812015-03-24 16:42:31 -0700479 *
480 * @param videoState The new video state.
Rekha Kumar07366812015-03-24 16:42:31 -0700481 */
482 public final void setVideoState(Connection c, int videoState) {
483 Log.d(this, "setVideoState Conference: %s Connection: %s VideoState: %s",
484 this, c, videoState);
485 for (Listener l : mListeners) {
486 l.onVideoStateChanged(this, videoState);
487 }
488 }
489
490 /**
491 * Sets the video connection provider.
492 *
493 * @param videoProvider The video provider.
Rekha Kumar07366812015-03-24 16:42:31 -0700494 */
495 public final void setVideoProvider(Connection c, Connection.VideoProvider videoProvider) {
496 Log.d(this, "setVideoProvider Conference: %s Connection: %s VideoState: %s",
497 this, c, videoProvider);
498 for (Listener l : mListeners) {
499 l.onVideoProviderChanged(this, videoProvider);
500 }
501 }
502
Ihab Awad50e35062014-09-30 09:17:03 -0700503 private final void fireOnConferenceableConnectionsChanged() {
504 for (Listener l : mListeners) {
505 l.onConferenceableConnectionsChanged(this, getConferenceableConnections());
506 }
507 }
508
509 /**
510 * Returns the connections with which this connection can be conferenced.
511 */
512 public final List<Connection> getConferenceableConnections() {
513 return mUnmodifiableConferenceableConnections;
514 }
515
516 /**
Nancy Chenea38cca2014-09-05 16:38:49 -0700517 * Tears down the conference object and any of its current connections.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700518 */
Santos Cordona4868042014-09-04 17:39:22 -0700519 public final void destroy() {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700520 Log.d(this, "destroying conference : %s", this);
521 // Tear down the children.
Santos Cordon0159ac02014-08-21 14:28:11 -0700522 for (Connection connection : mChildConnections) {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700523 Log.d(this, "removing connection %s", connection);
524 removeConnection(connection);
525 }
526
527 // If not yet disconnected, set the conference call as disconnected first.
528 if (mState != Connection.STATE_DISCONNECTED) {
529 Log.d(this, "setting to disconnected");
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700530 setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
Santos Cordon823fd3c2014-08-07 18:35:18 -0700531 }
532
533 // ...and notify.
534 for (Listener l : mListeners) {
535 l.onDestroyed(this);
536 }
537 }
538
539 /**
540 * Add a listener to be notified of a state change.
541 *
542 * @param listener The new listener.
543 * @return This conference.
544 * @hide
545 */
546 public final Conference addListener(Listener listener) {
547 mListeners.add(listener);
548 return this;
549 }
550
551 /**
552 * Removes the specified listener.
553 *
554 * @param listener The listener to remove.
555 * @return This conference.
556 * @hide
557 */
558 public final Conference removeListener(Listener listener) {
559 mListeners.remove(listener);
560 return this;
561 }
562
Yorke Leea0d3ca92014-09-15 19:18:13 -0700563 /**
Tyler Gunn4a57b9b2014-10-30 14:27:48 -0700564 * Retrieves the primary connection associated with the conference. The primary connection is
565 * the connection from which the conference will retrieve its current state.
566 *
567 * @return The primary connection.
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700568 * @hide
Tyler Gunn4a57b9b2014-10-30 14:27:48 -0700569 */
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700570 @SystemApi
Santos Cordon4055d642015-05-12 14:19:24 -0700571 public Connection getPrimaryConnection() {
Tyler Gunn4a57b9b2014-10-30 14:27:48 -0700572 if (mUnmodifiableChildConnections == null || mUnmodifiableChildConnections.isEmpty()) {
573 return null;
574 }
575 return mUnmodifiableChildConnections.get(0);
576 }
577
578 /**
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700579 * @hide
580 * @deprecated Use {@link #setConnectionTime}.
Tyler Gunncd5d33c2015-01-12 09:02:01 -0800581 */
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700582 @Deprecated
583 @SystemApi
584 public final void setConnectTimeMillis(long connectTimeMillis) {
585 setConnectionTime(connectTimeMillis);
Tyler Gunncd5d33c2015-01-12 09:02:01 -0800586 }
587
588 /**
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700589 * Sets the connection start time of the {@code Conference}.
590 *
591 * @param connectionTimeMillis The connection time, in milliseconds.
592 */
593 public final void setConnectionTime(long connectionTimeMillis) {
594 mConnectTimeMillis = connectionTimeMillis;
595 }
596
597 /**
598 * @hide
599 * @deprecated Use {@link #getConnectionTime}.
600 */
601 @Deprecated
602 @SystemApi
603 public final long getConnectTimeMillis() {
604 return getConnectionTime();
605 }
606
607 /**
608 * Retrieves the connection start time of the {@code Conference}, if specified. A value of
Tyler Gunncd5d33c2015-01-12 09:02:01 -0800609 * {@link #CONNECT_TIME_NOT_SPECIFIED} indicates that Telecom should determine the start time
610 * of the conference.
611 *
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700612 * @return The time at which the {@code Conference} was connected.
Tyler Gunncd5d33c2015-01-12 09:02:01 -0800613 */
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700614 public final long getConnectionTime() {
Tyler Gunncd5d33c2015-01-12 09:02:01 -0800615 return mConnectTimeMillis;
616 }
617
618 /**
Yorke Leea0d3ca92014-09-15 19:18:13 -0700619 * Inform this Conference that the state of its audio output has been changed externally.
620 *
621 * @param state The new audio state.
622 * @hide
623 */
Yorke Lee4af59352015-05-13 14:14:54 -0700624 final void setCallAudioState(CallAudioState state) {
625 Log.d(this, "setCallAudioState %s", state);
626 mCallAudioState = state;
627 onAudioStateChanged(getAudioState());
628 onCallAudioStateChanged(state);
Yorke Leea0d3ca92014-09-15 19:18:13 -0700629 }
630
Santos Cordon823fd3c2014-08-07 18:35:18 -0700631 private void setState(int newState) {
632 if (newState != Connection.STATE_ACTIVE &&
633 newState != Connection.STATE_HOLDING &&
634 newState != Connection.STATE_DISCONNECTED) {
635 Log.w(this, "Unsupported state transition for Conference call.",
636 Connection.stateToString(newState));
637 return;
638 }
639
640 if (mState != newState) {
641 int oldState = mState;
642 mState = newState;
643 for (Listener l : mListeners) {
644 l.onStateChanged(this, oldState, newState);
645 }
646 }
647 }
Ihab Awad50e35062014-09-30 09:17:03 -0700648
649 private final void clearConferenceableList() {
650 for (Connection c : mConferenceableConnections) {
651 c.removeConnectionListener(mConnectionDeathListener);
652 }
653 mConferenceableConnections.clear();
654 }
Rekha Kumar07366812015-03-24 16:42:31 -0700655
656 @Override
657 public String toString() {
658 return String.format(Locale.US,
659 "[State: %s,Capabilites: %s, VideoState: %s, VideoProvider: %s, ThisObject %s]",
660 Connection.stateToString(mState),
661 Call.Details.capabilitiesToString(mConnectionCapabilities),
662 getVideoState(),
663 getVideoProvider(),
664 super.toString());
665 }
Andrew Lee0f51da32015-04-16 13:11:55 -0700666
Andrew Leeedc625f2015-04-14 13:38:12 -0700667 /**
668 * Sets the label and icon status to display in the InCall UI.
669 *
670 * @param statusHints The status label and icon to set.
671 */
672 public final void setStatusHints(StatusHints statusHints) {
673 mStatusHints = statusHints;
674 for (Listener l : mListeners) {
675 l.onStatusHintsChanged(this, statusHints);
676 }
677 }
678
679 /**
680 * @return The status hints for this conference.
681 */
682 public final StatusHints getStatusHints() {
683 return mStatusHints;
684 }
Santos Cordon6b7f9552015-05-27 17:21:45 -0700685
686 /**
Tyler Gunndee56a82016-03-23 16:06:34 -0700687 * Replaces all the extras associated with this {@code Conference}.
688 * <p>
689 * New or existing keys are replaced in the {@code Conference} extras. Keys which are no longer
690 * in the new extras, but were present the last time {@code setExtras} was called are removed.
691 * <p>
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700692 * Alternatively you may use the {@link #putExtras(Bundle)}, and
693 * {@link #removeExtras(String...)} methods to modify the extras.
694 * <p>
Tyler Gunndee56a82016-03-23 16:06:34 -0700695 * 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 -0700696 * Keys should be fully qualified (e.g., com.example.extras.MY_EXTRA) to avoid conflicts.
Santos Cordon6b7f9552015-05-27 17:21:45 -0700697 *
Tyler Gunndee56a82016-03-23 16:06:34 -0700698 * @param extras The extras associated with this {@code Conference}.
Santos Cordon6b7f9552015-05-27 17:21:45 -0700699 */
700 public final void setExtras(@Nullable Bundle extras) {
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700701 // Keeping putExtras and removeExtras in the same lock so that this operation happens as a
702 // block instead of letting other threads put/remove while this method is running.
703 synchronized (mExtrasLock) {
704 // Add/replace any new or changed extras values.
705 putExtras(extras);
706 // If we have used "setExtras" in the past, compare the key set from the last invocation
707 // to the current one and remove any keys that went away.
708 if (mPreviousExtraKeys != null) {
709 List<String> toRemove = new ArrayList<String>();
710 for (String oldKey : mPreviousExtraKeys) {
711 if (extras == null || !extras.containsKey(oldKey)) {
712 toRemove.add(oldKey);
713 }
714 }
Tyler Gunndee56a82016-03-23 16:06:34 -0700715
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700716 if (!toRemove.isEmpty()) {
717 removeExtras(toRemove);
Tyler Gunndee56a82016-03-23 16:06:34 -0700718 }
719 }
720
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700721 // Track the keys the last time set called setExtras. This way, the next time setExtras
722 // is called we can see if the caller has removed any extras values.
723 if (mPreviousExtraKeys == null) {
724 mPreviousExtraKeys = new ArraySet<String>();
Tyler Gunndee56a82016-03-23 16:06:34 -0700725 }
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700726 mPreviousExtraKeys.clear();
727 if (extras != null) {
728 mPreviousExtraKeys.addAll(extras.keySet());
729 }
Tyler Gunna8fb8ab2016-03-29 10:24:22 -0700730 }
Tyler Gunndee56a82016-03-23 16:06:34 -0700731 }
732
733 /**
734 * Adds some extras to this {@link Conference}. Existing keys are replaced and new ones are
735 * added.
736 * <p>
737 * No assumptions should be made as to how an In-Call UI or service will handle these extras.
738 * Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
739 *
740 * @param extras The extras to add.
741 */
742 public final void putExtras(@NonNull Bundle extras) {
743 if (extras == null) {
744 return;
745 }
746
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700747 // Creating a Bundle clone so we don't have to synchronize on mExtrasLock while calling
748 // onExtrasChanged.
749 Bundle listenersBundle;
750 synchronized (mExtrasLock) {
751 if (mExtras == null) {
752 mExtras = new Bundle();
753 }
754 mExtras.putAll(extras);
755 listenersBundle = new Bundle(mExtras);
Tyler Gunndee56a82016-03-23 16:06:34 -0700756 }
Tyler Gunndee56a82016-03-23 16:06:34 -0700757
Santos Cordon6b7f9552015-05-27 17:21:45 -0700758 for (Listener l : mListeners) {
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700759 l.onExtrasChanged(this, new Bundle(listenersBundle));
Santos Cordon6b7f9552015-05-27 17:21:45 -0700760 }
761 }
762
763 /**
Tyler Gunndee56a82016-03-23 16:06:34 -0700764 * Adds a boolean extra to this {@link Conference}.
765 *
766 * @param key The extra key.
767 * @param value The value.
768 * @hide
769 */
770 public final void putExtra(String key, boolean value) {
771 Bundle newExtras = new Bundle();
772 newExtras.putBoolean(key, value);
773 putExtras(newExtras);
774 }
775
776 /**
777 * Adds an integer extra to this {@link Conference}.
778 *
779 * @param key The extra key.
780 * @param value The value.
781 * @hide
782 */
783 public final void putExtra(String key, int value) {
784 Bundle newExtras = new Bundle();
785 newExtras.putInt(key, value);
786 putExtras(newExtras);
787 }
788
789 /**
790 * Adds a string extra to this {@link Conference}.
791 *
792 * @param key The extra key.
793 * @param value The value.
794 * @hide
795 */
796 public final void putExtra(String key, String value) {
797 Bundle newExtras = new Bundle();
798 newExtras.putString(key, value);
799 putExtras(newExtras);
800 }
801
802 /**
Tyler Gunn071be6f2016-05-10 14:52:33 -0700803 * Removes extras from this {@link Conference}.
Tyler Gunndee56a82016-03-23 16:06:34 -0700804 *
Tyler Gunn071be6f2016-05-10 14:52:33 -0700805 * @param keys The keys of the extras to remove.
Tyler Gunndee56a82016-03-23 16:06:34 -0700806 */
807 public final void removeExtras(List<String> keys) {
808 if (keys == null || keys.isEmpty()) {
809 return;
810 }
811
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700812 synchronized (mExtrasLock) {
813 if (mExtras != null) {
814 for (String key : keys) {
815 mExtras.remove(key);
816 }
Tyler Gunndee56a82016-03-23 16:06:34 -0700817 }
818 }
819
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700820 List<String> unmodifiableKeys = Collections.unmodifiableList(keys);
Tyler Gunndee56a82016-03-23 16:06:34 -0700821 for (Listener l : mListeners) {
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700822 l.onExtrasRemoved(this, unmodifiableKeys);
Tyler Gunndee56a82016-03-23 16:06:34 -0700823 }
824 }
825
826 /**
Tyler Gunn071be6f2016-05-10 14:52:33 -0700827 * Removes extras from this {@link Conference}.
828 *
829 * @param keys The keys of the extras to remove.
830 */
831 public final void removeExtras(String ... keys) {
832 removeExtras(Arrays.asList(keys));
833 }
834
835 /**
Tyler Gunndee56a82016-03-23 16:06:34 -0700836 * Returns the extras associated with this conference.
Tyler Gunn2cbe2b52016-05-04 15:48:10 +0000837 * <p>
838 * Extras should be updated using {@link #putExtras(Bundle)} and {@link #removeExtras(List)}.
839 * <p>
840 * Telecom or an {@link InCallService} can also update the extras via
841 * {@link android.telecom.Call#putExtras(Bundle)}, and
842 * {@link Call#removeExtras(List)}.
843 * <p>
844 * The conference is notified of changes to the extras made by Telecom or an
845 * {@link InCallService} by {@link #onExtrasChanged(Bundle)}.
Tyler Gunndee56a82016-03-23 16:06:34 -0700846 *
847 * @return The extras associated with this connection.
Santos Cordon6b7f9552015-05-27 17:21:45 -0700848 */
849 public final Bundle getExtras() {
850 return mExtras;
851 }
Tyler Gunndee56a82016-03-23 16:06:34 -0700852
853 /**
854 * Notifies this {@link Conference} of a change to the extras made outside the
855 * {@link ConnectionService}.
856 * <p>
857 * These extras changes can originate from Telecom itself, or from an {@link InCallService} via
858 * {@link android.telecom.Call#putExtras(Bundle)}, and
859 * {@link Call#removeExtras(List)}.
860 *
861 * @param extras The new extras bundle.
862 */
863 public void onExtrasChanged(Bundle extras) {}
864
865 /**
866 * Handles a change to extras received from Telecom.
867 *
868 * @param extras The new extras.
869 * @hide
870 */
871 final void handleExtrasChanged(Bundle extras) {
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700872 Bundle b = null;
873 synchronized (mExtrasLock) {
874 mExtras = extras;
875 if (mExtras != null) {
876 b = new Bundle(mExtras);
877 }
878 }
879 onExtrasChanged(b);
Tyler Gunndee56a82016-03-23 16:06:34 -0700880 }
Santos Cordon823fd3c2014-08-07 18:35:18 -0700881}