blob: 15cb786fba1ce640561406a71532765793f17161 [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
Ihab Awad50e35062014-09-30 09:17:03 -070019import java.util.ArrayList;
Santos Cordon823fd3c2014-08-07 18:35:18 -070020import java.util.Collections;
Santos Cordon823fd3c2014-08-07 18:35:18 -070021import java.util.List;
22import java.util.Set;
23import java.util.concurrent.CopyOnWriteArrayList;
24import java.util.concurrent.CopyOnWriteArraySet;
25
26/**
27 * Represents a conference call which can contain any number of {@link Connection} objects.
28 */
29public abstract class Conference {
30
31 /** @hide */
32 public abstract static class Listener {
33 public void onStateChanged(Conference conference, int oldState, int newState) {}
Andrew Lee7f3d41f2014-09-11 17:33:16 -070034 public void onDisconnected(Conference conference, DisconnectCause disconnectCause) {}
Santos Cordon823fd3c2014-08-07 18:35:18 -070035 public void onConnectionAdded(Conference conference, Connection connection) {}
36 public void onConnectionRemoved(Conference conference, Connection connection) {}
Ihab Awad50e35062014-09-30 09:17:03 -070037 public void onConferenceableConnectionsChanged(
38 Conference conference, List<Connection> conferenceableConnections) {}
Santos Cordon823fd3c2014-08-07 18:35:18 -070039 public void onDestroyed(Conference conference) {}
40 public void onCapabilitiesChanged(Conference conference, int capabilities) {}
41 }
42
43 private final Set<Listener> mListeners = new CopyOnWriteArraySet<>();
44 private final List<Connection> mChildConnections = new CopyOnWriteArrayList<>();
Ihab Awadb8e85c72014-08-23 20:34:57 -070045 private final List<Connection> mUnmodifiableChildConnections =
Santos Cordon823fd3c2014-08-07 18:35:18 -070046 Collections.unmodifiableList(mChildConnections);
Ihab Awad50e35062014-09-30 09:17:03 -070047 private final List<Connection> mConferenceableConnections = new ArrayList<>();
48 private final List<Connection> mUnmodifiableConferenceableConnections =
49 Collections.unmodifiableList(mConferenceableConnections);
Santos Cordon823fd3c2014-08-07 18:35:18 -070050
51 private PhoneAccountHandle mPhoneAccount;
Yorke Leea0d3ca92014-09-15 19:18:13 -070052 private AudioState mAudioState;
Santos Cordon823fd3c2014-08-07 18:35:18 -070053 private int mState = Connection.STATE_NEW;
Andrew Lee7f3d41f2014-09-11 17:33:16 -070054 private DisconnectCause mDisconnectCause;
Santos Cordon823fd3c2014-08-07 18:35:18 -070055 private int mCapabilities;
56 private String mDisconnectMessage;
57
Ihab Awad50e35062014-09-30 09:17:03 -070058 private final Connection.Listener mConnectionDeathListener = new Connection.Listener() {
59 @Override
60 public void onDestroyed(Connection c) {
61 if (mConferenceableConnections.remove(c)) {
62 fireOnConferenceableConnectionsChanged();
63 }
64 }
65 };
66
Nancy Chen56fc25d2014-09-09 12:24:51 -070067 /**
68 * Constructs a new Conference with a mandatory {@link PhoneAccountHandle}
69 *
70 * @param phoneAccount The {@code PhoneAccountHandle} associated with the conference.
71 */
Santos Cordon823fd3c2014-08-07 18:35:18 -070072 public Conference(PhoneAccountHandle phoneAccount) {
73 mPhoneAccount = phoneAccount;
74 }
75
Nancy Chen56fc25d2014-09-09 12:24:51 -070076 /**
77 * Returns the {@link PhoneAccountHandle} the conference call is being placed through.
78 *
79 * @return A {@code PhoneAccountHandle} object representing the PhoneAccount of the conference.
80 */
Nancy Chenea38cca2014-09-05 16:38:49 -070081 public final PhoneAccountHandle getPhoneAccountHandle() {
Santos Cordon823fd3c2014-08-07 18:35:18 -070082 return mPhoneAccount;
83 }
84
Nancy Chen56fc25d2014-09-09 12:24:51 -070085 /**
86 * Returns the list of connections currently associated with the conference call.
87 *
88 * @return A list of {@code Connection} objects which represent the children of the conference.
89 */
Santos Cordon823fd3c2014-08-07 18:35:18 -070090 public final List<Connection> getConnections() {
Ihab Awadb8e85c72014-08-23 20:34:57 -070091 return mUnmodifiableChildConnections;
Santos Cordon823fd3c2014-08-07 18:35:18 -070092 }
93
Nancy Chen56fc25d2014-09-09 12:24:51 -070094 /**
95 * Gets the state of the conference call. See {@link Connection} for valid values.
96 *
97 * @return A constant representing the state the conference call is currently in.
98 */
Santos Cordon823fd3c2014-08-07 18:35:18 -070099 public final int getState() {
100 return mState;
101 }
102
Nancy Chen56fc25d2014-09-09 12:24:51 -0700103 /**
104 * Returns the capabilities of a conference. See {@link PhoneCapabilities} for valid values.
105 *
106 * @return A bitmask of the {@code PhoneCapabilities} of the conference call.
107 */
Santos Cordon823fd3c2014-08-07 18:35:18 -0700108 public final int getCapabilities() {
109 return mCapabilities;
110 }
111
112 /**
Yorke Leea0d3ca92014-09-15 19:18:13 -0700113 * @return The audio state of the conference, describing how its audio is currently
114 * being routed by the system. This is {@code null} if this Conference
115 * does not directly know about its audio state.
116 */
117 public final AudioState getAudioState() {
118 return mAudioState;
119 }
120
121 /**
Santos Cordon823fd3c2014-08-07 18:35:18 -0700122 * Invoked when the Conference and all it's {@link Connection}s should be disconnected.
123 */
124 public void onDisconnect() {}
125
126 /**
127 * Invoked when the specified {@link Connection} should be separated from the conference call.
128 *
129 * @param connection The connection to separate.
130 */
131 public void onSeparate(Connection connection) {}
132
133 /**
Ihab Awad50e35062014-09-30 09:17:03 -0700134 * Invoked when the specified {@link Connection} should merged with the conference call.
135 *
136 * @param connection The {@code Connection} to merge.
137 */
138 public void onMerge(Connection connection) {}
139
140 /**
Santos Cordon823fd3c2014-08-07 18:35:18 -0700141 * Invoked when the conference should be put on hold.
142 */
143 public void onHold() {}
144
145 /**
146 * Invoked when the conference should be moved from hold to active.
147 */
148 public void onUnhold() {}
149
150 /**
Santos Cordona4868042014-09-04 17:39:22 -0700151 * Invoked when the child calls should be merged. Only invoked if the conference contains the
152 * capability {@link PhoneCapabilities#MERGE_CONFERENCE}.
153 */
154 public void onMerge() {}
155
156 /**
157 * Invoked when the child calls should be swapped. Only invoked if the conference contains the
158 * capability {@link PhoneCapabilities#SWAP_CONFERENCE}.
159 */
160 public void onSwap() {}
161
162 /**
Yorke Leea0d3ca92014-09-15 19:18:13 -0700163 * Notifies this conference of a request to play a DTMF tone.
164 *
165 * @param c A DTMF character.
166 */
167 public void onPlayDtmfTone(char c) {}
168
169 /**
170 * Notifies this conference of a request to stop any currently playing DTMF tones.
171 */
172 public void onStopDtmfTone() {}
173
174 /**
175 * Notifies this conference that the {@link #getAudioState()} property has a new value.
176 *
177 * @param state The new call audio state.
178 */
179 public void onAudioStateChanged(AudioState state) {}
180
181 /**
Santos Cordon823fd3c2014-08-07 18:35:18 -0700182 * Sets state to be on hold.
183 */
184 public final void setOnHold() {
185 setState(Connection.STATE_HOLDING);
186 }
187
188 /**
189 * Sets state to be active.
190 */
191 public final void setActive() {
192 setState(Connection.STATE_ACTIVE);
193 }
194
195 /**
196 * Sets state to disconnected.
197 *
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700198 * @param disconnectCause The reason for the disconnection, as described by
199 * {@link android.telecom.DisconnectCause}.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700200 */
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700201 public final void setDisconnected(DisconnectCause disconnectCause) {
202 mDisconnectCause = disconnectCause;;
Santos Cordon823fd3c2014-08-07 18:35:18 -0700203 setState(Connection.STATE_DISCONNECTED);
204 for (Listener l : mListeners) {
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700205 l.onDisconnected(this, mDisconnectCause);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700206 }
207 }
208
209 /**
Nancy Chen56fc25d2014-09-09 12:24:51 -0700210 * Sets the capabilities of a conference. See {@link PhoneCapabilities} for valid values.
211 *
212 * @param capabilities A bitmask of the {@code PhoneCapabilities} of the conference call.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700213 */
214 public final void setCapabilities(int capabilities) {
215 if (capabilities != mCapabilities) {
216 mCapabilities = capabilities;
217
218 for (Listener l : mListeners) {
219 l.onCapabilitiesChanged(this, mCapabilities);
220 }
221 }
222 }
223
224 /**
225 * Adds the specified connection as a child of this conference.
226 *
227 * @param connection The connection to add.
228 * @return True if the connection was successfully added.
229 */
Santos Cordona4868042014-09-04 17:39:22 -0700230 public final boolean addConnection(Connection connection) {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700231 if (connection != null && !mChildConnections.contains(connection)) {
232 if (connection.setConference(this)) {
233 mChildConnections.add(connection);
234 for (Listener l : mListeners) {
235 l.onConnectionAdded(this, connection);
236 }
237 return true;
238 }
239 }
240 return false;
241 }
242
243 /**
244 * Removes the specified connection as a child of this conference.
245 *
246 * @param connection The connection to remove.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700247 */
Santos Cordona4868042014-09-04 17:39:22 -0700248 public final void removeConnection(Connection connection) {
Santos Cordon0159ac02014-08-21 14:28:11 -0700249 Log.d(this, "removing %s from %s", connection, mChildConnections);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700250 if (connection != null && mChildConnections.remove(connection)) {
251 connection.resetConference();
252 for (Listener l : mListeners) {
253 l.onConnectionRemoved(this, connection);
254 }
255 }
256 }
257
258 /**
Ihab Awad50e35062014-09-30 09:17:03 -0700259 * Sets the connections with which this connection can be conferenced.
260 *
261 * @param conferenceableConnections The set of connections this connection can conference with.
262 */
263 public final void setConferenceableConnections(List<Connection> conferenceableConnections) {
264 clearConferenceableList();
265 for (Connection c : conferenceableConnections) {
266 // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a
267 // small amount of items here.
268 if (!mConferenceableConnections.contains(c)) {
269 c.addConnectionListener(mConnectionDeathListener);
270 mConferenceableConnections.add(c);
271 }
272 }
273 fireOnConferenceableConnectionsChanged();
274 }
275
276 private final void fireOnConferenceableConnectionsChanged() {
277 for (Listener l : mListeners) {
278 l.onConferenceableConnectionsChanged(this, getConferenceableConnections());
279 }
280 }
281
282 /**
283 * Returns the connections with which this connection can be conferenced.
284 */
285 public final List<Connection> getConferenceableConnections() {
286 return mUnmodifiableConferenceableConnections;
287 }
288
289 /**
Nancy Chenea38cca2014-09-05 16:38:49 -0700290 * Tears down the conference object and any of its current connections.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700291 */
Santos Cordona4868042014-09-04 17:39:22 -0700292 public final void destroy() {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700293 Log.d(this, "destroying conference : %s", this);
294 // Tear down the children.
Santos Cordon0159ac02014-08-21 14:28:11 -0700295 for (Connection connection : mChildConnections) {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700296 Log.d(this, "removing connection %s", connection);
297 removeConnection(connection);
298 }
299
300 // If not yet disconnected, set the conference call as disconnected first.
301 if (mState != Connection.STATE_DISCONNECTED) {
302 Log.d(this, "setting to disconnected");
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700303 setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
Santos Cordon823fd3c2014-08-07 18:35:18 -0700304 }
305
306 // ...and notify.
307 for (Listener l : mListeners) {
308 l.onDestroyed(this);
309 }
310 }
311
312 /**
313 * Add a listener to be notified of a state change.
314 *
315 * @param listener The new listener.
316 * @return This conference.
317 * @hide
318 */
319 public final Conference addListener(Listener listener) {
320 mListeners.add(listener);
321 return this;
322 }
323
324 /**
325 * Removes the specified listener.
326 *
327 * @param listener The listener to remove.
328 * @return This conference.
329 * @hide
330 */
331 public final Conference removeListener(Listener listener) {
332 mListeners.remove(listener);
333 return this;
334 }
335
Yorke Leea0d3ca92014-09-15 19:18:13 -0700336 /**
337 * Inform this Conference that the state of its audio output has been changed externally.
338 *
339 * @param state The new audio state.
340 * @hide
341 */
342 final void setAudioState(AudioState state) {
343 Log.d(this, "setAudioState %s", state);
344 mAudioState = state;
345 onAudioStateChanged(state);
346 }
347
Santos Cordon823fd3c2014-08-07 18:35:18 -0700348 private void setState(int newState) {
349 if (newState != Connection.STATE_ACTIVE &&
350 newState != Connection.STATE_HOLDING &&
351 newState != Connection.STATE_DISCONNECTED) {
352 Log.w(this, "Unsupported state transition for Conference call.",
353 Connection.stateToString(newState));
354 return;
355 }
356
357 if (mState != newState) {
358 int oldState = mState;
359 mState = newState;
360 for (Listener l : mListeners) {
361 l.onStateChanged(this, oldState, newState);
362 }
363 }
364 }
Ihab Awad50e35062014-09-30 09:17:03 -0700365
366 private final void clearConferenceableList() {
367 for (Connection c : mConferenceableConnections) {
368 c.removeConnectionListener(mConnectionDeathListener);
369 }
370 mConferenceableConnections.clear();
371 }
Santos Cordon823fd3c2014-08-07 18:35:18 -0700372}