blob: b59584b585085eb3b2969cbc300b436cc47d135a [file] [log] [blame]
Ihab Awadb8e85c72014-08-23 20:34:57 -07001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Tyler Gunnef9f6f92014-09-12 22:16:17 -070017package android.telecom;
Ihab Awadb8e85c72014-08-23 20:34:57 -070018
Tyler Gunnef9f6f92014-09-12 22:16:17 -070019import com.android.internal.telecom.IConnectionService;
Ihab Awadb8e85c72014-08-23 20:34:57 -070020
Andrew Lee011728f2015-04-23 15:47:06 -070021import android.os.Handler;
Ihab Awadb8e85c72014-08-23 20:34:57 -070022import android.os.RemoteException;
Ihab Awadb8e85c72014-08-23 20:34:57 -070023
Ihab Awad50e35062014-09-30 09:17:03 -070024import java.util.ArrayList;
Ihab Awadb8e85c72014-08-23 20:34:57 -070025import java.util.Collections;
26import java.util.List;
27import java.util.Set;
28import java.util.concurrent.CopyOnWriteArrayList;
29import java.util.concurrent.CopyOnWriteArraySet;
30
31/**
Santos Cordonb804f8d2015-05-12 12:09:47 -070032 * A conference provided to a {@link ConnectionService} by another {@code ConnectionService}
33 * running in a different process.
34 *
35 * @see ConnectionService#onRemoteConferenceAdded
Ihab Awadb8e85c72014-08-23 20:34:57 -070036 */
37public final class RemoteConference {
38
Nancy Chen1d834f52014-09-05 11:03:21 -070039 public abstract static class Callback {
Ihab Awadb8e85c72014-08-23 20:34:57 -070040 public void onStateChanged(RemoteConference conference, int oldState, int newState) {}
Andrew Lee7f3d41f2014-09-11 17:33:16 -070041 public void onDisconnected(RemoteConference conference, DisconnectCause disconnectCause) {}
Ihab Awadb8e85c72014-08-23 20:34:57 -070042 public void onConnectionAdded(RemoteConference conference, RemoteConnection connection) {}
43 public void onConnectionRemoved(RemoteConference conference, RemoteConnection connection) {}
Ihab Awad5c9c86e2014-11-12 13:41:16 -080044 public void onConnectionCapabilitiesChanged(
45 RemoteConference conference,
46 int connectionCapabilities) {}
Ihab Awad50e35062014-09-30 09:17:03 -070047 public void onConferenceableConnectionsChanged(
48 RemoteConference conference,
49 List<RemoteConnection> conferenceableConnections) {}
Ihab Awadb8e85c72014-08-23 20:34:57 -070050 public void onDestroyed(RemoteConference conference) {}
51 }
52
53 private final String mId;
54 private final IConnectionService mConnectionService;
55
Andrew Lee011728f2015-04-23 15:47:06 -070056 private final Set<CallbackRecord<Callback>> mCallbackRecords = new CopyOnWriteArraySet<>();
Ihab Awadb8e85c72014-08-23 20:34:57 -070057 private final List<RemoteConnection> mChildConnections = new CopyOnWriteArrayList<>();
58 private final List<RemoteConnection> mUnmodifiableChildConnections =
59 Collections.unmodifiableList(mChildConnections);
Ihab Awad50e35062014-09-30 09:17:03 -070060 private final List<RemoteConnection> mConferenceableConnections = new ArrayList<>();
61 private final List<RemoteConnection> mUnmodifiableConferenceableConnections =
62 Collections.unmodifiableList(mConferenceableConnections);
Ihab Awadb8e85c72014-08-23 20:34:57 -070063
64 private int mState = Connection.STATE_NEW;
Andrew Lee7f3d41f2014-09-11 17:33:16 -070065 private DisconnectCause mDisconnectCause;
Ihab Awad5c9c86e2014-11-12 13:41:16 -080066 private int mConnectionCapabilities;
Ihab Awadb8e85c72014-08-23 20:34:57 -070067
Santos Cordonb804f8d2015-05-12 12:09:47 -070068 /** @hide */
Ihab Awadb8e85c72014-08-23 20:34:57 -070069 RemoteConference(String id, IConnectionService connectionService) {
70 mId = id;
71 mConnectionService = connectionService;
72 }
73
Santos Cordonb804f8d2015-05-12 12:09:47 -070074 /** @hide */
Ihab Awadb8e85c72014-08-23 20:34:57 -070075 String getId() {
76 return mId;
77 }
78
Santos Cordonb804f8d2015-05-12 12:09:47 -070079 /** @hide */
Ihab Awadb8e85c72014-08-23 20:34:57 -070080 void setDestroyed() {
81 for (RemoteConnection connection : mChildConnections) {
82 connection.setConference(null);
83 }
Andrew Lee011728f2015-04-23 15:47:06 -070084 for (CallbackRecord<Callback> record : mCallbackRecords) {
85 final RemoteConference conference = this;
86 final Callback callback = record.getCallback();
87 record.getHandler().post(new Runnable() {
88 @Override
89 public void run() {
90 callback.onDestroyed(conference);
91 }
92 });
Ihab Awadb8e85c72014-08-23 20:34:57 -070093 }
94 }
95
Santos Cordonb804f8d2015-05-12 12:09:47 -070096 /** @hide */
Andrew Lee011728f2015-04-23 15:47:06 -070097 void setState(final int newState) {
Ihab Awadb8e85c72014-08-23 20:34:57 -070098 if (newState != Connection.STATE_ACTIVE &&
99 newState != Connection.STATE_HOLDING &&
100 newState != Connection.STATE_DISCONNECTED) {
101 Log.w(this, "Unsupported state transition for Conference call.",
102 Connection.stateToString(newState));
103 return;
104 }
105
106 if (mState != newState) {
Andrew Lee011728f2015-04-23 15:47:06 -0700107 final int oldState = mState;
Ihab Awadb8e85c72014-08-23 20:34:57 -0700108 mState = newState;
Andrew Lee011728f2015-04-23 15:47:06 -0700109 for (CallbackRecord<Callback> record : mCallbackRecords) {
110 final RemoteConference conference = this;
111 final Callback callback = record.getCallback();
112 record.getHandler().post(new Runnable() {
113 @Override
114 public void run() {
115 callback.onStateChanged(conference, oldState, newState);
116 }
117 });
Ihab Awadb8e85c72014-08-23 20:34:57 -0700118 }
119 }
120 }
121
Santos Cordonb804f8d2015-05-12 12:09:47 -0700122 /** @hide */
Andrew Lee011728f2015-04-23 15:47:06 -0700123 void addConnection(final RemoteConnection connection) {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700124 if (!mChildConnections.contains(connection)) {
125 mChildConnections.add(connection);
126 connection.setConference(this);
Andrew Lee011728f2015-04-23 15:47:06 -0700127 for (CallbackRecord<Callback> record : mCallbackRecords) {
128 final RemoteConference conference = this;
129 final Callback callback = record.getCallback();
130 record.getHandler().post(new Runnable() {
131 @Override
132 public void run() {
133 callback.onConnectionAdded(conference, connection);
134 }
135 });
Ihab Awadb8e85c72014-08-23 20:34:57 -0700136 }
137 }
138 }
139
Santos Cordonb804f8d2015-05-12 12:09:47 -0700140 /** @hide */
Andrew Lee011728f2015-04-23 15:47:06 -0700141 void removeConnection(final RemoteConnection connection) {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700142 if (mChildConnections.contains(connection)) {
143 mChildConnections.remove(connection);
144 connection.setConference(null);
Andrew Lee011728f2015-04-23 15:47:06 -0700145 for (CallbackRecord<Callback> record : mCallbackRecords) {
146 final RemoteConference conference = this;
147 final Callback callback = record.getCallback();
148 record.getHandler().post(new Runnable() {
149 @Override
150 public void run() {
151 callback.onConnectionRemoved(conference, connection);
152 }
153 });
Ihab Awadb8e85c72014-08-23 20:34:57 -0700154 }
155 }
156 }
157
Santos Cordonb804f8d2015-05-12 12:09:47 -0700158 /** @hide */
Andrew Lee011728f2015-04-23 15:47:06 -0700159 void setConnectionCapabilities(final int connectionCapabilities) {
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800160 if (mConnectionCapabilities != connectionCapabilities) {
161 mConnectionCapabilities = connectionCapabilities;
Andrew Lee011728f2015-04-23 15:47:06 -0700162 for (CallbackRecord<Callback> record : mCallbackRecords) {
163 final RemoteConference conference = this;
164 final Callback callback = record.getCallback();
165 record.getHandler().post(new Runnable() {
166 @Override
167 public void run() {
168 callback.onConnectionCapabilitiesChanged(
169 conference, mConnectionCapabilities);
170 }
171 });
Ihab Awadb8e85c72014-08-23 20:34:57 -0700172 }
173 }
174 }
175
Ihab Awad50e35062014-09-30 09:17:03 -0700176 /** @hide */
177 void setConferenceableConnections(List<RemoteConnection> conferenceableConnections) {
178 mConferenceableConnections.clear();
179 mConferenceableConnections.addAll(conferenceableConnections);
Andrew Lee011728f2015-04-23 15:47:06 -0700180 for (CallbackRecord<Callback> record : mCallbackRecords) {
181 final RemoteConference conference = this;
182 final Callback callback = record.getCallback();
183 record.getHandler().post(new Runnable() {
184 @Override
185 public void run() {
186 callback.onConferenceableConnectionsChanged(
187 conference, mUnmodifiableConferenceableConnections);
188 }
189 });
Ihab Awad50e35062014-09-30 09:17:03 -0700190 }
191 }
192
Santos Cordonb804f8d2015-05-12 12:09:47 -0700193 /** @hide */
Andrew Lee011728f2015-04-23 15:47:06 -0700194 void setDisconnected(final DisconnectCause disconnectCause) {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700195 if (mState != Connection.STATE_DISCONNECTED) {
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700196 mDisconnectCause = disconnectCause;
Ihab Awadb8e85c72014-08-23 20:34:57 -0700197 setState(Connection.STATE_DISCONNECTED);
Andrew Lee011728f2015-04-23 15:47:06 -0700198 for (CallbackRecord<Callback> record : mCallbackRecords) {
199 final RemoteConference conference = this;
200 final Callback callback = record.getCallback();
201 record.getHandler().post(new Runnable() {
202 @Override
203 public void run() {
204 callback.onDisconnected(conference, disconnectCause);
205 }
206 });
Ihab Awadb8e85c72014-08-23 20:34:57 -0700207 }
208 }
209 }
210
Santos Cordonb804f8d2015-05-12 12:09:47 -0700211 /**
212 * Returns the list of {@link RemoteConnection}s contained in this conference.
213 *
214 * @return A list of child connections.
215 */
Ihab Awadb8e85c72014-08-23 20:34:57 -0700216 public final List<RemoteConnection> getConnections() {
217 return mUnmodifiableChildConnections;
218 }
219
Santos Cordonb804f8d2015-05-12 12:09:47 -0700220 /**
221 * Gets the state of the conference call. See {@link Connection} for valid values.
222 *
223 * @return A constant representing the state the conference call is currently in.
224 */
Ihab Awadb8e85c72014-08-23 20:34:57 -0700225 public final int getState() {
226 return mState;
227 }
228
Santos Cordonb804f8d2015-05-12 12:09:47 -0700229 /**
230 * Returns the capabilities of the conference. See {@code CAPABILITY_*} constants in class
231 * {@link Connection} for valid values.
232 *
233 * @return A bitmask of the capabilities of the conference call.
234 */
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800235 public final int getConnectionCapabilities() {
236 return mConnectionCapabilities;
Ihab Awadb8e85c72014-08-23 20:34:57 -0700237 }
238
Santos Cordonb804f8d2015-05-12 12:09:47 -0700239 /**
240 * Disconnects the conference call as well as the child {@link RemoteConnection}s.
241 */
Ihab Awadb8e85c72014-08-23 20:34:57 -0700242 public void disconnect() {
243 try {
244 mConnectionService.disconnect(mId);
245 } catch (RemoteException e) {
246 }
247 }
248
Santos Cordonb804f8d2015-05-12 12:09:47 -0700249 /**
250 * Removes the specified {@link RemoteConnection} from the conference. This causes the
251 * {@link RemoteConnection} to become a standalone connection. This is a no-op if the
252 * {@link RemoteConnection} does not belong to this conference.
253 *
254 * @param connection The remote-connection to remove.
255 */
Ihab Awadb8e85c72014-08-23 20:34:57 -0700256 public void separate(RemoteConnection connection) {
257 if (mChildConnections.contains(connection)) {
258 try {
259 mConnectionService.splitFromConference(connection.getId());
260 } catch (RemoteException e) {
261 }
262 }
263 }
264
Santos Cordonb804f8d2015-05-12 12:09:47 -0700265 /**
266 * Merges all {@link RemoteConnection}s of this conference into a single call. This should be
267 * invoked only if the conference contains the capability
268 * {@link Connection#CAPABILITY_MERGE_CONFERENCE}, otherwise it is a no-op. The presence of said
269 * capability indicates that the connections of this conference, despite being part of the
270 * same conference object, are yet to have their audio streams merged; this is a common pattern
271 * for CDMA conference calls, but the capability is not used for GSM and SIP conference calls.
272 * Invoking this method will cause the unmerged child connections to merge their audio
273 * streams.
274 */
mike dooley95ea5762014-09-25 14:49:21 -0700275 public void merge() {
276 try {
277 mConnectionService.mergeConference(mId);
278 } catch (RemoteException e) {
279 }
280 }
281
Santos Cordonb804f8d2015-05-12 12:09:47 -0700282 /**
283 * Swaps the active audio stream between the conference's child {@link RemoteConnection}s.
284 * This should be invoked only if the conference contains the capability
285 * {@link Connection#CAPABILITY_SWAP_CONFERENCE}, otherwise it is a no-op. This is only used by
286 * {@link ConnectionService}s that create conferences for connections that do not yet have
287 * their audio streams merged; this is a common pattern for CDMA conference calls, but the
288 * capability is not used for GSM and SIP conference calls. Invoking this method will change the
289 * active audio stream to a different child connection.
290 */
mike dooley95ea5762014-09-25 14:49:21 -0700291 public void swap() {
292 try {
293 mConnectionService.swapConference(mId);
294 } catch (RemoteException e) {
295 }
296 }
297
Santos Cordonb804f8d2015-05-12 12:09:47 -0700298 /**
299 * Puts the conference on hold.
300 */
Ihab Awadb8e85c72014-08-23 20:34:57 -0700301 public void hold() {
302 try {
303 mConnectionService.hold(mId);
304 } catch (RemoteException e) {
305 }
306 }
307
Santos Cordonb804f8d2015-05-12 12:09:47 -0700308 /**
309 * Unholds the conference call.
310 */
Ihab Awadb8e85c72014-08-23 20:34:57 -0700311 public void unhold() {
312 try {
313 mConnectionService.unhold(mId);
314 } catch (RemoteException e) {
315 }
316 }
317
Santos Cordonb804f8d2015-05-12 12:09:47 -0700318 /**
319 * Returns the {@link DisconnectCause} for the conference if it is in the state
320 * {@link Connection#STATE_DISCONNECTED}. If the conference is not disconnected, this will
321 * return null.
322 *
323 * @return The disconnect cause.
324 */
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700325 public DisconnectCause getDisconnectCause() {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700326 return mDisconnectCause;
327 }
328
Santos Cordonb804f8d2015-05-12 12:09:47 -0700329 /**
330 * Requests that the conference start playing the specified DTMF tone.
331 *
332 * @param digit The digit for which to play a DTMF tone.
333 */
Yorke Lee58bacc52014-09-16 10:43:06 -0700334 public void playDtmfTone(char digit) {
335 try {
336 mConnectionService.playDtmfTone(mId, digit);
337 } catch (RemoteException e) {
338 }
339 }
340
Santos Cordonb804f8d2015-05-12 12:09:47 -0700341 /**
342 * Stops the most recent request to play a DTMF tone.
343 *
344 * @see #playDtmfTone
345 */
Yorke Lee58bacc52014-09-16 10:43:06 -0700346 public void stopDtmfTone() {
347 try {
348 mConnectionService.stopDtmfTone(mId);
349 } catch (RemoteException e) {
350 }
351 }
352
Santos Cordonb804f8d2015-05-12 12:09:47 -0700353 /**
354 * Request to change the conference's audio routing to the specified state. The specified state
355 * can include audio routing (Bluetooth, Speaker, etc) and muting state.
356 *
357 * @see android.telecom.AudioState
358 */
Yorke Lee58bacc52014-09-16 10:43:06 -0700359 public void setAudioState(AudioState state) {
360 try {
361 mConnectionService.onAudioStateChanged(mId, state);
362 } catch (RemoteException e) {
363 }
364 }
365
Santos Cordonb804f8d2015-05-12 12:09:47 -0700366 /**
367 * Returns a list of independent connections that can me merged with this conference.
368 *
369 * @return A list of conferenceable connections.
370 */
Ihab Awad50e35062014-09-30 09:17:03 -0700371 public List<RemoteConnection> getConferenceableConnections() {
372 return mUnmodifiableConferenceableConnections;
373 }
374
Santos Cordonb804f8d2015-05-12 12:09:47 -0700375 /**
376 * Register a callback through which to receive state updates for this conference.
377 *
378 * @param callback The callback to notify of state changes.
379 */
Andrew Lee100e2932014-09-08 15:34:24 -0700380 public final void registerCallback(Callback callback) {
Andrew Lee011728f2015-04-23 15:47:06 -0700381 registerCallback(callback, new Handler());
382 }
383
Santos Cordonb804f8d2015-05-12 12:09:47 -0700384 /**
385 * Registers a callback through which to receive state updates for this conference.
386 * Callbacks will be notified using the specified handler, if provided.
387 *
388 * @param callback The callback to notify of state changes.
389 * @param handler The handler on which to execute the callbacks.
390 */
Andrew Lee011728f2015-04-23 15:47:06 -0700391 public final void registerCallback(Callback callback, Handler handler) {
392 unregisterCallback(callback);
393 if (callback != null && handler != null) {
394 mCallbackRecords.add(new CallbackRecord(callback, handler));
395 }
Ihab Awadb8e85c72014-08-23 20:34:57 -0700396 }
397
Santos Cordonb804f8d2015-05-12 12:09:47 -0700398 /**
399 * Unregisters a previously registered callback.
400 *
401 * @see #registerCallback
402 *
403 * @param callback The callback to unregister.
404 */
Andrew Lee100e2932014-09-08 15:34:24 -0700405 public final void unregisterCallback(Callback callback) {
Andrew Lee011728f2015-04-23 15:47:06 -0700406 if (callback != null) {
407 for (CallbackRecord<Callback> record : mCallbackRecords) {
408 if (record.getCallback() == callback) {
409 mCallbackRecords.remove(record);
410 break;
411 }
412 }
413 }
Ihab Awadb8e85c72014-08-23 20:34:57 -0700414 }
415}