blob: c2261c37e13dee28ffeb27d3a35dfd145ac404aa [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
Santos Cordon6b7f9552015-05-27 17:21:45 -070021import android.annotation.Nullable;
Yorke Lee4af59352015-05-13 14:14:54 -070022import android.annotation.SystemApi;
Santos Cordon6b7f9552015-05-27 17:21:45 -070023import android.os.Bundle;
Andrew Lee011728f2015-04-23 15:47:06 -070024import android.os.Handler;
Ihab Awadb8e85c72014-08-23 20:34:57 -070025import android.os.RemoteException;
Ihab Awadb8e85c72014-08-23 20:34:57 -070026
Ihab Awad50e35062014-09-30 09:17:03 -070027import java.util.ArrayList;
Ihab Awadb8e85c72014-08-23 20:34:57 -070028import java.util.Collections;
29import java.util.List;
30import java.util.Set;
31import java.util.concurrent.CopyOnWriteArrayList;
32import java.util.concurrent.CopyOnWriteArraySet;
33
34/**
Santos Cordonb804f8d2015-05-12 12:09:47 -070035 * A conference provided to a {@link ConnectionService} by another {@code ConnectionService}
36 * running in a different process.
37 *
38 * @see ConnectionService#onRemoteConferenceAdded
Ihab Awadb8e85c72014-08-23 20:34:57 -070039 */
40public final class RemoteConference {
41
Nancy Chen1d834f52014-09-05 11:03:21 -070042 public abstract static class Callback {
Ihab Awadb8e85c72014-08-23 20:34:57 -070043 public void onStateChanged(RemoteConference conference, int oldState, int newState) {}
Andrew Lee7f3d41f2014-09-11 17:33:16 -070044 public void onDisconnected(RemoteConference conference, DisconnectCause disconnectCause) {}
Ihab Awadb8e85c72014-08-23 20:34:57 -070045 public void onConnectionAdded(RemoteConference conference, RemoteConnection connection) {}
46 public void onConnectionRemoved(RemoteConference conference, RemoteConnection connection) {}
Ihab Awad5c9c86e2014-11-12 13:41:16 -080047 public void onConnectionCapabilitiesChanged(
48 RemoteConference conference,
49 int connectionCapabilities) {}
Ihab Awad50e35062014-09-30 09:17:03 -070050 public void onConferenceableConnectionsChanged(
51 RemoteConference conference,
52 List<RemoteConnection> conferenceableConnections) {}
Ihab Awadb8e85c72014-08-23 20:34:57 -070053 public void onDestroyed(RemoteConference conference) {}
Santos Cordon6b7f9552015-05-27 17:21:45 -070054 public void onExtrasChanged(RemoteConference conference, @Nullable Bundle extras) {}
Ihab Awadb8e85c72014-08-23 20:34:57 -070055 }
56
57 private final String mId;
58 private final IConnectionService mConnectionService;
59
Andrew Lee011728f2015-04-23 15:47:06 -070060 private final Set<CallbackRecord<Callback>> mCallbackRecords = new CopyOnWriteArraySet<>();
Ihab Awadb8e85c72014-08-23 20:34:57 -070061 private final List<RemoteConnection> mChildConnections = new CopyOnWriteArrayList<>();
62 private final List<RemoteConnection> mUnmodifiableChildConnections =
63 Collections.unmodifiableList(mChildConnections);
Ihab Awad50e35062014-09-30 09:17:03 -070064 private final List<RemoteConnection> mConferenceableConnections = new ArrayList<>();
65 private final List<RemoteConnection> mUnmodifiableConferenceableConnections =
66 Collections.unmodifiableList(mConferenceableConnections);
Ihab Awadb8e85c72014-08-23 20:34:57 -070067
68 private int mState = Connection.STATE_NEW;
Andrew Lee7f3d41f2014-09-11 17:33:16 -070069 private DisconnectCause mDisconnectCause;
Ihab Awad5c9c86e2014-11-12 13:41:16 -080070 private int mConnectionCapabilities;
Santos Cordon6b7f9552015-05-27 17:21:45 -070071 private Bundle mExtras;
Ihab Awadb8e85c72014-08-23 20:34:57 -070072
Santos Cordonb804f8d2015-05-12 12:09:47 -070073 /** @hide */
Ihab Awadb8e85c72014-08-23 20:34:57 -070074 RemoteConference(String id, IConnectionService connectionService) {
75 mId = id;
76 mConnectionService = connectionService;
77 }
78
Santos Cordonb804f8d2015-05-12 12:09:47 -070079 /** @hide */
Ihab Awadb8e85c72014-08-23 20:34:57 -070080 String getId() {
81 return mId;
82 }
83
Santos Cordonb804f8d2015-05-12 12:09:47 -070084 /** @hide */
Ihab Awadb8e85c72014-08-23 20:34:57 -070085 void setDestroyed() {
86 for (RemoteConnection connection : mChildConnections) {
87 connection.setConference(null);
88 }
Andrew Lee011728f2015-04-23 15:47:06 -070089 for (CallbackRecord<Callback> record : mCallbackRecords) {
90 final RemoteConference conference = this;
91 final Callback callback = record.getCallback();
92 record.getHandler().post(new Runnable() {
93 @Override
94 public void run() {
95 callback.onDestroyed(conference);
96 }
97 });
Ihab Awadb8e85c72014-08-23 20:34:57 -070098 }
99 }
100
Santos Cordonb804f8d2015-05-12 12:09:47 -0700101 /** @hide */
Andrew Lee011728f2015-04-23 15:47:06 -0700102 void setState(final int newState) {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700103 if (newState != Connection.STATE_ACTIVE &&
104 newState != Connection.STATE_HOLDING &&
105 newState != Connection.STATE_DISCONNECTED) {
106 Log.w(this, "Unsupported state transition for Conference call.",
107 Connection.stateToString(newState));
108 return;
109 }
110
111 if (mState != newState) {
Andrew Lee011728f2015-04-23 15:47:06 -0700112 final int oldState = mState;
Ihab Awadb8e85c72014-08-23 20:34:57 -0700113 mState = newState;
Andrew Lee011728f2015-04-23 15:47:06 -0700114 for (CallbackRecord<Callback> record : mCallbackRecords) {
115 final RemoteConference conference = this;
116 final Callback callback = record.getCallback();
117 record.getHandler().post(new Runnable() {
118 @Override
119 public void run() {
120 callback.onStateChanged(conference, oldState, newState);
121 }
122 });
Ihab Awadb8e85c72014-08-23 20:34:57 -0700123 }
124 }
125 }
126
Santos Cordonb804f8d2015-05-12 12:09:47 -0700127 /** @hide */
Andrew Lee011728f2015-04-23 15:47:06 -0700128 void addConnection(final RemoteConnection connection) {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700129 if (!mChildConnections.contains(connection)) {
130 mChildConnections.add(connection);
131 connection.setConference(this);
Andrew Lee011728f2015-04-23 15:47:06 -0700132 for (CallbackRecord<Callback> record : mCallbackRecords) {
133 final RemoteConference conference = this;
134 final Callback callback = record.getCallback();
135 record.getHandler().post(new Runnable() {
136 @Override
137 public void run() {
138 callback.onConnectionAdded(conference, connection);
139 }
140 });
Ihab Awadb8e85c72014-08-23 20:34:57 -0700141 }
142 }
143 }
144
Santos Cordonb804f8d2015-05-12 12:09:47 -0700145 /** @hide */
Andrew Lee011728f2015-04-23 15:47:06 -0700146 void removeConnection(final RemoteConnection connection) {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700147 if (mChildConnections.contains(connection)) {
148 mChildConnections.remove(connection);
149 connection.setConference(null);
Andrew Lee011728f2015-04-23 15:47:06 -0700150 for (CallbackRecord<Callback> record : mCallbackRecords) {
151 final RemoteConference conference = this;
152 final Callback callback = record.getCallback();
153 record.getHandler().post(new Runnable() {
154 @Override
155 public void run() {
156 callback.onConnectionRemoved(conference, connection);
157 }
158 });
Ihab Awadb8e85c72014-08-23 20:34:57 -0700159 }
160 }
161 }
162
Santos Cordonb804f8d2015-05-12 12:09:47 -0700163 /** @hide */
Andrew Lee011728f2015-04-23 15:47:06 -0700164 void setConnectionCapabilities(final int connectionCapabilities) {
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800165 if (mConnectionCapabilities != connectionCapabilities) {
166 mConnectionCapabilities = connectionCapabilities;
Andrew Lee011728f2015-04-23 15:47:06 -0700167 for (CallbackRecord<Callback> record : mCallbackRecords) {
168 final RemoteConference conference = this;
169 final Callback callback = record.getCallback();
170 record.getHandler().post(new Runnable() {
171 @Override
172 public void run() {
173 callback.onConnectionCapabilitiesChanged(
174 conference, mConnectionCapabilities);
175 }
176 });
Ihab Awadb8e85c72014-08-23 20:34:57 -0700177 }
178 }
179 }
180
Ihab Awad50e35062014-09-30 09:17:03 -0700181 /** @hide */
182 void setConferenceableConnections(List<RemoteConnection> conferenceableConnections) {
183 mConferenceableConnections.clear();
184 mConferenceableConnections.addAll(conferenceableConnections);
Andrew Lee011728f2015-04-23 15:47:06 -0700185 for (CallbackRecord<Callback> record : mCallbackRecords) {
186 final RemoteConference conference = this;
187 final Callback callback = record.getCallback();
188 record.getHandler().post(new Runnable() {
189 @Override
190 public void run() {
191 callback.onConferenceableConnectionsChanged(
192 conference, mUnmodifiableConferenceableConnections);
193 }
194 });
Ihab Awad50e35062014-09-30 09:17:03 -0700195 }
196 }
197
Santos Cordonb804f8d2015-05-12 12:09:47 -0700198 /** @hide */
Andrew Lee011728f2015-04-23 15:47:06 -0700199 void setDisconnected(final DisconnectCause disconnectCause) {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700200 if (mState != Connection.STATE_DISCONNECTED) {
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700201 mDisconnectCause = disconnectCause;
Ihab Awadb8e85c72014-08-23 20:34:57 -0700202 setState(Connection.STATE_DISCONNECTED);
Andrew Lee011728f2015-04-23 15:47:06 -0700203 for (CallbackRecord<Callback> record : mCallbackRecords) {
204 final RemoteConference conference = this;
205 final Callback callback = record.getCallback();
206 record.getHandler().post(new Runnable() {
207 @Override
208 public void run() {
209 callback.onDisconnected(conference, disconnectCause);
210 }
211 });
Ihab Awadb8e85c72014-08-23 20:34:57 -0700212 }
213 }
214 }
215
Santos Cordon6b7f9552015-05-27 17:21:45 -0700216 /** @hide */
217 void setExtras(final Bundle extras) {
218 mExtras = extras;
219 for (CallbackRecord<Callback> record : mCallbackRecords) {
220 final RemoteConference conference = this;
221 final Callback callback = record.getCallback();
222 record.getHandler().post(new Runnable() {
223 @Override
224 public void run() {
225 callback.onExtrasChanged(conference, extras);
226 }
227 });
228 }
229 }
230
Santos Cordonb804f8d2015-05-12 12:09:47 -0700231 /**
232 * Returns the list of {@link RemoteConnection}s contained in this conference.
233 *
234 * @return A list of child connections.
235 */
Ihab Awadb8e85c72014-08-23 20:34:57 -0700236 public final List<RemoteConnection> getConnections() {
237 return mUnmodifiableChildConnections;
238 }
239
Santos Cordonb804f8d2015-05-12 12:09:47 -0700240 /**
241 * Gets the state of the conference call. See {@link Connection} for valid values.
242 *
243 * @return A constant representing the state the conference call is currently in.
244 */
Ihab Awadb8e85c72014-08-23 20:34:57 -0700245 public final int getState() {
246 return mState;
247 }
248
Santos Cordonb804f8d2015-05-12 12:09:47 -0700249 /**
250 * Returns the capabilities of the conference. See {@code CAPABILITY_*} constants in class
251 * {@link Connection} for valid values.
252 *
253 * @return A bitmask of the capabilities of the conference call.
254 */
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800255 public final int getConnectionCapabilities() {
256 return mConnectionCapabilities;
Ihab Awadb8e85c72014-08-23 20:34:57 -0700257 }
258
Santos Cordonb804f8d2015-05-12 12:09:47 -0700259 /**
Santos Cordon6b7f9552015-05-27 17:21:45 -0700260 * Obtain the extras associated with this {@code RemoteConnection}.
261 *
262 * @return The extras for this connection.
263 */
264 public final Bundle getExtras() {
265 return mExtras;
266 }
267
268 /**
Santos Cordonb804f8d2015-05-12 12:09:47 -0700269 * Disconnects the conference call as well as the child {@link RemoteConnection}s.
270 */
Ihab Awadb8e85c72014-08-23 20:34:57 -0700271 public void disconnect() {
272 try {
273 mConnectionService.disconnect(mId);
274 } catch (RemoteException e) {
275 }
276 }
277
Santos Cordonb804f8d2015-05-12 12:09:47 -0700278 /**
279 * Removes the specified {@link RemoteConnection} from the conference. This causes the
280 * {@link RemoteConnection} to become a standalone connection. This is a no-op if the
281 * {@link RemoteConnection} does not belong to this conference.
282 *
283 * @param connection The remote-connection to remove.
284 */
Ihab Awadb8e85c72014-08-23 20:34:57 -0700285 public void separate(RemoteConnection connection) {
286 if (mChildConnections.contains(connection)) {
287 try {
288 mConnectionService.splitFromConference(connection.getId());
289 } catch (RemoteException e) {
290 }
291 }
292 }
293
Santos Cordonb804f8d2015-05-12 12:09:47 -0700294 /**
295 * Merges all {@link RemoteConnection}s of this conference into a single call. This should be
296 * invoked only if the conference contains the capability
297 * {@link Connection#CAPABILITY_MERGE_CONFERENCE}, otherwise it is a no-op. The presence of said
298 * capability indicates that the connections of this conference, despite being part of the
299 * same conference object, are yet to have their audio streams merged; this is a common pattern
300 * for CDMA conference calls, but the capability is not used for GSM and SIP conference calls.
301 * Invoking this method will cause the unmerged child connections to merge their audio
302 * streams.
303 */
mike dooley95ea5762014-09-25 14:49:21 -0700304 public void merge() {
305 try {
306 mConnectionService.mergeConference(mId);
307 } catch (RemoteException e) {
308 }
309 }
310
Santos Cordonb804f8d2015-05-12 12:09:47 -0700311 /**
312 * Swaps the active audio stream between the conference's child {@link RemoteConnection}s.
313 * This should be invoked only if the conference contains the capability
314 * {@link Connection#CAPABILITY_SWAP_CONFERENCE}, otherwise it is a no-op. This is only used by
315 * {@link ConnectionService}s that create conferences for connections that do not yet have
316 * their audio streams merged; this is a common pattern for CDMA conference calls, but the
317 * capability is not used for GSM and SIP conference calls. Invoking this method will change the
318 * active audio stream to a different child connection.
319 */
mike dooley95ea5762014-09-25 14:49:21 -0700320 public void swap() {
321 try {
322 mConnectionService.swapConference(mId);
323 } catch (RemoteException e) {
324 }
325 }
326
Santos Cordonb804f8d2015-05-12 12:09:47 -0700327 /**
328 * Puts the conference on hold.
329 */
Ihab Awadb8e85c72014-08-23 20:34:57 -0700330 public void hold() {
331 try {
332 mConnectionService.hold(mId);
333 } catch (RemoteException e) {
334 }
335 }
336
Santos Cordonb804f8d2015-05-12 12:09:47 -0700337 /**
338 * Unholds the conference call.
339 */
Ihab Awadb8e85c72014-08-23 20:34:57 -0700340 public void unhold() {
341 try {
342 mConnectionService.unhold(mId);
343 } catch (RemoteException e) {
344 }
345 }
346
Santos Cordonb804f8d2015-05-12 12:09:47 -0700347 /**
348 * Returns the {@link DisconnectCause} for the conference if it is in the state
349 * {@link Connection#STATE_DISCONNECTED}. If the conference is not disconnected, this will
350 * return null.
351 *
352 * @return The disconnect cause.
353 */
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700354 public DisconnectCause getDisconnectCause() {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700355 return mDisconnectCause;
356 }
357
Santos Cordonb804f8d2015-05-12 12:09:47 -0700358 /**
359 * Requests that the conference start playing the specified DTMF tone.
360 *
361 * @param digit The digit for which to play a DTMF tone.
362 */
Yorke Lee58bacc52014-09-16 10:43:06 -0700363 public void playDtmfTone(char digit) {
364 try {
365 mConnectionService.playDtmfTone(mId, digit);
366 } catch (RemoteException e) {
367 }
368 }
369
Santos Cordonb804f8d2015-05-12 12:09:47 -0700370 /**
371 * Stops the most recent request to play a DTMF tone.
372 *
373 * @see #playDtmfTone
374 */
Yorke Lee58bacc52014-09-16 10:43:06 -0700375 public void stopDtmfTone() {
376 try {
377 mConnectionService.stopDtmfTone(mId);
378 } catch (RemoteException e) {
379 }
380 }
381
Santos Cordonb804f8d2015-05-12 12:09:47 -0700382 /**
383 * Request to change the conference's audio routing to the specified state. The specified state
384 * can include audio routing (Bluetooth, Speaker, etc) and muting state.
385 *
386 * @see android.telecom.AudioState
Yorke Lee4af59352015-05-13 14:14:54 -0700387 * @deprecated Use {@link #setCallAudioState(CallAudioState)} instead.
388 * @hide
Santos Cordonb804f8d2015-05-12 12:09:47 -0700389 */
Yorke Lee4af59352015-05-13 14:14:54 -0700390 @SystemApi
391 @Deprecated
Yorke Lee58bacc52014-09-16 10:43:06 -0700392 public void setAudioState(AudioState state) {
Yorke Lee4af59352015-05-13 14:14:54 -0700393 setCallAudioState(new CallAudioState(state));
394 }
395
396 /**
397 * Request to change the conference's audio routing to the specified state. The specified state
398 * can include audio routing (Bluetooth, Speaker, etc) and muting state.
399 */
400 public void setCallAudioState(CallAudioState state) {
Yorke Lee58bacc52014-09-16 10:43:06 -0700401 try {
Yorke Lee4af59352015-05-13 14:14:54 -0700402 mConnectionService.onCallAudioStateChanged(mId, state);
Yorke Lee58bacc52014-09-16 10:43:06 -0700403 } catch (RemoteException e) {
404 }
405 }
406
Yorke Lee4af59352015-05-13 14:14:54 -0700407
Santos Cordonb804f8d2015-05-12 12:09:47 -0700408 /**
409 * Returns a list of independent connections that can me merged with this conference.
410 *
411 * @return A list of conferenceable connections.
412 */
Ihab Awad50e35062014-09-30 09:17:03 -0700413 public List<RemoteConnection> getConferenceableConnections() {
414 return mUnmodifiableConferenceableConnections;
415 }
416
Santos Cordonb804f8d2015-05-12 12:09:47 -0700417 /**
418 * Register a callback through which to receive state updates for this conference.
419 *
420 * @param callback The callback to notify of state changes.
421 */
Andrew Lee100e2932014-09-08 15:34:24 -0700422 public final void registerCallback(Callback callback) {
Andrew Lee011728f2015-04-23 15:47:06 -0700423 registerCallback(callback, new Handler());
424 }
425
Santos Cordonb804f8d2015-05-12 12:09:47 -0700426 /**
427 * Registers a callback through which to receive state updates for this conference.
428 * Callbacks will be notified using the specified handler, if provided.
429 *
430 * @param callback The callback to notify of state changes.
431 * @param handler The handler on which to execute the callbacks.
432 */
Andrew Lee011728f2015-04-23 15:47:06 -0700433 public final void registerCallback(Callback callback, Handler handler) {
434 unregisterCallback(callback);
435 if (callback != null && handler != null) {
436 mCallbackRecords.add(new CallbackRecord(callback, handler));
437 }
Ihab Awadb8e85c72014-08-23 20:34:57 -0700438 }
439
Santos Cordonb804f8d2015-05-12 12:09:47 -0700440 /**
441 * Unregisters a previously registered callback.
442 *
443 * @see #registerCallback
444 *
445 * @param callback The callback to unregister.
446 */
Andrew Lee100e2932014-09-08 15:34:24 -0700447 public final void unregisterCallback(Callback callback) {
Andrew Lee011728f2015-04-23 15:47:06 -0700448 if (callback != null) {
449 for (CallbackRecord<Callback> record : mCallbackRecords) {
450 if (record.getCallback() == callback) {
451 mCallbackRecords.remove(record);
452 break;
453 }
454 }
455 }
Ihab Awadb8e85c72014-08-23 20:34:57 -0700456 }
457}