blob: 024bd303304fbe2babd3eba4406e67a2d0c26364 [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;
Tyler Gunndee56a82016-03-23 16:06:34 -070025import android.util.ArraySet;
Evan Charlton0e094d92014-11-08 15:49:16 -080026
Ihab Awad50e35062014-09-30 09:17:03 -070027import java.util.ArrayList;
Tyler Gunn071be6f2016-05-10 14:52:33 -070028import java.util.Arrays;
Santos Cordon823fd3c2014-08-07 18:35:18 -070029import java.util.Collections;
Santos Cordon823fd3c2014-08-07 18:35:18 -070030import java.util.List;
Rekha Kumar07366812015-03-24 16:42:31 -070031import java.util.Locale;
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;
Tyler Gunn17541392018-02-01 08:58:38 -080084 private long mConnectionStartElapsedRealTime = CONNECT_TIME_NOT_SPECIFIED;
Andrew Leeedc625f2015-04-14 13:38:12 -070085 private StatusHints mStatusHints;
Santos Cordon6b7f9552015-05-27 17:21:45 -070086 private Bundle mExtras;
Tyler Gunndee56a82016-03-23 16:06:34 -070087 private Set<String> mPreviousExtraKeys;
Brad Ebinger4fa6a012016-06-14 17:04:01 -070088 private final Object mExtrasLock = new Object();
Santos Cordon823fd3c2014-08-07 18:35:18 -070089
Ihab Awad50e35062014-09-30 09:17:03 -070090 private final Connection.Listener mConnectionDeathListener = new Connection.Listener() {
91 @Override
92 public void onDestroyed(Connection c) {
93 if (mConferenceableConnections.remove(c)) {
94 fireOnConferenceableConnectionsChanged();
95 }
96 }
97 };
98
Nancy Chen56fc25d2014-09-09 12:24:51 -070099 /**
100 * Constructs a new Conference with a mandatory {@link PhoneAccountHandle}
101 *
102 * @param phoneAccount The {@code PhoneAccountHandle} associated with the conference.
103 */
Santos Cordon823fd3c2014-08-07 18:35:18 -0700104 public Conference(PhoneAccountHandle phoneAccount) {
105 mPhoneAccount = phoneAccount;
106 }
107
Nancy Chen56fc25d2014-09-09 12:24:51 -0700108 /**
Jack Yu67140302015-12-10 12:27:58 -0800109 * Returns the telecom internal call ID associated with this conference.
110 *
111 * @return The telecom call ID.
112 * @hide
113 */
114 public final String getTelecomCallId() {
115 return mTelecomCallId;
116 }
117
118 /**
119 * Sets the telecom internal call ID associated with this conference.
120 *
121 * @param telecomCallId The telecom call ID.
122 * @hide
123 */
124 public final void setTelecomCallId(String telecomCallId) {
125 mTelecomCallId = telecomCallId;
126 }
127
128 /**
Nancy Chen56fc25d2014-09-09 12:24:51 -0700129 * Returns the {@link PhoneAccountHandle} the conference call is being placed through.
130 *
131 * @return A {@code PhoneAccountHandle} object representing the PhoneAccount of the conference.
132 */
Nancy Chenea38cca2014-09-05 16:38:49 -0700133 public final PhoneAccountHandle getPhoneAccountHandle() {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700134 return mPhoneAccount;
135 }
136
Nancy Chen56fc25d2014-09-09 12:24:51 -0700137 /**
138 * Returns the list of connections currently associated with the conference call.
139 *
140 * @return A list of {@code Connection} objects which represent the children of the conference.
141 */
Santos Cordon823fd3c2014-08-07 18:35:18 -0700142 public final List<Connection> getConnections() {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700143 return mUnmodifiableChildConnections;
Santos Cordon823fd3c2014-08-07 18:35:18 -0700144 }
145
Nancy Chen56fc25d2014-09-09 12:24:51 -0700146 /**
147 * Gets the state of the conference call. See {@link Connection} for valid values.
148 *
149 * @return A constant representing the state the conference call is currently in.
150 */
Santos Cordon823fd3c2014-08-07 18:35:18 -0700151 public final int getState() {
152 return mState;
153 }
154
Nancy Chen56fc25d2014-09-09 12:24:51 -0700155 /**
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700156 * Returns the capabilities of the conference. See {@code CAPABILITY_*} constants in class
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800157 * {@link Connection} for valid values.
Nancy Chen56fc25d2014-09-09 12:24:51 -0700158 *
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800159 * @return A bitmask of the capabilities of the conference call.
Nancy Chen56fc25d2014-09-09 12:24:51 -0700160 */
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800161 public final int getConnectionCapabilities() {
162 return mConnectionCapabilities;
163 }
164
165 /**
Tyler Gunn720c6642016-03-22 09:02:47 -0700166 * Returns the properties of the conference. See {@code PROPERTY_*} constants in class
167 * {@link Connection} for valid values.
168 *
169 * @return A bitmask of the properties of the conference call.
170 */
171 public final int getConnectionProperties() {
172 return mConnectionProperties;
173 }
174
175 /**
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800176 * Whether the given capabilities support the specified capability.
177 *
178 * @param capabilities A capability bit field.
179 * @param capability The capability to check capabilities for.
180 * @return Whether the specified capability is supported.
181 * @hide
182 */
183 public static boolean can(int capabilities, int capability) {
184 return (capabilities & capability) != 0;
185 }
186
187 /**
188 * Whether the capabilities of this {@code Connection} supports the specified capability.
189 *
190 * @param capability The capability to check capabilities for.
191 * @return Whether the specified capability is supported.
192 * @hide
193 */
194 public boolean can(int capability) {
195 return can(mConnectionCapabilities, capability);
196 }
197
198 /**
199 * Removes the specified capability from the set of capabilities of this {@code Conference}.
200 *
201 * @param capability The capability to remove from the set.
202 * @hide
203 */
204 public void removeCapability(int capability) {
Omkar Kolangadea0f46a92015-03-23 17:51:16 -0700205 int newCapabilities = mConnectionCapabilities;
206 newCapabilities &= ~capability;
207
208 setConnectionCapabilities(newCapabilities);
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800209 }
210
211 /**
212 * Adds the specified capability to the set of capabilities of this {@code Conference}.
213 *
214 * @param capability The capability to add to the set.
215 * @hide
216 */
217 public void addCapability(int capability) {
Omkar Kolangadea0f46a92015-03-23 17:51:16 -0700218 int newCapabilities = mConnectionCapabilities;
219 newCapabilities |= capability;
220
221 setConnectionCapabilities(newCapabilities);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700222 }
223
224 /**
Yorke Leea0d3ca92014-09-15 19:18:13 -0700225 * @return The audio state of the conference, describing how its audio is currently
226 * being routed by the system. This is {@code null} if this Conference
227 * does not directly know about its audio state.
Yorke Lee4af59352015-05-13 14:14:54 -0700228 * @deprecated Use {@link #getCallAudioState()} instead.
229 * @hide
Yorke Leea0d3ca92014-09-15 19:18:13 -0700230 */
Yorke Lee4af59352015-05-13 14:14:54 -0700231 @Deprecated
232 @SystemApi
Yorke Leea0d3ca92014-09-15 19:18:13 -0700233 public final AudioState getAudioState() {
Yorke Lee4af59352015-05-13 14:14:54 -0700234 return new AudioState(mCallAudioState);
235 }
236
237 /**
238 * @return The audio state of the conference, describing how its audio is currently
239 * being routed by the system. This is {@code null} if this Conference
240 * does not directly know about its audio state.
241 */
242 public final CallAudioState getCallAudioState() {
243 return mCallAudioState;
Yorke Leea0d3ca92014-09-15 19:18:13 -0700244 }
245
246 /**
Rekha Kumar07366812015-03-24 16:42:31 -0700247 * Returns VideoProvider of the primary call. This can be null.
Rekha Kumar07366812015-03-24 16:42:31 -0700248 */
249 public VideoProvider getVideoProvider() {
250 return null;
251 }
252
253 /**
254 * Returns video state of the primary call.
Rekha Kumar07366812015-03-24 16:42:31 -0700255 */
256 public int getVideoState() {
Tyler Gunn87b73f32015-06-03 10:09:59 -0700257 return VideoProfile.STATE_AUDIO_ONLY;
Rekha Kumar07366812015-03-24 16:42:31 -0700258 }
259
260 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700261 * Notifies the {@link Conference} when the Conference and all it's {@link Connection}s should
262 * be disconnected.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700263 */
264 public void onDisconnect() {}
265
266 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700267 * Notifies the {@link Conference} when the specified {@link Connection} should be separated
268 * from the conference call.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700269 *
270 * @param connection The connection to separate.
271 */
272 public void onSeparate(Connection connection) {}
273
274 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700275 * Notifies the {@link Conference} when the specified {@link Connection} should merged with the
276 * conference call.
Ihab Awad50e35062014-09-30 09:17:03 -0700277 *
278 * @param connection The {@code Connection} to merge.
279 */
280 public void onMerge(Connection connection) {}
281
282 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700283 * Notifies the {@link Conference} when it should be put on hold.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700284 */
285 public void onHold() {}
286
287 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700288 * Notifies the {@link Conference} when it should be moved from a held to active state.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700289 */
290 public void onUnhold() {}
291
292 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700293 * Notifies the {@link Conference} when the child calls should be merged. Only invoked if the
294 * conference contains the capability {@link Connection#CAPABILITY_MERGE_CONFERENCE}.
Santos Cordona4868042014-09-04 17:39:22 -0700295 */
296 public void onMerge() {}
297
298 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700299 * Notifies the {@link Conference} when the child calls should be swapped. Only invoked if the
300 * conference contains the capability {@link Connection#CAPABILITY_SWAP_CONFERENCE}.
Santos Cordona4868042014-09-04 17:39:22 -0700301 */
302 public void onSwap() {}
303
304 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700305 * Notifies the {@link Conference} of a request to play a DTMF tone.
Yorke Leea0d3ca92014-09-15 19:18:13 -0700306 *
307 * @param c A DTMF character.
308 */
309 public void onPlayDtmfTone(char c) {}
310
311 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700312 * Notifies the {@link Conference} of a request to stop any currently playing DTMF tones.
Yorke Leea0d3ca92014-09-15 19:18:13 -0700313 */
314 public void onStopDtmfTone() {}
315
316 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700317 * Notifies the {@link Conference} that the {@link #getAudioState()} property has a new value.
Yorke Leea0d3ca92014-09-15 19:18:13 -0700318 *
319 * @param state The new call audio state.
Yorke Lee4af59352015-05-13 14:14:54 -0700320 * @deprecated Use {@link #onCallAudioStateChanged(CallAudioState)} instead.
321 * @hide
Yorke Leea0d3ca92014-09-15 19:18:13 -0700322 */
Yorke Lee4af59352015-05-13 14:14:54 -0700323 @SystemApi
324 @Deprecated
Yorke Leea0d3ca92014-09-15 19:18:13 -0700325 public void onAudioStateChanged(AudioState state) {}
326
327 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700328 * Notifies the {@link Conference} that the {@link #getCallAudioState()} property has a new
329 * value.
Yorke Lee4af59352015-05-13 14:14:54 -0700330 *
331 * @param state The new call audio state.
332 */
333 public void onCallAudioStateChanged(CallAudioState state) {}
334
335 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700336 * Notifies the {@link Conference} that a {@link Connection} has been added to it.
Andrew Lee46f7f5d2014-11-06 17:00:25 -0800337 *
338 * @param connection The newly added connection.
339 */
340 public void onConnectionAdded(Connection connection) {}
341
342 /**
Santos Cordon823fd3c2014-08-07 18:35:18 -0700343 * Sets state to be on hold.
344 */
345 public final void setOnHold() {
346 setState(Connection.STATE_HOLDING);
347 }
348
349 /**
Tyler Gunnd46595a2015-06-01 14:29:11 -0700350 * Sets state to be dialing.
351 */
352 public final void setDialing() {
353 setState(Connection.STATE_DIALING);
354 }
355
356 /**
Santos Cordon823fd3c2014-08-07 18:35:18 -0700357 * Sets state to be active.
358 */
359 public final void setActive() {
360 setState(Connection.STATE_ACTIVE);
361 }
362
363 /**
364 * Sets state to disconnected.
365 *
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700366 * @param disconnectCause The reason for the disconnection, as described by
367 * {@link android.telecom.DisconnectCause}.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700368 */
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700369 public final void setDisconnected(DisconnectCause disconnectCause) {
370 mDisconnectCause = disconnectCause;;
Santos Cordon823fd3c2014-08-07 18:35:18 -0700371 setState(Connection.STATE_DISCONNECTED);
372 for (Listener l : mListeners) {
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700373 l.onDisconnected(this, mDisconnectCause);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700374 }
375 }
376
377 /**
mike dooley1cf14ac2014-11-04 10:59:53 -0800378 * @return The {@link DisconnectCause} for this connection.
379 */
380 public final DisconnectCause getDisconnectCause() {
381 return mDisconnectCause;
382 }
383
384 /**
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800385 * Sets the capabilities of a conference. See {@code CAPABILITY_*} constants of class
386 * {@link Connection} for valid values.
Nancy Chen56fc25d2014-09-09 12:24:51 -0700387 *
Tyler Gunn720c6642016-03-22 09:02:47 -0700388 * @param connectionCapabilities A bitmask of the {@code Capabilities} of the conference call.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700389 */
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800390 public final void setConnectionCapabilities(int connectionCapabilities) {
391 if (connectionCapabilities != mConnectionCapabilities) {
392 mConnectionCapabilities = connectionCapabilities;
Santos Cordon823fd3c2014-08-07 18:35:18 -0700393
394 for (Listener l : mListeners) {
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800395 l.onConnectionCapabilitiesChanged(this, mConnectionCapabilities);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700396 }
397 }
398 }
399
400 /**
Tyler Gunn720c6642016-03-22 09:02:47 -0700401 * Sets the properties of a conference. See {@code PROPERTY_*} constants of class
402 * {@link Connection} for valid values.
403 *
404 * @param connectionProperties A bitmask of the {@code Properties} of the conference call.
405 */
406 public final void setConnectionProperties(int connectionProperties) {
407 if (connectionProperties != mConnectionProperties) {
408 mConnectionProperties = connectionProperties;
409
410 for (Listener l : mListeners) {
411 l.onConnectionPropertiesChanged(this, mConnectionProperties);
412 }
413 }
414 }
415
416 /**
Santos Cordon823fd3c2014-08-07 18:35:18 -0700417 * Adds the specified connection as a child of this conference.
418 *
419 * @param connection The connection to add.
420 * @return True if the connection was successfully added.
421 */
Santos Cordona4868042014-09-04 17:39:22 -0700422 public final boolean addConnection(Connection connection) {
Rekha Kumar07366812015-03-24 16:42:31 -0700423 Log.d(this, "Connection=%s, connection=", connection);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700424 if (connection != null && !mChildConnections.contains(connection)) {
425 if (connection.setConference(this)) {
426 mChildConnections.add(connection);
Andrew Lee46f7f5d2014-11-06 17:00:25 -0800427 onConnectionAdded(connection);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700428 for (Listener l : mListeners) {
429 l.onConnectionAdded(this, connection);
430 }
431 return true;
432 }
433 }
434 return false;
435 }
436
437 /**
438 * Removes the specified connection as a child of this conference.
439 *
440 * @param connection The connection to remove.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700441 */
Santos Cordona4868042014-09-04 17:39:22 -0700442 public final void removeConnection(Connection connection) {
Santos Cordon0159ac02014-08-21 14:28:11 -0700443 Log.d(this, "removing %s from %s", connection, mChildConnections);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700444 if (connection != null && mChildConnections.remove(connection)) {
445 connection.resetConference();
446 for (Listener l : mListeners) {
447 l.onConnectionRemoved(this, connection);
448 }
449 }
450 }
451
452 /**
Ihab Awad50e35062014-09-30 09:17:03 -0700453 * Sets the connections with which this connection can be conferenced.
454 *
455 * @param conferenceableConnections The set of connections this connection can conference with.
456 */
457 public final void setConferenceableConnections(List<Connection> conferenceableConnections) {
458 clearConferenceableList();
459 for (Connection c : conferenceableConnections) {
460 // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a
461 // small amount of items here.
462 if (!mConferenceableConnections.contains(c)) {
463 c.addConnectionListener(mConnectionDeathListener);
464 mConferenceableConnections.add(c);
465 }
466 }
467 fireOnConferenceableConnectionsChanged();
468 }
469
Rekha Kumar07366812015-03-24 16:42:31 -0700470 /**
471 * Set the video state for the conference.
Yorke Lee32f24732015-05-12 16:18:03 -0700472 * Valid values: {@link VideoProfile#STATE_AUDIO_ONLY},
473 * {@link VideoProfile#STATE_BIDIRECTIONAL},
474 * {@link VideoProfile#STATE_TX_ENABLED},
475 * {@link VideoProfile#STATE_RX_ENABLED}.
Rekha Kumar07366812015-03-24 16:42:31 -0700476 *
477 * @param videoState The new video state.
Rekha Kumar07366812015-03-24 16:42:31 -0700478 */
479 public final void setVideoState(Connection c, int videoState) {
480 Log.d(this, "setVideoState Conference: %s Connection: %s VideoState: %s",
481 this, c, videoState);
482 for (Listener l : mListeners) {
483 l.onVideoStateChanged(this, videoState);
484 }
485 }
486
487 /**
488 * Sets the video connection provider.
489 *
490 * @param videoProvider The video provider.
Rekha Kumar07366812015-03-24 16:42:31 -0700491 */
492 public final void setVideoProvider(Connection c, Connection.VideoProvider videoProvider) {
493 Log.d(this, "setVideoProvider Conference: %s Connection: %s VideoState: %s",
494 this, c, videoProvider);
495 for (Listener l : mListeners) {
496 l.onVideoProviderChanged(this, videoProvider);
497 }
498 }
499
Ihab Awad50e35062014-09-30 09:17:03 -0700500 private final void fireOnConferenceableConnectionsChanged() {
501 for (Listener l : mListeners) {
502 l.onConferenceableConnectionsChanged(this, getConferenceableConnections());
503 }
504 }
505
506 /**
507 * Returns the connections with which this connection can be conferenced.
508 */
509 public final List<Connection> getConferenceableConnections() {
510 return mUnmodifiableConferenceableConnections;
511 }
512
513 /**
Nancy Chenea38cca2014-09-05 16:38:49 -0700514 * Tears down the conference object and any of its current connections.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700515 */
Santos Cordona4868042014-09-04 17:39:22 -0700516 public final void destroy() {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700517 Log.d(this, "destroying conference : %s", this);
518 // Tear down the children.
Santos Cordon0159ac02014-08-21 14:28:11 -0700519 for (Connection connection : mChildConnections) {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700520 Log.d(this, "removing connection %s", connection);
521 removeConnection(connection);
522 }
523
524 // If not yet disconnected, set the conference call as disconnected first.
525 if (mState != Connection.STATE_DISCONNECTED) {
526 Log.d(this, "setting to disconnected");
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700527 setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
Santos Cordon823fd3c2014-08-07 18:35:18 -0700528 }
529
530 // ...and notify.
531 for (Listener l : mListeners) {
532 l.onDestroyed(this);
533 }
534 }
535
536 /**
537 * Add a listener to be notified of a state change.
538 *
539 * @param listener The new listener.
540 * @return This conference.
541 * @hide
542 */
543 public final Conference addListener(Listener listener) {
544 mListeners.add(listener);
545 return this;
546 }
547
548 /**
549 * Removes the specified listener.
550 *
551 * @param listener The listener to remove.
552 * @return This conference.
553 * @hide
554 */
555 public final Conference removeListener(Listener listener) {
556 mListeners.remove(listener);
557 return this;
558 }
559
Yorke Leea0d3ca92014-09-15 19:18:13 -0700560 /**
Tyler Gunn4a57b9b2014-10-30 14:27:48 -0700561 * Retrieves the primary connection associated with the conference. The primary connection is
562 * the connection from which the conference will retrieve its current state.
563 *
564 * @return The primary connection.
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700565 * @hide
Tyler Gunn4a57b9b2014-10-30 14:27:48 -0700566 */
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700567 @SystemApi
Santos Cordon4055d642015-05-12 14:19:24 -0700568 public Connection getPrimaryConnection() {
Tyler Gunn4a57b9b2014-10-30 14:27:48 -0700569 if (mUnmodifiableChildConnections == null || mUnmodifiableChildConnections.isEmpty()) {
570 return null;
571 }
572 return mUnmodifiableChildConnections.get(0);
573 }
574
575 /**
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700576 * @hide
577 * @deprecated Use {@link #setConnectionTime}.
Tyler Gunncd5d33c2015-01-12 09:02:01 -0800578 */
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700579 @Deprecated
580 @SystemApi
581 public final void setConnectTimeMillis(long connectTimeMillis) {
582 setConnectionTime(connectTimeMillis);
Tyler Gunncd5d33c2015-01-12 09:02:01 -0800583 }
584
585 /**
Tyler Gunn17541392018-02-01 08:58:38 -0800586 * Sets the connection start time of the {@code Conference}. This is used in the call log to
587 * indicate the date and time when the conference took place.
588 * <p>
589 * Should be specified in wall-clock time returned by {@link System#currentTimeMillis()}.
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700590 * <p>
591 * When setting the connection time, you should always set the connection elapsed time via
Tyler Gunn17541392018-02-01 08:58:38 -0800592 * {@link #setConnectionStartElapsedRealTime(long)} to ensure the duration is reflected.
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700593 *
Tyler Gunn17541392018-02-01 08:58:38 -0800594 * @param connectionTimeMillis The connection time, in milliseconds, as returned by
595 * {@link System#currentTimeMillis()}.
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700596 */
597 public final void setConnectionTime(long connectionTimeMillis) {
598 mConnectTimeMillis = connectionTimeMillis;
599 }
600
601 /**
Tyler Gunn17541392018-02-01 08:58:38 -0800602 * Sets the start time of the {@link Conference} which is the basis for the determining the
603 * duration of the {@link Conference}.
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700604 * <p>
Tyler Gunn17541392018-02-01 08:58:38 -0800605 * You should use a value returned by {@link SystemClock#elapsedRealtime()} to ensure that time
606 * zone changes do not impact the conference duration.
607 * <p>
608 * When setting this, you should also set the connection time via
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700609 * {@link #setConnectionTime(long)}.
610 *
Tyler Gunn17541392018-02-01 08:58:38 -0800611 * @param connectionStartElapsedRealTime The connection time, as measured by
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700612 * {@link SystemClock#elapsedRealtime()}.
613 */
Tyler Gunn17541392018-02-01 08:58:38 -0800614 public final void setConnectionStartElapsedRealTime(long connectionStartElapsedRealTime) {
615 mConnectionStartElapsedRealTime = connectionStartElapsedRealTime;
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700616 }
617
618 /**
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700619 * @hide
620 * @deprecated Use {@link #getConnectionTime}.
621 */
622 @Deprecated
623 @SystemApi
624 public final long getConnectTimeMillis() {
625 return getConnectionTime();
626 }
627
628 /**
629 * Retrieves the connection start time of the {@code Conference}, if specified. A value of
Tyler Gunncd5d33c2015-01-12 09:02:01 -0800630 * {@link #CONNECT_TIME_NOT_SPECIFIED} indicates that Telecom should determine the start time
631 * of the conference.
632 *
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700633 * @return The time at which the {@code Conference} was connected.
Tyler Gunncd5d33c2015-01-12 09:02:01 -0800634 */
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700635 public final long getConnectionTime() {
Tyler Gunncd5d33c2015-01-12 09:02:01 -0800636 return mConnectTimeMillis;
637 }
638
639 /**
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700640 * Retrieves the connection start time of the {@link Conference}, if specified. A value of
641 * {@link #CONNECT_TIME_NOT_SPECIFIED} indicates that Telecom should determine the start time
642 * of the conference.
643 *
644 * This is based on the value of {@link SystemClock#elapsedRealtime()} to ensure that it is not
645 * impacted by wall clock changes (user initiated, network initiated, time zone change, etc).
646 *
647 * @return The elapsed time at which the {@link Conference} was connected.
648 * @hide
649 */
Tyler Gunn17541392018-02-01 08:58:38 -0800650 public final long getConnectionStartElapsedRealTime() {
651 return mConnectionStartElapsedRealTime;
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700652 }
653
654 /**
Yorke Leea0d3ca92014-09-15 19:18:13 -0700655 * Inform this Conference that the state of its audio output has been changed externally.
656 *
657 * @param state The new audio state.
658 * @hide
659 */
Yorke Lee4af59352015-05-13 14:14:54 -0700660 final void setCallAudioState(CallAudioState state) {
661 Log.d(this, "setCallAudioState %s", state);
662 mCallAudioState = state;
663 onAudioStateChanged(getAudioState());
664 onCallAudioStateChanged(state);
Yorke Leea0d3ca92014-09-15 19:18:13 -0700665 }
666
Santos Cordon823fd3c2014-08-07 18:35:18 -0700667 private void setState(int newState) {
668 if (newState != Connection.STATE_ACTIVE &&
669 newState != Connection.STATE_HOLDING &&
670 newState != Connection.STATE_DISCONNECTED) {
671 Log.w(this, "Unsupported state transition for Conference call.",
672 Connection.stateToString(newState));
673 return;
674 }
675
676 if (mState != newState) {
677 int oldState = mState;
678 mState = newState;
679 for (Listener l : mListeners) {
680 l.onStateChanged(this, oldState, newState);
681 }
682 }
683 }
Ihab Awad50e35062014-09-30 09:17:03 -0700684
685 private final void clearConferenceableList() {
686 for (Connection c : mConferenceableConnections) {
687 c.removeConnectionListener(mConnectionDeathListener);
688 }
689 mConferenceableConnections.clear();
690 }
Rekha Kumar07366812015-03-24 16:42:31 -0700691
692 @Override
693 public String toString() {
694 return String.format(Locale.US,
695 "[State: %s,Capabilites: %s, VideoState: %s, VideoProvider: %s, ThisObject %s]",
696 Connection.stateToString(mState),
697 Call.Details.capabilitiesToString(mConnectionCapabilities),
698 getVideoState(),
699 getVideoProvider(),
700 super.toString());
701 }
Andrew Lee0f51da32015-04-16 13:11:55 -0700702
Andrew Leeedc625f2015-04-14 13:38:12 -0700703 /**
704 * Sets the label and icon status to display in the InCall UI.
705 *
706 * @param statusHints The status label and icon to set.
707 */
708 public final void setStatusHints(StatusHints statusHints) {
709 mStatusHints = statusHints;
710 for (Listener l : mListeners) {
711 l.onStatusHintsChanged(this, statusHints);
712 }
713 }
714
715 /**
716 * @return The status hints for this conference.
717 */
718 public final StatusHints getStatusHints() {
719 return mStatusHints;
720 }
Santos Cordon6b7f9552015-05-27 17:21:45 -0700721
722 /**
Tyler Gunndee56a82016-03-23 16:06:34 -0700723 * Replaces all the extras associated with this {@code Conference}.
724 * <p>
725 * New or existing keys are replaced in the {@code Conference} extras. Keys which are no longer
726 * in the new extras, but were present the last time {@code setExtras} was called are removed.
727 * <p>
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700728 * Alternatively you may use the {@link #putExtras(Bundle)}, and
729 * {@link #removeExtras(String...)} methods to modify the extras.
730 * <p>
Tyler Gunndee56a82016-03-23 16:06:34 -0700731 * 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 -0700732 * Keys should be fully qualified (e.g., com.example.extras.MY_EXTRA) to avoid conflicts.
Santos Cordon6b7f9552015-05-27 17:21:45 -0700733 *
Tyler Gunndee56a82016-03-23 16:06:34 -0700734 * @param extras The extras associated with this {@code Conference}.
Santos Cordon6b7f9552015-05-27 17:21:45 -0700735 */
736 public final void setExtras(@Nullable Bundle extras) {
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700737 // Keeping putExtras and removeExtras in the same lock so that this operation happens as a
738 // block instead of letting other threads put/remove while this method is running.
739 synchronized (mExtrasLock) {
740 // Add/replace any new or changed extras values.
741 putExtras(extras);
742 // If we have used "setExtras" in the past, compare the key set from the last invocation
743 // to the current one and remove any keys that went away.
744 if (mPreviousExtraKeys != null) {
745 List<String> toRemove = new ArrayList<String>();
746 for (String oldKey : mPreviousExtraKeys) {
747 if (extras == null || !extras.containsKey(oldKey)) {
748 toRemove.add(oldKey);
749 }
750 }
Tyler Gunndee56a82016-03-23 16:06:34 -0700751
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700752 if (!toRemove.isEmpty()) {
753 removeExtras(toRemove);
Tyler Gunndee56a82016-03-23 16:06:34 -0700754 }
755 }
756
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700757 // Track the keys the last time set called setExtras. This way, the next time setExtras
758 // is called we can see if the caller has removed any extras values.
759 if (mPreviousExtraKeys == null) {
760 mPreviousExtraKeys = new ArraySet<String>();
Tyler Gunndee56a82016-03-23 16:06:34 -0700761 }
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700762 mPreviousExtraKeys.clear();
763 if (extras != null) {
764 mPreviousExtraKeys.addAll(extras.keySet());
765 }
Tyler Gunna8fb8ab2016-03-29 10:24:22 -0700766 }
Tyler Gunndee56a82016-03-23 16:06:34 -0700767 }
768
769 /**
770 * Adds some extras to this {@link Conference}. Existing keys are replaced and new ones are
771 * added.
772 * <p>
773 * No assumptions should be made as to how an In-Call UI or service will handle these extras.
774 * Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
775 *
776 * @param extras The extras to add.
777 */
778 public final void putExtras(@NonNull Bundle extras) {
779 if (extras == null) {
780 return;
781 }
782
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700783 // Creating a Bundle clone so we don't have to synchronize on mExtrasLock while calling
784 // onExtrasChanged.
785 Bundle listenersBundle;
786 synchronized (mExtrasLock) {
787 if (mExtras == null) {
788 mExtras = new Bundle();
789 }
790 mExtras.putAll(extras);
791 listenersBundle = new Bundle(mExtras);
Tyler Gunndee56a82016-03-23 16:06:34 -0700792 }
Tyler Gunndee56a82016-03-23 16:06:34 -0700793
Santos Cordon6b7f9552015-05-27 17:21:45 -0700794 for (Listener l : mListeners) {
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700795 l.onExtrasChanged(this, new Bundle(listenersBundle));
Santos Cordon6b7f9552015-05-27 17:21:45 -0700796 }
797 }
798
799 /**
Tyler Gunndee56a82016-03-23 16:06:34 -0700800 * Adds a boolean extra to this {@link Conference}.
801 *
802 * @param key The extra key.
803 * @param value The value.
804 * @hide
805 */
806 public final void putExtra(String key, boolean value) {
807 Bundle newExtras = new Bundle();
808 newExtras.putBoolean(key, value);
809 putExtras(newExtras);
810 }
811
812 /**
813 * Adds an integer extra to this {@link Conference}.
814 *
815 * @param key The extra key.
816 * @param value The value.
817 * @hide
818 */
819 public final void putExtra(String key, int value) {
820 Bundle newExtras = new Bundle();
821 newExtras.putInt(key, value);
822 putExtras(newExtras);
823 }
824
825 /**
826 * Adds a string extra to this {@link Conference}.
827 *
828 * @param key The extra key.
829 * @param value The value.
830 * @hide
831 */
832 public final void putExtra(String key, String value) {
833 Bundle newExtras = new Bundle();
834 newExtras.putString(key, value);
835 putExtras(newExtras);
836 }
837
838 /**
Tyler Gunn071be6f2016-05-10 14:52:33 -0700839 * Removes extras from this {@link Conference}.
Tyler Gunndee56a82016-03-23 16:06:34 -0700840 *
Tyler Gunn071be6f2016-05-10 14:52:33 -0700841 * @param keys The keys of the extras to remove.
Tyler Gunndee56a82016-03-23 16:06:34 -0700842 */
843 public final void removeExtras(List<String> keys) {
844 if (keys == null || keys.isEmpty()) {
845 return;
846 }
847
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700848 synchronized (mExtrasLock) {
849 if (mExtras != null) {
850 for (String key : keys) {
851 mExtras.remove(key);
852 }
Tyler Gunndee56a82016-03-23 16:06:34 -0700853 }
854 }
855
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700856 List<String> unmodifiableKeys = Collections.unmodifiableList(keys);
Tyler Gunndee56a82016-03-23 16:06:34 -0700857 for (Listener l : mListeners) {
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700858 l.onExtrasRemoved(this, unmodifiableKeys);
Tyler Gunndee56a82016-03-23 16:06:34 -0700859 }
860 }
861
862 /**
Tyler Gunn071be6f2016-05-10 14:52:33 -0700863 * Removes extras from this {@link Conference}.
864 *
865 * @param keys The keys of the extras to remove.
866 */
867 public final void removeExtras(String ... keys) {
868 removeExtras(Arrays.asList(keys));
869 }
870
871 /**
Tyler Gunndee56a82016-03-23 16:06:34 -0700872 * Returns the extras associated with this conference.
Tyler Gunn2cbe2b52016-05-04 15:48:10 +0000873 * <p>
874 * Extras should be updated using {@link #putExtras(Bundle)} and {@link #removeExtras(List)}.
875 * <p>
876 * Telecom or an {@link InCallService} can also update the extras via
877 * {@link android.telecom.Call#putExtras(Bundle)}, and
878 * {@link Call#removeExtras(List)}.
879 * <p>
880 * The conference is notified of changes to the extras made by Telecom or an
881 * {@link InCallService} by {@link #onExtrasChanged(Bundle)}.
Tyler Gunndee56a82016-03-23 16:06:34 -0700882 *
883 * @return The extras associated with this connection.
Santos Cordon6b7f9552015-05-27 17:21:45 -0700884 */
885 public final Bundle getExtras() {
886 return mExtras;
887 }
Tyler Gunndee56a82016-03-23 16:06:34 -0700888
889 /**
890 * Notifies this {@link Conference} of a change to the extras made outside the
891 * {@link ConnectionService}.
892 * <p>
893 * These extras changes can originate from Telecom itself, or from an {@link InCallService} via
894 * {@link android.telecom.Call#putExtras(Bundle)}, and
895 * {@link Call#removeExtras(List)}.
896 *
897 * @param extras The new extras bundle.
898 */
899 public void onExtrasChanged(Bundle extras) {}
900
901 /**
902 * Handles a change to extras received from Telecom.
903 *
904 * @param extras The new extras.
905 * @hide
906 */
907 final void handleExtrasChanged(Bundle extras) {
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700908 Bundle b = null;
909 synchronized (mExtrasLock) {
910 mExtras = extras;
911 if (mExtras != null) {
912 b = new Bundle(mExtras);
913 }
914 }
915 onExtrasChanged(b);
Tyler Gunndee56a82016-03-23 16:06:34 -0700916 }
Santos Cordon823fd3c2014-08-07 18:35:18 -0700917}