blob: 095a88feb41f31cc41c5aaeeb88d4bbd254be68c [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
Yorke Lee4af59352015-05-13 14:14:54 -070021import android.annotation.SystemApi;
Andrew Lee011728f2015-04-23 15:47:06 -070022import android.os.Handler;
Ihab Awadb8e85c72014-08-23 20:34:57 -070023import android.os.RemoteException;
Ihab Awadb8e85c72014-08-23 20:34:57 -070024
Ihab Awad50e35062014-09-30 09:17:03 -070025import java.util.ArrayList;
Ihab Awadb8e85c72014-08-23 20:34:57 -070026import java.util.Collections;
27import java.util.List;
28import java.util.Set;
29import java.util.concurrent.CopyOnWriteArrayList;
30import java.util.concurrent.CopyOnWriteArraySet;
31
32/**
Santos Cordonb804f8d2015-05-12 12:09:47 -070033 * A conference provided to a {@link ConnectionService} by another {@code ConnectionService}
34 * running in a different process.
35 *
36 * @see ConnectionService#onRemoteConferenceAdded
Ihab Awadb8e85c72014-08-23 20:34:57 -070037 */
38public final class RemoteConference {
39
Nancy Chen1d834f52014-09-05 11:03:21 -070040 public abstract static class Callback {
Ihab Awadb8e85c72014-08-23 20:34:57 -070041 public void onStateChanged(RemoteConference conference, int oldState, int newState) {}
Andrew Lee7f3d41f2014-09-11 17:33:16 -070042 public void onDisconnected(RemoteConference conference, DisconnectCause disconnectCause) {}
Ihab Awadb8e85c72014-08-23 20:34:57 -070043 public void onConnectionAdded(RemoteConference conference, RemoteConnection connection) {}
44 public void onConnectionRemoved(RemoteConference conference, RemoteConnection connection) {}
Ihab Awad5c9c86e2014-11-12 13:41:16 -080045 public void onConnectionCapabilitiesChanged(
46 RemoteConference conference,
47 int connectionCapabilities) {}
Ihab Awad50e35062014-09-30 09:17:03 -070048 public void onConferenceableConnectionsChanged(
49 RemoteConference conference,
50 List<RemoteConnection> conferenceableConnections) {}
Ihab Awadb8e85c72014-08-23 20:34:57 -070051 public void onDestroyed(RemoteConference conference) {}
52 }
53
54 private final String mId;
55 private final IConnectionService mConnectionService;
56
Andrew Lee011728f2015-04-23 15:47:06 -070057 private final Set<CallbackRecord<Callback>> mCallbackRecords = new CopyOnWriteArraySet<>();
Ihab Awadb8e85c72014-08-23 20:34:57 -070058 private final List<RemoteConnection> mChildConnections = new CopyOnWriteArrayList<>();
59 private final List<RemoteConnection> mUnmodifiableChildConnections =
60 Collections.unmodifiableList(mChildConnections);
Ihab Awad50e35062014-09-30 09:17:03 -070061 private final List<RemoteConnection> mConferenceableConnections = new ArrayList<>();
62 private final List<RemoteConnection> mUnmodifiableConferenceableConnections =
63 Collections.unmodifiableList(mConferenceableConnections);
Ihab Awadb8e85c72014-08-23 20:34:57 -070064
65 private int mState = Connection.STATE_NEW;
Andrew Lee7f3d41f2014-09-11 17:33:16 -070066 private DisconnectCause mDisconnectCause;
Ihab Awad5c9c86e2014-11-12 13:41:16 -080067 private int mConnectionCapabilities;
Ihab Awadb8e85c72014-08-23 20:34:57 -070068
Santos Cordonb804f8d2015-05-12 12:09:47 -070069 /** @hide */
Ihab Awadb8e85c72014-08-23 20:34:57 -070070 RemoteConference(String id, IConnectionService connectionService) {
71 mId = id;
72 mConnectionService = connectionService;
73 }
74
Santos Cordonb804f8d2015-05-12 12:09:47 -070075 /** @hide */
Ihab Awadb8e85c72014-08-23 20:34:57 -070076 String getId() {
77 return mId;
78 }
79
Santos Cordonb804f8d2015-05-12 12:09:47 -070080 /** @hide */
Ihab Awadb8e85c72014-08-23 20:34:57 -070081 void setDestroyed() {
82 for (RemoteConnection connection : mChildConnections) {
83 connection.setConference(null);
84 }
Andrew Lee011728f2015-04-23 15:47:06 -070085 for (CallbackRecord<Callback> record : mCallbackRecords) {
86 final RemoteConference conference = this;
87 final Callback callback = record.getCallback();
88 record.getHandler().post(new Runnable() {
89 @Override
90 public void run() {
91 callback.onDestroyed(conference);
92 }
93 });
Ihab Awadb8e85c72014-08-23 20:34:57 -070094 }
95 }
96
Santos Cordonb804f8d2015-05-12 12:09:47 -070097 /** @hide */
Andrew Lee011728f2015-04-23 15:47:06 -070098 void setState(final int newState) {
Ihab Awadb8e85c72014-08-23 20:34:57 -070099 if (newState != Connection.STATE_ACTIVE &&
100 newState != Connection.STATE_HOLDING &&
101 newState != Connection.STATE_DISCONNECTED) {
102 Log.w(this, "Unsupported state transition for Conference call.",
103 Connection.stateToString(newState));
104 return;
105 }
106
107 if (mState != newState) {
Andrew Lee011728f2015-04-23 15:47:06 -0700108 final int oldState = mState;
Ihab Awadb8e85c72014-08-23 20:34:57 -0700109 mState = newState;
Andrew Lee011728f2015-04-23 15:47:06 -0700110 for (CallbackRecord<Callback> record : mCallbackRecords) {
111 final RemoteConference conference = this;
112 final Callback callback = record.getCallback();
113 record.getHandler().post(new Runnable() {
114 @Override
115 public void run() {
116 callback.onStateChanged(conference, oldState, newState);
117 }
118 });
Ihab Awadb8e85c72014-08-23 20:34:57 -0700119 }
120 }
121 }
122
Santos Cordonb804f8d2015-05-12 12:09:47 -0700123 /** @hide */
Andrew Lee011728f2015-04-23 15:47:06 -0700124 void addConnection(final RemoteConnection connection) {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700125 if (!mChildConnections.contains(connection)) {
126 mChildConnections.add(connection);
127 connection.setConference(this);
Andrew Lee011728f2015-04-23 15:47:06 -0700128 for (CallbackRecord<Callback> record : mCallbackRecords) {
129 final RemoteConference conference = this;
130 final Callback callback = record.getCallback();
131 record.getHandler().post(new Runnable() {
132 @Override
133 public void run() {
134 callback.onConnectionAdded(conference, connection);
135 }
136 });
Ihab Awadb8e85c72014-08-23 20:34:57 -0700137 }
138 }
139 }
140
Santos Cordonb804f8d2015-05-12 12:09:47 -0700141 /** @hide */
Andrew Lee011728f2015-04-23 15:47:06 -0700142 void removeConnection(final RemoteConnection connection) {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700143 if (mChildConnections.contains(connection)) {
144 mChildConnections.remove(connection);
145 connection.setConference(null);
Andrew Lee011728f2015-04-23 15:47:06 -0700146 for (CallbackRecord<Callback> record : mCallbackRecords) {
147 final RemoteConference conference = this;
148 final Callback callback = record.getCallback();
149 record.getHandler().post(new Runnable() {
150 @Override
151 public void run() {
152 callback.onConnectionRemoved(conference, connection);
153 }
154 });
Ihab Awadb8e85c72014-08-23 20:34:57 -0700155 }
156 }
157 }
158
Santos Cordonb804f8d2015-05-12 12:09:47 -0700159 /** @hide */
Andrew Lee011728f2015-04-23 15:47:06 -0700160 void setConnectionCapabilities(final int connectionCapabilities) {
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800161 if (mConnectionCapabilities != connectionCapabilities) {
162 mConnectionCapabilities = connectionCapabilities;
Andrew Lee011728f2015-04-23 15:47:06 -0700163 for (CallbackRecord<Callback> record : mCallbackRecords) {
164 final RemoteConference conference = this;
165 final Callback callback = record.getCallback();
166 record.getHandler().post(new Runnable() {
167 @Override
168 public void run() {
169 callback.onConnectionCapabilitiesChanged(
170 conference, mConnectionCapabilities);
171 }
172 });
Ihab Awadb8e85c72014-08-23 20:34:57 -0700173 }
174 }
175 }
176
Ihab Awad50e35062014-09-30 09:17:03 -0700177 /** @hide */
178 void setConferenceableConnections(List<RemoteConnection> conferenceableConnections) {
179 mConferenceableConnections.clear();
180 mConferenceableConnections.addAll(conferenceableConnections);
Andrew Lee011728f2015-04-23 15:47:06 -0700181 for (CallbackRecord<Callback> record : mCallbackRecords) {
182 final RemoteConference conference = this;
183 final Callback callback = record.getCallback();
184 record.getHandler().post(new Runnable() {
185 @Override
186 public void run() {
187 callback.onConferenceableConnectionsChanged(
188 conference, mUnmodifiableConferenceableConnections);
189 }
190 });
Ihab Awad50e35062014-09-30 09:17:03 -0700191 }
192 }
193
Santos Cordonb804f8d2015-05-12 12:09:47 -0700194 /** @hide */
Andrew Lee011728f2015-04-23 15:47:06 -0700195 void setDisconnected(final DisconnectCause disconnectCause) {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700196 if (mState != Connection.STATE_DISCONNECTED) {
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700197 mDisconnectCause = disconnectCause;
Ihab Awadb8e85c72014-08-23 20:34:57 -0700198 setState(Connection.STATE_DISCONNECTED);
Andrew Lee011728f2015-04-23 15:47:06 -0700199 for (CallbackRecord<Callback> record : mCallbackRecords) {
200 final RemoteConference conference = this;
201 final Callback callback = record.getCallback();
202 record.getHandler().post(new Runnable() {
203 @Override
204 public void run() {
205 callback.onDisconnected(conference, disconnectCause);
206 }
207 });
Ihab Awadb8e85c72014-08-23 20:34:57 -0700208 }
209 }
210 }
211
Santos Cordonb804f8d2015-05-12 12:09:47 -0700212 /**
213 * Returns the list of {@link RemoteConnection}s contained in this conference.
214 *
215 * @return A list of child connections.
216 */
Ihab Awadb8e85c72014-08-23 20:34:57 -0700217 public final List<RemoteConnection> getConnections() {
218 return mUnmodifiableChildConnections;
219 }
220
Santos Cordonb804f8d2015-05-12 12:09:47 -0700221 /**
222 * Gets the state of the conference call. See {@link Connection} for valid values.
223 *
224 * @return A constant representing the state the conference call is currently in.
225 */
Ihab Awadb8e85c72014-08-23 20:34:57 -0700226 public final int getState() {
227 return mState;
228 }
229
Santos Cordonb804f8d2015-05-12 12:09:47 -0700230 /**
231 * Returns the capabilities of the conference. See {@code CAPABILITY_*} constants in class
232 * {@link Connection} for valid values.
233 *
234 * @return A bitmask of the capabilities of the conference call.
235 */
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800236 public final int getConnectionCapabilities() {
237 return mConnectionCapabilities;
Ihab Awadb8e85c72014-08-23 20:34:57 -0700238 }
239
Santos Cordonb804f8d2015-05-12 12:09:47 -0700240 /**
241 * Disconnects the conference call as well as the child {@link RemoteConnection}s.
242 */
Ihab Awadb8e85c72014-08-23 20:34:57 -0700243 public void disconnect() {
244 try {
245 mConnectionService.disconnect(mId);
246 } catch (RemoteException e) {
247 }
248 }
249
Santos Cordonb804f8d2015-05-12 12:09:47 -0700250 /**
251 * Removes the specified {@link RemoteConnection} from the conference. This causes the
252 * {@link RemoteConnection} to become a standalone connection. This is a no-op if the
253 * {@link RemoteConnection} does not belong to this conference.
254 *
255 * @param connection The remote-connection to remove.
256 */
Ihab Awadb8e85c72014-08-23 20:34:57 -0700257 public void separate(RemoteConnection connection) {
258 if (mChildConnections.contains(connection)) {
259 try {
260 mConnectionService.splitFromConference(connection.getId());
261 } catch (RemoteException e) {
262 }
263 }
264 }
265
Santos Cordonb804f8d2015-05-12 12:09:47 -0700266 /**
267 * Merges all {@link RemoteConnection}s of this conference into a single call. This should be
268 * invoked only if the conference contains the capability
269 * {@link Connection#CAPABILITY_MERGE_CONFERENCE}, otherwise it is a no-op. The presence of said
270 * capability indicates that the connections of this conference, despite being part of the
271 * same conference object, are yet to have their audio streams merged; this is a common pattern
272 * for CDMA conference calls, but the capability is not used for GSM and SIP conference calls.
273 * Invoking this method will cause the unmerged child connections to merge their audio
274 * streams.
275 */
mike dooley95ea5762014-09-25 14:49:21 -0700276 public void merge() {
277 try {
278 mConnectionService.mergeConference(mId);
279 } catch (RemoteException e) {
280 }
281 }
282
Santos Cordonb804f8d2015-05-12 12:09:47 -0700283 /**
284 * Swaps the active audio stream between the conference's child {@link RemoteConnection}s.
285 * This should be invoked only if the conference contains the capability
286 * {@link Connection#CAPABILITY_SWAP_CONFERENCE}, otherwise it is a no-op. This is only used by
287 * {@link ConnectionService}s that create conferences for connections that do not yet have
288 * their audio streams merged; this is a common pattern for CDMA conference calls, but the
289 * capability is not used for GSM and SIP conference calls. Invoking this method will change the
290 * active audio stream to a different child connection.
291 */
mike dooley95ea5762014-09-25 14:49:21 -0700292 public void swap() {
293 try {
294 mConnectionService.swapConference(mId);
295 } catch (RemoteException e) {
296 }
297 }
298
Santos Cordonb804f8d2015-05-12 12:09:47 -0700299 /**
300 * Puts the conference on hold.
301 */
Ihab Awadb8e85c72014-08-23 20:34:57 -0700302 public void hold() {
303 try {
304 mConnectionService.hold(mId);
305 } catch (RemoteException e) {
306 }
307 }
308
Santos Cordonb804f8d2015-05-12 12:09:47 -0700309 /**
310 * Unholds the conference call.
311 */
Ihab Awadb8e85c72014-08-23 20:34:57 -0700312 public void unhold() {
313 try {
314 mConnectionService.unhold(mId);
315 } catch (RemoteException e) {
316 }
317 }
318
Santos Cordonb804f8d2015-05-12 12:09:47 -0700319 /**
320 * Returns the {@link DisconnectCause} for the conference if it is in the state
321 * {@link Connection#STATE_DISCONNECTED}. If the conference is not disconnected, this will
322 * return null.
323 *
324 * @return The disconnect cause.
325 */
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700326 public DisconnectCause getDisconnectCause() {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700327 return mDisconnectCause;
328 }
329
Santos Cordonb804f8d2015-05-12 12:09:47 -0700330 /**
331 * Requests that the conference start playing the specified DTMF tone.
332 *
333 * @param digit The digit for which to play a DTMF tone.
334 */
Yorke Lee58bacc52014-09-16 10:43:06 -0700335 public void playDtmfTone(char digit) {
336 try {
337 mConnectionService.playDtmfTone(mId, digit);
338 } catch (RemoteException e) {
339 }
340 }
341
Santos Cordonb804f8d2015-05-12 12:09:47 -0700342 /**
343 * Stops the most recent request to play a DTMF tone.
344 *
345 * @see #playDtmfTone
346 */
Yorke Lee58bacc52014-09-16 10:43:06 -0700347 public void stopDtmfTone() {
348 try {
349 mConnectionService.stopDtmfTone(mId);
350 } catch (RemoteException e) {
351 }
352 }
353
Santos Cordonb804f8d2015-05-12 12:09:47 -0700354 /**
355 * Request to change the conference's audio routing to the specified state. The specified state
356 * can include audio routing (Bluetooth, Speaker, etc) and muting state.
357 *
358 * @see android.telecom.AudioState
Yorke Lee4af59352015-05-13 14:14:54 -0700359 * @deprecated Use {@link #setCallAudioState(CallAudioState)} instead.
360 * @hide
Santos Cordonb804f8d2015-05-12 12:09:47 -0700361 */
Yorke Lee4af59352015-05-13 14:14:54 -0700362 @SystemApi
363 @Deprecated
Yorke Lee58bacc52014-09-16 10:43:06 -0700364 public void setAudioState(AudioState state) {
Yorke Lee4af59352015-05-13 14:14:54 -0700365 setCallAudioState(new CallAudioState(state));
366 }
367
368 /**
369 * Request to change the conference's audio routing to the specified state. The specified state
370 * can include audio routing (Bluetooth, Speaker, etc) and muting state.
371 */
372 public void setCallAudioState(CallAudioState state) {
Yorke Lee58bacc52014-09-16 10:43:06 -0700373 try {
Yorke Lee4af59352015-05-13 14:14:54 -0700374 mConnectionService.onCallAudioStateChanged(mId, state);
Yorke Lee58bacc52014-09-16 10:43:06 -0700375 } catch (RemoteException e) {
376 }
377 }
378
Yorke Lee4af59352015-05-13 14:14:54 -0700379
Santos Cordonb804f8d2015-05-12 12:09:47 -0700380 /**
381 * Returns a list of independent connections that can me merged with this conference.
382 *
383 * @return A list of conferenceable connections.
384 */
Ihab Awad50e35062014-09-30 09:17:03 -0700385 public List<RemoteConnection> getConferenceableConnections() {
386 return mUnmodifiableConferenceableConnections;
387 }
388
Santos Cordonb804f8d2015-05-12 12:09:47 -0700389 /**
390 * Register a callback through which to receive state updates for this conference.
391 *
392 * @param callback The callback to notify of state changes.
393 */
Andrew Lee100e2932014-09-08 15:34:24 -0700394 public final void registerCallback(Callback callback) {
Andrew Lee011728f2015-04-23 15:47:06 -0700395 registerCallback(callback, new Handler());
396 }
397
Santos Cordonb804f8d2015-05-12 12:09:47 -0700398 /**
399 * Registers a callback through which to receive state updates for this conference.
400 * Callbacks will be notified using the specified handler, if provided.
401 *
402 * @param callback The callback to notify of state changes.
403 * @param handler The handler on which to execute the callbacks.
404 */
Andrew Lee011728f2015-04-23 15:47:06 -0700405 public final void registerCallback(Callback callback, Handler handler) {
406 unregisterCallback(callback);
407 if (callback != null && handler != null) {
408 mCallbackRecords.add(new CallbackRecord(callback, handler));
409 }
Ihab Awadb8e85c72014-08-23 20:34:57 -0700410 }
411
Santos Cordonb804f8d2015-05-12 12:09:47 -0700412 /**
413 * Unregisters a previously registered callback.
414 *
415 * @see #registerCallback
416 *
417 * @param callback The callback to unregister.
418 */
Andrew Lee100e2932014-09-08 15:34:24 -0700419 public final void unregisterCallback(Callback callback) {
Andrew Lee011728f2015-04-23 15:47:06 -0700420 if (callback != null) {
421 for (CallbackRecord<Callback> record : mCallbackRecords) {
422 if (record.getCallback() == callback) {
423 mCallbackRecords.remove(record);
424 break;
425 }
426 }
427 }
Ihab Awadb8e85c72014-08-23 20:34:57 -0700428 }
429}