blob: f84dd7b0bb170e13bd4926dacd6814ae2803dff9 [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 Gunnc9503d62020-01-27 10:30:51 -080019import static android.Manifest.permission.MODIFY_PHONE_STATE;
20
21import android.annotation.ElapsedRealtimeLong;
22import android.annotation.IntRange;
Tyler Gunndee56a82016-03-23 16:06:34 -070023import android.annotation.NonNull;
Santos Cordon6b7f9552015-05-27 17:21:45 -070024import android.annotation.Nullable;
Tyler Gunnc9503d62020-01-27 10:30:51 -080025import android.annotation.RequiresPermission;
Santos Cordon5d2e4f22015-05-12 12:32:51 -070026import android.annotation.SystemApi;
Tyler Gunn68a73a42018-10-03 15:38:57 -070027import android.net.Uri;
Santos Cordon6b7f9552015-05-27 17:21:45 -070028import android.os.Bundle;
Tyler Gunn3fa819c2017-08-04 09:27:26 -070029import android.os.SystemClock;
Rekha Kumar07366812015-03-24 16:42:31 -070030import android.telecom.Connection.VideoProvider;
Tyler Gunndee56a82016-03-23 16:06:34 -070031import android.util.ArraySet;
Evan Charlton0e094d92014-11-08 15:49:16 -080032
Ihab Awad50e35062014-09-30 09:17:03 -070033import java.util.ArrayList;
Tyler Gunn071be6f2016-05-10 14:52:33 -070034import java.util.Arrays;
Santos Cordon823fd3c2014-08-07 18:35:18 -070035import java.util.Collections;
Santos Cordon823fd3c2014-08-07 18:35:18 -070036import java.util.List;
Rekha Kumar07366812015-03-24 16:42:31 -070037import java.util.Locale;
Santos Cordon823fd3c2014-08-07 18:35:18 -070038import java.util.Set;
39import java.util.concurrent.CopyOnWriteArrayList;
40import java.util.concurrent.CopyOnWriteArraySet;
41
42/**
43 * Represents a conference call which can contain any number of {@link Connection} objects.
44 */
Yorke Leeabfcfdc2015-05-13 18:55:18 -070045public abstract class Conference extends Conferenceable {
Santos Cordon823fd3c2014-08-07 18:35:18 -070046
Tyler Gunncd5d33c2015-01-12 09:02:01 -080047 /**
48 * Used to indicate that the conference connection time is not specified. If not specified,
49 * Telecom will set the connect time.
50 */
Jay Shrauner164a0ac2015-04-14 18:16:10 -070051 public static final long CONNECT_TIME_NOT_SPECIFIED = 0;
Tyler Gunncd5d33c2015-01-12 09:02:01 -080052
Santos Cordon823fd3c2014-08-07 18:35:18 -070053 /** @hide */
Tyler Gunn5567d742019-10-31 13:04:37 -070054 abstract static class Listener {
Santos Cordon823fd3c2014-08-07 18:35:18 -070055 public void onStateChanged(Conference conference, int oldState, int newState) {}
Andrew Lee7f3d41f2014-09-11 17:33:16 -070056 public void onDisconnected(Conference conference, DisconnectCause disconnectCause) {}
Santos Cordon823fd3c2014-08-07 18:35:18 -070057 public void onConnectionAdded(Conference conference, Connection connection) {}
58 public void onConnectionRemoved(Conference conference, Connection connection) {}
Ihab Awad50e35062014-09-30 09:17:03 -070059 public void onConferenceableConnectionsChanged(
60 Conference conference, List<Connection> conferenceableConnections) {}
Santos Cordon823fd3c2014-08-07 18:35:18 -070061 public void onDestroyed(Conference conference) {}
Ihab Awad5c9c86e2014-11-12 13:41:16 -080062 public void onConnectionCapabilitiesChanged(
63 Conference conference, int connectionCapabilities) {}
Tyler Gunn720c6642016-03-22 09:02:47 -070064 public void onConnectionPropertiesChanged(
65 Conference conference, int connectionProperties) {}
Rekha Kumar07366812015-03-24 16:42:31 -070066 public void onVideoStateChanged(Conference c, int videoState) { }
67 public void onVideoProviderChanged(Conference c, Connection.VideoProvider videoProvider) {}
Andrew Leeedc625f2015-04-14 13:38:12 -070068 public void onStatusHintsChanged(Conference conference, StatusHints statusHints) {}
Tyler Gunndee56a82016-03-23 16:06:34 -070069 public void onExtrasChanged(Conference c, Bundle extras) {}
70 public void onExtrasRemoved(Conference c, List<String> keys) {}
Tyler Gunn68a73a42018-10-03 15:38:57 -070071 public void onConferenceStateChanged(Conference c, boolean isConference) {}
72 public void onAddressChanged(Conference c, Uri newAddress, int presentation) {}
Hall Liuc9bc1c62019-04-16 14:00:55 -070073 public void onConnectionEvent(Conference c, String event, Bundle extras) {}
Tyler Gunn68a73a42018-10-03 15:38:57 -070074 public void onCallerDisplayNameChanged(
75 Conference c, String callerDisplayName, int presentation) {}
Brad Ebingere0c12f42020-04-08 16:25:12 -070076 public void onCallDirectionChanged(Conference c, int callDirection) {}
Ravi Paluri80aa2142019-12-02 11:57:37 +053077 public void onRingbackRequested(Conference c, boolean ringback) {}
Santos Cordon823fd3c2014-08-07 18:35:18 -070078 }
79
80 private final Set<Listener> mListeners = new CopyOnWriteArraySet<>();
81 private final List<Connection> mChildConnections = new CopyOnWriteArrayList<>();
Ihab Awadb8e85c72014-08-23 20:34:57 -070082 private final List<Connection> mUnmodifiableChildConnections =
Santos Cordon823fd3c2014-08-07 18:35:18 -070083 Collections.unmodifiableList(mChildConnections);
Ihab Awad50e35062014-09-30 09:17:03 -070084 private final List<Connection> mConferenceableConnections = new ArrayList<>();
85 private final List<Connection> mUnmodifiableConferenceableConnections =
86 Collections.unmodifiableList(mConferenceableConnections);
Santos Cordon823fd3c2014-08-07 18:35:18 -070087
Jack Yu67140302015-12-10 12:27:58 -080088 private String mTelecomCallId;
Jay Shrauner164a0ac2015-04-14 18:16:10 -070089 private PhoneAccountHandle mPhoneAccount;
Yorke Lee4af59352015-05-13 14:14:54 -070090 private CallAudioState mCallAudioState;
Santos Cordon823fd3c2014-08-07 18:35:18 -070091 private int mState = Connection.STATE_NEW;
Andrew Lee7f3d41f2014-09-11 17:33:16 -070092 private DisconnectCause mDisconnectCause;
Ihab Awad5c9c86e2014-11-12 13:41:16 -080093 private int mConnectionCapabilities;
Tyler Gunn720c6642016-03-22 09:02:47 -070094 private int mConnectionProperties;
Santos Cordon823fd3c2014-08-07 18:35:18 -070095 private String mDisconnectMessage;
Tyler Gunncd5d33c2015-01-12 09:02:01 -080096 private long mConnectTimeMillis = CONNECT_TIME_NOT_SPECIFIED;
Tyler Gunn17541392018-02-01 08:58:38 -080097 private long mConnectionStartElapsedRealTime = CONNECT_TIME_NOT_SPECIFIED;
Andrew Leeedc625f2015-04-14 13:38:12 -070098 private StatusHints mStatusHints;
Santos Cordon6b7f9552015-05-27 17:21:45 -070099 private Bundle mExtras;
Tyler Gunndee56a82016-03-23 16:06:34 -0700100 private Set<String> mPreviousExtraKeys;
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700101 private final Object mExtrasLock = new Object();
Tyler Gunnac60f952019-05-31 07:23:16 -0700102 private Uri mAddress;
103 private int mAddressPresentation;
104 private String mCallerDisplayName;
105 private int mCallerDisplayNamePresentation;
Brad Ebingere0c12f42020-04-08 16:25:12 -0700106 private int mCallDirection;
Ravi Paluri80aa2142019-12-02 11:57:37 +0530107 private boolean mRingbackRequested = false;
Tyler Gunn10362372020-04-08 13:12:30 -0700108 private boolean mIsMultiparty = true;
Santos Cordon823fd3c2014-08-07 18:35:18 -0700109
Ihab Awad50e35062014-09-30 09:17:03 -0700110 private final Connection.Listener mConnectionDeathListener = new Connection.Listener() {
111 @Override
112 public void onDestroyed(Connection c) {
113 if (mConferenceableConnections.remove(c)) {
114 fireOnConferenceableConnectionsChanged();
115 }
116 }
117 };
118
Nancy Chen56fc25d2014-09-09 12:24:51 -0700119 /**
120 * Constructs a new Conference with a mandatory {@link PhoneAccountHandle}
121 *
122 * @param phoneAccount The {@code PhoneAccountHandle} associated with the conference.
123 */
Santos Cordon823fd3c2014-08-07 18:35:18 -0700124 public Conference(PhoneAccountHandle phoneAccount) {
125 mPhoneAccount = phoneAccount;
126 }
127
Nancy Chen56fc25d2014-09-09 12:24:51 -0700128 /**
Jack Yu67140302015-12-10 12:27:58 -0800129 * Returns the telecom internal call ID associated with this conference.
Tyler Gunn5567d742019-10-31 13:04:37 -0700130 * <p>
131 * Note: This is ONLY used for debugging purposes so that the Telephony stack can better
132 * associate logs in Telephony with those in Telecom.
133 * The ID returned should not be used for any other purpose.
Jack Yu67140302015-12-10 12:27:58 -0800134 *
135 * @return The telecom call ID.
136 * @hide
137 */
Tyler Gunn5567d742019-10-31 13:04:37 -0700138 @SystemApi
Tyler Gunn5567d742019-10-31 13:04:37 -0700139 public final @NonNull String getTelecomCallId() {
Jack Yu67140302015-12-10 12:27:58 -0800140 return mTelecomCallId;
141 }
142
143 /**
144 * Sets the telecom internal call ID associated with this conference.
145 *
146 * @param telecomCallId The telecom call ID.
147 * @hide
148 */
149 public final void setTelecomCallId(String telecomCallId) {
150 mTelecomCallId = telecomCallId;
151 }
152
153 /**
Nancy Chen56fc25d2014-09-09 12:24:51 -0700154 * Returns the {@link PhoneAccountHandle} the conference call is being placed through.
155 *
156 * @return A {@code PhoneAccountHandle} object representing the PhoneAccount of the conference.
157 */
Nancy Chenea38cca2014-09-05 16:38:49 -0700158 public final PhoneAccountHandle getPhoneAccountHandle() {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700159 return mPhoneAccount;
160 }
161
Nancy Chen56fc25d2014-09-09 12:24:51 -0700162 /**
163 * Returns the list of connections currently associated with the conference call.
164 *
165 * @return A list of {@code Connection} objects which represent the children of the conference.
166 */
Santos Cordon823fd3c2014-08-07 18:35:18 -0700167 public final List<Connection> getConnections() {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700168 return mUnmodifiableChildConnections;
Santos Cordon823fd3c2014-08-07 18:35:18 -0700169 }
170
Nancy Chen56fc25d2014-09-09 12:24:51 -0700171 /**
172 * Gets the state of the conference call. See {@link Connection} for valid values.
173 *
174 * @return A constant representing the state the conference call is currently in.
175 */
Santos Cordon823fd3c2014-08-07 18:35:18 -0700176 public final int getState() {
177 return mState;
178 }
179
Nancy Chen56fc25d2014-09-09 12:24:51 -0700180 /**
Ravi Paluri80aa2142019-12-02 11:57:37 +0530181 * Returns whether this conference is requesting that the system play a ringback tone
Grace Jia8587ee52020-07-10 15:42:32 -0700182 * on its behalf. A ringback tone may be played when an outgoing conference is in the process of
183 * connecting to give the user an audible indication of that process.
Ravi Paluri80aa2142019-12-02 11:57:37 +0530184 */
185 public final boolean isRingbackRequested() {
186 return mRingbackRequested;
187 }
188
189 /**
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700190 * Returns the capabilities of the conference. See {@code CAPABILITY_*} constants in class
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800191 * {@link Connection} for valid values.
Nancy Chen56fc25d2014-09-09 12:24:51 -0700192 *
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800193 * @return A bitmask of the capabilities of the conference call.
Nancy Chen56fc25d2014-09-09 12:24:51 -0700194 */
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800195 public final int getConnectionCapabilities() {
196 return mConnectionCapabilities;
197 }
198
199 /**
Tyler Gunn720c6642016-03-22 09:02:47 -0700200 * Returns the properties of the conference. See {@code PROPERTY_*} constants in class
201 * {@link Connection} for valid values.
202 *
203 * @return A bitmask of the properties of the conference call.
204 */
205 public final int getConnectionProperties() {
206 return mConnectionProperties;
207 }
208
209 /**
Yorke Leea0d3ca92014-09-15 19:18:13 -0700210 * @return The audio state of the conference, describing how its audio is currently
211 * being routed by the system. This is {@code null} if this Conference
212 * does not directly know about its audio state.
Yorke Lee4af59352015-05-13 14:14:54 -0700213 * @deprecated Use {@link #getCallAudioState()} instead.
214 * @hide
Yorke Leea0d3ca92014-09-15 19:18:13 -0700215 */
Yorke Lee4af59352015-05-13 14:14:54 -0700216 @Deprecated
217 @SystemApi
Yorke Leea0d3ca92014-09-15 19:18:13 -0700218 public final AudioState getAudioState() {
Yorke Lee4af59352015-05-13 14:14:54 -0700219 return new AudioState(mCallAudioState);
220 }
221
222 /**
223 * @return The audio state of the conference, describing how its audio is currently
224 * being routed by the system. This is {@code null} if this Conference
225 * does not directly know about its audio state.
226 */
227 public final CallAudioState getCallAudioState() {
228 return mCallAudioState;
Yorke Leea0d3ca92014-09-15 19:18:13 -0700229 }
230
231 /**
Rekha Kumar07366812015-03-24 16:42:31 -0700232 * Returns VideoProvider of the primary call. This can be null.
Rekha Kumar07366812015-03-24 16:42:31 -0700233 */
234 public VideoProvider getVideoProvider() {
235 return null;
236 }
237
238 /**
239 * Returns video state of the primary call.
Rekha Kumar07366812015-03-24 16:42:31 -0700240 */
241 public int getVideoState() {
Tyler Gunn87b73f32015-06-03 10:09:59 -0700242 return VideoProfile.STATE_AUDIO_ONLY;
Rekha Kumar07366812015-03-24 16:42:31 -0700243 }
244
245 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700246 * Notifies the {@link Conference} when the Conference and all it's {@link Connection}s should
247 * be disconnected.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700248 */
249 public void onDisconnect() {}
250
251 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700252 * Notifies the {@link Conference} when the specified {@link Connection} should be separated
253 * from the conference call.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700254 *
255 * @param connection The connection to separate.
256 */
257 public void onSeparate(Connection connection) {}
258
259 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700260 * Notifies the {@link Conference} when the specified {@link Connection} should merged with the
261 * conference call.
Ihab Awad50e35062014-09-30 09:17:03 -0700262 *
263 * @param connection The {@code Connection} to merge.
264 */
265 public void onMerge(Connection connection) {}
266
267 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700268 * Notifies the {@link Conference} when it should be put on hold.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700269 */
270 public void onHold() {}
271
272 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700273 * Notifies the {@link Conference} when it should be moved from a held to active state.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700274 */
275 public void onUnhold() {}
276
277 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700278 * Notifies the {@link Conference} when the child calls should be merged. Only invoked if the
279 * conference contains the capability {@link Connection#CAPABILITY_MERGE_CONFERENCE}.
Santos Cordona4868042014-09-04 17:39:22 -0700280 */
281 public void onMerge() {}
282
283 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700284 * Notifies the {@link Conference} when the child calls should be swapped. Only invoked if the
285 * conference contains the capability {@link Connection#CAPABILITY_SWAP_CONFERENCE}.
Santos Cordona4868042014-09-04 17:39:22 -0700286 */
287 public void onSwap() {}
288
289 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700290 * Notifies the {@link Conference} of a request to play a DTMF tone.
Yorke Leea0d3ca92014-09-15 19:18:13 -0700291 *
292 * @param c A DTMF character.
293 */
294 public void onPlayDtmfTone(char c) {}
295
296 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700297 * Notifies the {@link Conference} of a request to stop any currently playing DTMF tones.
Yorke Leea0d3ca92014-09-15 19:18:13 -0700298 */
299 public void onStopDtmfTone() {}
300
301 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700302 * Notifies the {@link Conference} that the {@link #getAudioState()} property has a new value.
Yorke Leea0d3ca92014-09-15 19:18:13 -0700303 *
304 * @param state The new call audio state.
Yorke Lee4af59352015-05-13 14:14:54 -0700305 * @deprecated Use {@link #onCallAudioStateChanged(CallAudioState)} instead.
306 * @hide
Yorke Leea0d3ca92014-09-15 19:18:13 -0700307 */
Yorke Lee4af59352015-05-13 14:14:54 -0700308 @SystemApi
309 @Deprecated
Yorke Leea0d3ca92014-09-15 19:18:13 -0700310 public void onAudioStateChanged(AudioState state) {}
311
312 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700313 * Notifies the {@link Conference} that the {@link #getCallAudioState()} property has a new
314 * value.
Yorke Lee4af59352015-05-13 14:14:54 -0700315 *
316 * @param state The new call audio state.
317 */
318 public void onCallAudioStateChanged(CallAudioState state) {}
319
320 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700321 * Notifies the {@link Conference} that a {@link Connection} has been added to it.
Andrew Lee46f7f5d2014-11-06 17:00:25 -0800322 *
323 * @param connection The newly added connection.
324 */
325 public void onConnectionAdded(Connection connection) {}
326
327 /**
Tyler Gunn0c62ef02020-02-11 14:39:43 -0800328 * Notifies the {@link Conference} of a request to add a new participants to the conference call
329 * @param participants that will be added to this conference call
Ravi Paluri404babb2020-01-23 19:02:44 +0530330 */
331 public void onAddConferenceParticipants(@NonNull List<Uri> participants) {}
332
333 /**
Ravi Paluri80aa2142019-12-02 11:57:37 +0530334 * Notifies this Conference, which is in {@code STATE_RINGING}, of
335 * a request to accept.
336 * For managed {@link ConnectionService}s, this will be called when the user answers a call via
337 * the default dialer's {@link InCallService}.
338 *
339 * @param videoState The video state in which to answer the connection.
340 */
Grace Jia41895152021-01-19 13:57:51 -0800341 public void onAnswer(@VideoProfile.VideoState int videoState) {}
Ravi Paluri80aa2142019-12-02 11:57:37 +0530342
343 /**
344 * Notifies this Conference, which is in {@code STATE_RINGING}, of
345 * a request to accept.
346 * For managed {@link ConnectionService}s, this will be called when the user answers a call via
347 * the default dialer's {@link InCallService}.
348 * @hide
349 */
350 public final void onAnswer() {
351 onAnswer(VideoProfile.STATE_AUDIO_ONLY);
352 }
353
354 /**
355 * Notifies this Conference, which is in {@code STATE_RINGING}, of
356 * a request to reject.
357 * For managed {@link ConnectionService}s, this will be called when the user rejects a call via
358 * the default dialer's {@link InCallService}.
359 */
360 public void onReject() {}
361
362 /**
Santos Cordon823fd3c2014-08-07 18:35:18 -0700363 * Sets state to be on hold.
364 */
365 public final void setOnHold() {
366 setState(Connection.STATE_HOLDING);
367 }
368
369 /**
Tyler Gunnd46595a2015-06-01 14:29:11 -0700370 * Sets state to be dialing.
371 */
372 public final void setDialing() {
373 setState(Connection.STATE_DIALING);
374 }
375
376 /**
Ravi Paluri80aa2142019-12-02 11:57:37 +0530377 * Sets state to be ringing.
378 */
379 public final void setRinging() {
380 setState(Connection.STATE_RINGING);
381 }
382
383 /**
Santos Cordon823fd3c2014-08-07 18:35:18 -0700384 * Sets state to be active.
385 */
386 public final void setActive() {
Ravi Paluri80aa2142019-12-02 11:57:37 +0530387 setRingbackRequested(false);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700388 setState(Connection.STATE_ACTIVE);
389 }
390
391 /**
392 * Sets state to disconnected.
393 *
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700394 * @param disconnectCause The reason for the disconnection, as described by
395 * {@link android.telecom.DisconnectCause}.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700396 */
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700397 public final void setDisconnected(DisconnectCause disconnectCause) {
398 mDisconnectCause = disconnectCause;;
Santos Cordon823fd3c2014-08-07 18:35:18 -0700399 setState(Connection.STATE_DISCONNECTED);
400 for (Listener l : mListeners) {
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700401 l.onDisconnected(this, mDisconnectCause);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700402 }
403 }
404
405 /**
mike dooley1cf14ac2014-11-04 10:59:53 -0800406 * @return The {@link DisconnectCause} for this connection.
407 */
408 public final DisconnectCause getDisconnectCause() {
409 return mDisconnectCause;
410 }
411
412 /**
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800413 * Sets the capabilities of a conference. See {@code CAPABILITY_*} constants of class
414 * {@link Connection} for valid values.
Nancy Chen56fc25d2014-09-09 12:24:51 -0700415 *
Tyler Gunn720c6642016-03-22 09:02:47 -0700416 * @param connectionCapabilities A bitmask of the {@code Capabilities} of the conference call.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700417 */
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800418 public final void setConnectionCapabilities(int connectionCapabilities) {
419 if (connectionCapabilities != mConnectionCapabilities) {
420 mConnectionCapabilities = connectionCapabilities;
Santos Cordon823fd3c2014-08-07 18:35:18 -0700421
422 for (Listener l : mListeners) {
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800423 l.onConnectionCapabilitiesChanged(this, mConnectionCapabilities);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700424 }
425 }
426 }
427
428 /**
Tyler Gunn720c6642016-03-22 09:02:47 -0700429 * Sets the properties of a conference. See {@code PROPERTY_*} constants of class
430 * {@link Connection} for valid values.
431 *
432 * @param connectionProperties A bitmask of the {@code Properties} of the conference call.
433 */
434 public final void setConnectionProperties(int connectionProperties) {
435 if (connectionProperties != mConnectionProperties) {
436 mConnectionProperties = connectionProperties;
437
438 for (Listener l : mListeners) {
439 l.onConnectionPropertiesChanged(this, mConnectionProperties);
440 }
441 }
442 }
443
444 /**
Santos Cordon823fd3c2014-08-07 18:35:18 -0700445 * Adds the specified connection as a child of this conference.
446 *
447 * @param connection The connection to add.
448 * @return True if the connection was successfully added.
449 */
Santos Cordona4868042014-09-04 17:39:22 -0700450 public final boolean addConnection(Connection connection) {
Rekha Kumar07366812015-03-24 16:42:31 -0700451 Log.d(this, "Connection=%s, connection=", connection);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700452 if (connection != null && !mChildConnections.contains(connection)) {
453 if (connection.setConference(this)) {
454 mChildConnections.add(connection);
Andrew Lee46f7f5d2014-11-06 17:00:25 -0800455 onConnectionAdded(connection);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700456 for (Listener l : mListeners) {
457 l.onConnectionAdded(this, connection);
458 }
459 return true;
460 }
461 }
462 return false;
463 }
464
465 /**
466 * Removes the specified connection as a child of this conference.
467 *
468 * @param connection The connection to remove.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700469 */
Santos Cordona4868042014-09-04 17:39:22 -0700470 public final void removeConnection(Connection connection) {
Santos Cordon0159ac02014-08-21 14:28:11 -0700471 Log.d(this, "removing %s from %s", connection, mChildConnections);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700472 if (connection != null && mChildConnections.remove(connection)) {
473 connection.resetConference();
474 for (Listener l : mListeners) {
475 l.onConnectionRemoved(this, connection);
476 }
477 }
478 }
479
480 /**
Ihab Awad50e35062014-09-30 09:17:03 -0700481 * Sets the connections with which this connection can be conferenced.
482 *
483 * @param conferenceableConnections The set of connections this connection can conference with.
484 */
485 public final void setConferenceableConnections(List<Connection> conferenceableConnections) {
486 clearConferenceableList();
487 for (Connection c : conferenceableConnections) {
488 // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a
489 // small amount of items here.
490 if (!mConferenceableConnections.contains(c)) {
491 c.addConnectionListener(mConnectionDeathListener);
492 mConferenceableConnections.add(c);
493 }
494 }
495 fireOnConferenceableConnectionsChanged();
496 }
497
Rekha Kumar07366812015-03-24 16:42:31 -0700498 /**
Ravi Paluri80aa2142019-12-02 11:57:37 +0530499 * Requests that the framework play a ringback tone. This is to be invoked by implementations
500 * that do not play a ringback tone themselves in the conference's audio stream.
501 *
502 * @param ringback Whether the ringback tone is to be played.
503 */
504 public final void setRingbackRequested(boolean ringback) {
505 if (mRingbackRequested != ringback) {
506 mRingbackRequested = ringback;
507 for (Listener l : mListeners) {
508 l.onRingbackRequested(this, ringback);
509 }
510 }
511 }
512
513 /**
Rekha Kumar07366812015-03-24 16:42:31 -0700514 * Set the video state for the conference.
Yorke Lee32f24732015-05-12 16:18:03 -0700515 * Valid values: {@link VideoProfile#STATE_AUDIO_ONLY},
516 * {@link VideoProfile#STATE_BIDIRECTIONAL},
517 * {@link VideoProfile#STATE_TX_ENABLED},
518 * {@link VideoProfile#STATE_RX_ENABLED}.
Rekha Kumar07366812015-03-24 16:42:31 -0700519 *
520 * @param videoState The new video state.
Rekha Kumar07366812015-03-24 16:42:31 -0700521 */
522 public final void setVideoState(Connection c, int videoState) {
523 Log.d(this, "setVideoState Conference: %s Connection: %s VideoState: %s",
524 this, c, videoState);
525 for (Listener l : mListeners) {
526 l.onVideoStateChanged(this, videoState);
527 }
528 }
529
530 /**
531 * Sets the video connection provider.
532 *
533 * @param videoProvider The video provider.
Rekha Kumar07366812015-03-24 16:42:31 -0700534 */
535 public final void setVideoProvider(Connection c, Connection.VideoProvider videoProvider) {
536 Log.d(this, "setVideoProvider Conference: %s Connection: %s VideoState: %s",
537 this, c, videoProvider);
538 for (Listener l : mListeners) {
539 l.onVideoProviderChanged(this, videoProvider);
540 }
541 }
542
Ihab Awad50e35062014-09-30 09:17:03 -0700543 private final void fireOnConferenceableConnectionsChanged() {
544 for (Listener l : mListeners) {
545 l.onConferenceableConnectionsChanged(this, getConferenceableConnections());
546 }
547 }
548
549 /**
550 * Returns the connections with which this connection can be conferenced.
551 */
552 public final List<Connection> getConferenceableConnections() {
553 return mUnmodifiableConferenceableConnections;
554 }
555
556 /**
Nancy Chenea38cca2014-09-05 16:38:49 -0700557 * Tears down the conference object and any of its current connections.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700558 */
Santos Cordona4868042014-09-04 17:39:22 -0700559 public final void destroy() {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700560 Log.d(this, "destroying conference : %s", this);
561 // Tear down the children.
Santos Cordon0159ac02014-08-21 14:28:11 -0700562 for (Connection connection : mChildConnections) {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700563 Log.d(this, "removing connection %s", connection);
564 removeConnection(connection);
565 }
566
567 // If not yet disconnected, set the conference call as disconnected first.
568 if (mState != Connection.STATE_DISCONNECTED) {
569 Log.d(this, "setting to disconnected");
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700570 setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
Santos Cordon823fd3c2014-08-07 18:35:18 -0700571 }
572
573 // ...and notify.
574 for (Listener l : mListeners) {
575 l.onDestroyed(this);
576 }
577 }
578
579 /**
580 * Add a listener to be notified of a state change.
581 *
582 * @param listener The new listener.
583 * @return This conference.
584 * @hide
585 */
Tyler Gunn5567d742019-10-31 13:04:37 -0700586 final Conference addListener(Listener listener) {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700587 mListeners.add(listener);
588 return this;
589 }
590
591 /**
592 * Removes the specified listener.
593 *
594 * @param listener The listener to remove.
595 * @return This conference.
596 * @hide
597 */
Tyler Gunn5567d742019-10-31 13:04:37 -0700598 final Conference removeListener(Listener listener) {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700599 mListeners.remove(listener);
600 return this;
601 }
602
Yorke Leea0d3ca92014-09-15 19:18:13 -0700603 /**
Tyler Gunn4a57b9b2014-10-30 14:27:48 -0700604 * Retrieves the primary connection associated with the conference. The primary connection is
605 * the connection from which the conference will retrieve its current state.
606 *
607 * @return The primary connection.
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700608 * @hide
Tyler Gunn4a57b9b2014-10-30 14:27:48 -0700609 */
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700610 @SystemApi
Santos Cordon4055d642015-05-12 14:19:24 -0700611 public Connection getPrimaryConnection() {
Tyler Gunn4a57b9b2014-10-30 14:27:48 -0700612 if (mUnmodifiableChildConnections == null || mUnmodifiableChildConnections.isEmpty()) {
613 return null;
614 }
615 return mUnmodifiableChildConnections.get(0);
616 }
617
618 /**
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700619 * @hide
620 * @deprecated Use {@link #setConnectionTime}.
Tyler Gunncd5d33c2015-01-12 09:02:01 -0800621 */
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700622 @Deprecated
623 @SystemApi
624 public final void setConnectTimeMillis(long connectTimeMillis) {
625 setConnectionTime(connectTimeMillis);
Tyler Gunncd5d33c2015-01-12 09:02:01 -0800626 }
627
628 /**
Tyler Gunn17541392018-02-01 08:58:38 -0800629 * Sets the connection start time of the {@code Conference}. This is used in the call log to
630 * indicate the date and time when the conference took place.
631 * <p>
632 * Should be specified in wall-clock time returned by {@link System#currentTimeMillis()}.
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700633 * <p>
634 * When setting the connection time, you should always set the connection elapsed time via
Tyler Gunnc9503d62020-01-27 10:30:51 -0800635 * {@link #setConnectionStartElapsedRealtimeMillis(long)} to ensure the duration is reflected.
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700636 *
Tyler Gunn17541392018-02-01 08:58:38 -0800637 * @param connectionTimeMillis The connection time, in milliseconds, as returned by
638 * {@link System#currentTimeMillis()}.
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700639 */
Tyler Gunnc9503d62020-01-27 10:30:51 -0800640 public final void setConnectionTime(@IntRange(from = 0) long connectionTimeMillis) {
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700641 mConnectTimeMillis = connectionTimeMillis;
642 }
643
644 /**
Tyler Gunn17541392018-02-01 08:58:38 -0800645 * Sets the start time of the {@link Conference} which is the basis for the determining the
646 * duration of the {@link Conference}.
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700647 * <p>
Tyler Gunn17541392018-02-01 08:58:38 -0800648 * You should use a value returned by {@link SystemClock#elapsedRealtime()} to ensure that time
649 * zone changes do not impact the conference duration.
650 * <p>
651 * When setting this, you should also set the connection time via
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700652 * {@link #setConnectionTime(long)}.
653 *
Tyler Gunn17541392018-02-01 08:58:38 -0800654 * @param connectionStartElapsedRealTime The connection time, as measured by
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700655 * {@link SystemClock#elapsedRealtime()}.
Tyler Gunnc9503d62020-01-27 10:30:51 -0800656 * @deprecated use {@link #setConnectionStartElapsedRealtimeMillis(long)} instead.
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700657 */
Tyler Gunnc9503d62020-01-27 10:30:51 -0800658 @Deprecated
Tyler Gunn17541392018-02-01 08:58:38 -0800659 public final void setConnectionStartElapsedRealTime(long connectionStartElapsedRealTime) {
Tyler Gunnc9503d62020-01-27 10:30:51 -0800660 setConnectionStartElapsedRealtimeMillis(connectionStartElapsedRealTime);
661 }
662
663 /**
664 * Sets the start time of the {@link Conference} which is the basis for the determining the
665 * duration of the {@link Conference}.
666 * <p>
667 * You should use a value returned by {@link SystemClock#elapsedRealtime()} to ensure that time
668 * zone changes do not impact the conference duration.
669 * <p>
670 * When setting this, you should also set the connection time via
671 * {@link #setConnectionTime(long)}.
672 *
673 * @param connectionStartElapsedRealTime The connection time, as measured by
674 * {@link SystemClock#elapsedRealtime()}.
675 */
676 public final void setConnectionStartElapsedRealtimeMillis(
677 @ElapsedRealtimeLong long connectionStartElapsedRealTime) {
Tyler Gunn17541392018-02-01 08:58:38 -0800678 mConnectionStartElapsedRealTime = connectionStartElapsedRealTime;
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700679 }
680
681 /**
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700682 * @hide
683 * @deprecated Use {@link #getConnectionTime}.
684 */
685 @Deprecated
686 @SystemApi
687 public final long getConnectTimeMillis() {
688 return getConnectionTime();
689 }
690
691 /**
692 * Retrieves the connection start time of the {@code Conference}, if specified. A value of
Tyler Gunncd5d33c2015-01-12 09:02:01 -0800693 * {@link #CONNECT_TIME_NOT_SPECIFIED} indicates that Telecom should determine the start time
694 * of the conference.
695 *
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700696 * @return The time at which the {@code Conference} was connected.
Tyler Gunncd5d33c2015-01-12 09:02:01 -0800697 */
Tyler Gunnc9503d62020-01-27 10:30:51 -0800698 public final @IntRange(from = 0) long getConnectionTime() {
Tyler Gunncd5d33c2015-01-12 09:02:01 -0800699 return mConnectTimeMillis;
700 }
701
702 /**
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700703 * Retrieves the connection start time of the {@link Conference}, if specified. A value of
704 * {@link #CONNECT_TIME_NOT_SPECIFIED} indicates that Telecom should determine the start time
705 * of the conference.
Tyler Gunn5567d742019-10-31 13:04:37 -0700706 * <p>
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700707 * This is based on the value of {@link SystemClock#elapsedRealtime()} to ensure that it is not
708 * impacted by wall clock changes (user initiated, network initiated, time zone change, etc).
Tyler Gunn5567d742019-10-31 13:04:37 -0700709 * <p>
710 * Note: This is only exposed for use by the Telephony framework which needs it to copy
711 * conference start times among conference participants. It is exposed as a system API since it
712 * has no general use other than to the Telephony framework.
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700713 *
714 * @return The elapsed time at which the {@link Conference} was connected.
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700715 */
Tyler Gunnc9503d62020-01-27 10:30:51 -0800716 public final @ElapsedRealtimeLong long getConnectionStartElapsedRealtimeMillis() {
Tyler Gunn17541392018-02-01 08:58:38 -0800717 return mConnectionStartElapsedRealTime;
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700718 }
719
720 /**
Yorke Leea0d3ca92014-09-15 19:18:13 -0700721 * Inform this Conference that the state of its audio output has been changed externally.
722 *
723 * @param state The new audio state.
724 * @hide
725 */
Yorke Lee4af59352015-05-13 14:14:54 -0700726 final void setCallAudioState(CallAudioState state) {
727 Log.d(this, "setCallAudioState %s", state);
728 mCallAudioState = state;
729 onAudioStateChanged(getAudioState());
730 onCallAudioStateChanged(state);
Yorke Leea0d3ca92014-09-15 19:18:13 -0700731 }
732
Santos Cordon823fd3c2014-08-07 18:35:18 -0700733 private void setState(int newState) {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700734 if (mState != newState) {
735 int oldState = mState;
736 mState = newState;
737 for (Listener l : mListeners) {
738 l.onStateChanged(this, oldState, newState);
739 }
740 }
741 }
Ihab Awad50e35062014-09-30 09:17:03 -0700742
Ravi Paluri80aa2142019-12-02 11:57:37 +0530743 private static class FailureSignalingConference extends Conference {
744 private boolean mImmutable = false;
745 public FailureSignalingConference(DisconnectCause disconnectCause,
746 PhoneAccountHandle phoneAccount) {
747 super(phoneAccount);
748 setDisconnected(disconnectCause);
749 mImmutable = true;
750 }
751 public void checkImmutable() {
752 if (mImmutable) {
753 throw new UnsupportedOperationException("Conference is immutable");
754 }
755 }
756 }
757
758 /**
759 * Return a {@code Conference} which represents a failed conference attempt. The returned
760 * {@code Conference} will have a {@link android.telecom.DisconnectCause} and as specified,
761 * and a {@link #getState()} of {@code STATE_DISCONNECTED}.
762 * <p>
763 * The returned {@code Conference} can be assumed to {@link #destroy()} itself when appropriate,
764 * so users of this method need not maintain a reference to its return value to destroy it.
765 *
766 * @param disconnectCause The disconnect cause, ({@see android.telecomm.DisconnectCause}).
767 * @return A {@code Conference} which indicates failure.
768 */
769 public @NonNull static Conference createFailedConference(
770 @NonNull DisconnectCause disconnectCause, @NonNull PhoneAccountHandle phoneAccount) {
771 return new FailureSignalingConference(disconnectCause, phoneAccount);
772 }
773
Ihab Awad50e35062014-09-30 09:17:03 -0700774 private final void clearConferenceableList() {
775 for (Connection c : mConferenceableConnections) {
776 c.removeConnectionListener(mConnectionDeathListener);
777 }
778 mConferenceableConnections.clear();
779 }
Rekha Kumar07366812015-03-24 16:42:31 -0700780
781 @Override
782 public String toString() {
783 return String.format(Locale.US,
Ravi Paluri80aa2142019-12-02 11:57:37 +0530784 "[State: %s,Capabilites: %s, VideoState: %s, VideoProvider: %s,"
785 + "isRingbackRequested: %s, ThisObject %s]",
Rekha Kumar07366812015-03-24 16:42:31 -0700786 Connection.stateToString(mState),
787 Call.Details.capabilitiesToString(mConnectionCapabilities),
788 getVideoState(),
789 getVideoProvider(),
Ravi Paluri80aa2142019-12-02 11:57:37 +0530790 isRingbackRequested() ? "Y" : "N",
Rekha Kumar07366812015-03-24 16:42:31 -0700791 super.toString());
792 }
Andrew Lee0f51da32015-04-16 13:11:55 -0700793
Andrew Leeedc625f2015-04-14 13:38:12 -0700794 /**
795 * Sets the label and icon status to display in the InCall UI.
796 *
797 * @param statusHints The status label and icon to set.
798 */
799 public final void setStatusHints(StatusHints statusHints) {
800 mStatusHints = statusHints;
801 for (Listener l : mListeners) {
802 l.onStatusHintsChanged(this, statusHints);
803 }
804 }
805
806 /**
807 * @return The status hints for this conference.
808 */
809 public final StatusHints getStatusHints() {
810 return mStatusHints;
811 }
Santos Cordon6b7f9552015-05-27 17:21:45 -0700812
813 /**
Tyler Gunndee56a82016-03-23 16:06:34 -0700814 * Replaces all the extras associated with this {@code Conference}.
815 * <p>
816 * New or existing keys are replaced in the {@code Conference} extras. Keys which are no longer
817 * in the new extras, but were present the last time {@code setExtras} was called are removed.
818 * <p>
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700819 * Alternatively you may use the {@link #putExtras(Bundle)}, and
820 * {@link #removeExtras(String...)} methods to modify the extras.
821 * <p>
Tyler Gunndee56a82016-03-23 16:06:34 -0700822 * 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 -0700823 * Keys should be fully qualified (e.g., com.example.extras.MY_EXTRA) to avoid conflicts.
Santos Cordon6b7f9552015-05-27 17:21:45 -0700824 *
Tyler Gunndee56a82016-03-23 16:06:34 -0700825 * @param extras The extras associated with this {@code Conference}.
Santos Cordon6b7f9552015-05-27 17:21:45 -0700826 */
827 public final void setExtras(@Nullable Bundle extras) {
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700828 // Keeping putExtras and removeExtras in the same lock so that this operation happens as a
829 // block instead of letting other threads put/remove while this method is running.
830 synchronized (mExtrasLock) {
831 // Add/replace any new or changed extras values.
832 putExtras(extras);
833 // If we have used "setExtras" in the past, compare the key set from the last invocation
834 // to the current one and remove any keys that went away.
835 if (mPreviousExtraKeys != null) {
836 List<String> toRemove = new ArrayList<String>();
837 for (String oldKey : mPreviousExtraKeys) {
838 if (extras == null || !extras.containsKey(oldKey)) {
839 toRemove.add(oldKey);
840 }
841 }
Tyler Gunndee56a82016-03-23 16:06:34 -0700842
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700843 if (!toRemove.isEmpty()) {
844 removeExtras(toRemove);
Tyler Gunndee56a82016-03-23 16:06:34 -0700845 }
846 }
847
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700848 // Track the keys the last time set called setExtras. This way, the next time setExtras
849 // is called we can see if the caller has removed any extras values.
850 if (mPreviousExtraKeys == null) {
851 mPreviousExtraKeys = new ArraySet<String>();
Tyler Gunndee56a82016-03-23 16:06:34 -0700852 }
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700853 mPreviousExtraKeys.clear();
854 if (extras != null) {
855 mPreviousExtraKeys.addAll(extras.keySet());
856 }
Tyler Gunna8fb8ab2016-03-29 10:24:22 -0700857 }
Tyler Gunndee56a82016-03-23 16:06:34 -0700858 }
859
860 /**
861 * Adds some extras to this {@link Conference}. Existing keys are replaced and new ones are
862 * added.
863 * <p>
864 * No assumptions should be made as to how an In-Call UI or service will handle these extras.
865 * Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
866 *
867 * @param extras The extras to add.
868 */
869 public final void putExtras(@NonNull Bundle extras) {
870 if (extras == null) {
871 return;
872 }
873
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700874 // Creating a Bundle clone so we don't have to synchronize on mExtrasLock while calling
875 // onExtrasChanged.
876 Bundle listenersBundle;
877 synchronized (mExtrasLock) {
878 if (mExtras == null) {
879 mExtras = new Bundle();
880 }
881 mExtras.putAll(extras);
882 listenersBundle = new Bundle(mExtras);
Tyler Gunndee56a82016-03-23 16:06:34 -0700883 }
Tyler Gunndee56a82016-03-23 16:06:34 -0700884
Santos Cordon6b7f9552015-05-27 17:21:45 -0700885 for (Listener l : mListeners) {
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700886 l.onExtrasChanged(this, new Bundle(listenersBundle));
Santos Cordon6b7f9552015-05-27 17:21:45 -0700887 }
888 }
889
890 /**
Tyler Gunndee56a82016-03-23 16:06:34 -0700891 * Adds a boolean extra to this {@link Conference}.
892 *
893 * @param key The extra key.
894 * @param value The value.
895 * @hide
896 */
897 public final void putExtra(String key, boolean value) {
898 Bundle newExtras = new Bundle();
899 newExtras.putBoolean(key, value);
900 putExtras(newExtras);
901 }
902
903 /**
904 * Adds an integer extra to this {@link Conference}.
905 *
906 * @param key The extra key.
907 * @param value The value.
908 * @hide
909 */
910 public final void putExtra(String key, int value) {
911 Bundle newExtras = new Bundle();
912 newExtras.putInt(key, value);
913 putExtras(newExtras);
914 }
915
916 /**
917 * Adds a string extra to this {@link Conference}.
918 *
919 * @param key The extra key.
920 * @param value The value.
921 * @hide
922 */
923 public final void putExtra(String key, String value) {
924 Bundle newExtras = new Bundle();
925 newExtras.putString(key, value);
926 putExtras(newExtras);
927 }
928
929 /**
Tyler Gunn071be6f2016-05-10 14:52:33 -0700930 * Removes extras from this {@link Conference}.
Tyler Gunndee56a82016-03-23 16:06:34 -0700931 *
Tyler Gunn071be6f2016-05-10 14:52:33 -0700932 * @param keys The keys of the extras to remove.
Tyler Gunndee56a82016-03-23 16:06:34 -0700933 */
934 public final void removeExtras(List<String> keys) {
935 if (keys == null || keys.isEmpty()) {
936 return;
937 }
938
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700939 synchronized (mExtrasLock) {
940 if (mExtras != null) {
941 for (String key : keys) {
942 mExtras.remove(key);
943 }
Tyler Gunndee56a82016-03-23 16:06:34 -0700944 }
945 }
946
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700947 List<String> unmodifiableKeys = Collections.unmodifiableList(keys);
Tyler Gunndee56a82016-03-23 16:06:34 -0700948 for (Listener l : mListeners) {
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700949 l.onExtrasRemoved(this, unmodifiableKeys);
Tyler Gunndee56a82016-03-23 16:06:34 -0700950 }
951 }
952
953 /**
Tyler Gunn071be6f2016-05-10 14:52:33 -0700954 * Removes extras from this {@link Conference}.
955 *
956 * @param keys The keys of the extras to remove.
957 */
958 public final void removeExtras(String ... keys) {
959 removeExtras(Arrays.asList(keys));
960 }
961
962 /**
Tyler Gunndee56a82016-03-23 16:06:34 -0700963 * Returns the extras associated with this conference.
Tyler Gunn2cbe2b52016-05-04 15:48:10 +0000964 * <p>
965 * Extras should be updated using {@link #putExtras(Bundle)} and {@link #removeExtras(List)}.
966 * <p>
967 * Telecom or an {@link InCallService} can also update the extras via
968 * {@link android.telecom.Call#putExtras(Bundle)}, and
969 * {@link Call#removeExtras(List)}.
970 * <p>
971 * The conference is notified of changes to the extras made by Telecom or an
972 * {@link InCallService} by {@link #onExtrasChanged(Bundle)}.
Tyler Gunndee56a82016-03-23 16:06:34 -0700973 *
974 * @return The extras associated with this connection.
Santos Cordon6b7f9552015-05-27 17:21:45 -0700975 */
976 public final Bundle getExtras() {
977 return mExtras;
978 }
Tyler Gunndee56a82016-03-23 16:06:34 -0700979
980 /**
981 * Notifies this {@link Conference} of a change to the extras made outside the
982 * {@link ConnectionService}.
983 * <p>
984 * These extras changes can originate from Telecom itself, or from an {@link InCallService} via
985 * {@link android.telecom.Call#putExtras(Bundle)}, and
986 * {@link Call#removeExtras(List)}.
987 *
988 * @param extras The new extras bundle.
989 */
990 public void onExtrasChanged(Bundle extras) {}
991
992 /**
Tyler Gunn10362372020-04-08 13:12:30 -0700993 * Set whether Telecom should treat this {@link Conference} as a multiparty conference call or
994 * if it should treat it as a single-party call.
Tyler Gunn68a73a42018-10-03 15:38:57 -0700995 * This method is used as part of a workaround regarding IMS conference calls and user
996 * expectation. In IMS, once a conference is formed, the UE is connected to an IMS conference
997 * server. If all participants of the conference drop out of the conference except for one, the
998 * UE is still connected to the IMS conference server. At this point, the user logically
999 * assumes they're no longer in a conference, yet the underlying network actually is.
1000 * To help provide a better user experiece, IMS conference calls can pretend to actually be a
1001 * single-party call when the participant count drops to 1. Although the dialer/phone app
1002 * could perform this trickery, it makes sense to do this in Telephony since a fix there will
1003 * ensure that bluetooth head units, auto and wearable apps all behave consistently.
Tyler Gunn5567d742019-10-31 13:04:37 -07001004 * <p>
1005 * This API is intended for use by the platform Telephony stack only.
Tyler Gunn68a73a42018-10-03 15:38:57 -07001006 *
1007 * @param isConference {@code true} if this {@link Conference} should be treated like a
1008 * conference call, {@code false} if it should be treated like a single-party call.
1009 * @hide
1010 */
Tyler Gunn5567d742019-10-31 13:04:37 -07001011 @SystemApi
Tyler Gunnc9503d62020-01-27 10:30:51 -08001012 @RequiresPermission(MODIFY_PHONE_STATE)
Tyler Gunn68a73a42018-10-03 15:38:57 -07001013 public void setConferenceState(boolean isConference) {
Tyler Gunn10362372020-04-08 13:12:30 -07001014 mIsMultiparty = isConference;
Tyler Gunn68a73a42018-10-03 15:38:57 -07001015 for (Listener l : mListeners) {
1016 l.onConferenceStateChanged(this, isConference);
1017 }
1018 }
1019
1020 /**
Brad Ebingere0c12f42020-04-08 16:25:12 -07001021 * Sets the call direction of this {@link Conference}. By default, all {@link Conference}s have
1022 * a direction of {@link android.telecom.Call.Details.CallDirection#DIRECTION_UNKNOWN}. The
1023 * direction of a {@link Conference} is only applicable to the case where
1024 * {@link #setConferenceState(boolean)} has been set to {@code false}, otherwise the direction
1025 * will be ignored.
1026 * @param callDirection The direction of the conference.
1027 * @hide
1028 */
1029 @RequiresPermission(MODIFY_PHONE_STATE)
1030 public final void setCallDirection(@Call.Details.CallDirection int callDirection) {
1031 Log.d(this, "setDirection %d", callDirection);
1032 mCallDirection = callDirection;
1033 for (Listener l : mListeners) {
1034 l.onCallDirectionChanged(this, callDirection);
1035 }
1036 }
1037
Tyler Gunn10362372020-04-08 13:12:30 -07001038 /**
1039 * Determines if the {@link Conference} is considered "multiparty" or not. By default all
1040 * conferences are considered multiparty. A multiparty conference is one where there are
1041 * multiple conference participants (other than the host) in the conference.
1042 * This is tied to {@link #setConferenceState(boolean)}, which is used for some use cases to
1043 * have a conference appear as if it is a standalone call, in which case the conference will
1044 * no longer be multiparty.
1045 * @return {@code true} if conference is treated as a conference (i.e. it is multiparty),
1046 * {@code false} if it should emulate a standalone call (i.e. not multiparty).
1047 * @hide
1048 */
1049 public boolean isMultiparty() {
1050 return mIsMultiparty;
1051 }
Brad Ebingere0c12f42020-04-08 16:25:12 -07001052
1053 /**
Tyler Gunn68a73a42018-10-03 15:38:57 -07001054 * Sets the address of this {@link Conference}. Used when {@link #setConferenceState(boolean)}
1055 * is called to mark a conference temporarily as NOT a conference.
Tyler Gunn5567d742019-10-31 13:04:37 -07001056 * <p>
1057 * Note: This is a Telephony-specific implementation detail related to IMS conferences. It is
1058 * not intended for use outside of the Telephony stack.
Tyler Gunn68a73a42018-10-03 15:38:57 -07001059 *
1060 * @param address The new address.
1061 * @param presentation The presentation requirements for the address.
1062 * See {@link TelecomManager} for valid values.
1063 * @hide
1064 */
Tyler Gunn5567d742019-10-31 13:04:37 -07001065 @SystemApi
Tyler Gunnc9503d62020-01-27 10:30:51 -08001066 @RequiresPermission(MODIFY_PHONE_STATE)
Tyler Gunn5567d742019-10-31 13:04:37 -07001067 public final void setAddress(@NonNull Uri address,
1068 @TelecomManager.Presentation int presentation) {
Tyler Gunn68a73a42018-10-03 15:38:57 -07001069 Log.d(this, "setAddress %s", address);
Tyler Gunnac60f952019-05-31 07:23:16 -07001070 mAddress = address;
1071 mAddressPresentation = presentation;
Tyler Gunn68a73a42018-10-03 15:38:57 -07001072 for (Listener l : mListeners) {
1073 l.onAddressChanged(this, address, presentation);
1074 }
1075 }
1076
1077 /**
Tyler Gunnac60f952019-05-31 07:23:16 -07001078 * Returns the "address" associated with the conference. This is applicable in two cases:
1079 * <ol>
1080 * <li>When {@link #setConferenceState(boolean)} is used to mark a conference as
1081 * temporarily "not a conference"; we need to present the correct address in the in-call
1082 * UI.</li>
1083 * <li>When the conference is not hosted on the current device, we need to know the address
1084 * information for the purpose of showing the original address to the user, as well as for
1085 * logging to the call log.</li>
1086 * </ol>
1087 * @return The address of the conference, or {@code null} if not applicable.
1088 * @hide
1089 */
1090 public final Uri getAddress() {
1091 return mAddress;
1092 }
1093
1094 /**
1095 * Returns the address presentation associated with the conference.
1096 * <p>
1097 * This is applicable in two cases:
1098 * <ol>
1099 * <li>When {@link #setConferenceState(boolean)} is used to mark a conference as
Brad Ebinger0ae44ed2020-04-09 15:30:57 -07001100 * temporarily "not a conference"; we need to present the correct address presentation in
1101 * the in-call UI.</li>
Tyler Gunnac60f952019-05-31 07:23:16 -07001102 * <li>When the conference is not hosted on the current device, we need to know the address
Brad Ebinger0ae44ed2020-04-09 15:30:57 -07001103 * presentation information for the purpose of showing the original address to the user, as
1104 * well as for logging to the call log.</li>
Tyler Gunnac60f952019-05-31 07:23:16 -07001105 * </ol>
Brad Ebinger0ae44ed2020-04-09 15:30:57 -07001106 * @return The address presentation of the conference.
Tyler Gunnac60f952019-05-31 07:23:16 -07001107 * @hide
1108 */
Brad Ebinger0ae44ed2020-04-09 15:30:57 -07001109 public final @TelecomManager.Presentation int getAddressPresentation() {
Tyler Gunnac60f952019-05-31 07:23:16 -07001110 return mAddressPresentation;
1111 }
1112
1113 /**
1114 * @return The caller display name (CNAP).
1115 * @hide
1116 */
1117 public final String getCallerDisplayName() {
1118 return mCallerDisplayName;
1119 }
1120
1121 /**
1122 * @return The presentation requirements for the handle.
1123 * See {@link TelecomManager} for valid values.
1124 * @hide
1125 */
1126 public final int getCallerDisplayNamePresentation() {
1127 return mCallerDisplayNamePresentation;
1128 }
1129
1130 /**
Brad Ebingere0c12f42020-04-08 16:25:12 -07001131 * @return The call direction of this conference. Only applicable when
1132 * {@link #setConferenceState(boolean)} is set to false.
1133 * @hide
1134 */
1135 public final @Call.Details.CallDirection int getCallDirection() {
1136 return mCallDirection;
1137 }
1138
1139 /**
Tyler Gunn68a73a42018-10-03 15:38:57 -07001140 * Sets the caller display name (CNAP) of this {@link Conference}. Used when
1141 * {@link #setConferenceState(boolean)} is called to mark a conference temporarily as NOT a
1142 * conference.
Tyler Gunn5567d742019-10-31 13:04:37 -07001143 * <p>
1144 * Note: This is a Telephony-specific implementation detail related to IMS conferences. It is
1145 * not intended for use outside of the Telephony stack.
Tyler Gunn68a73a42018-10-03 15:38:57 -07001146 *
1147 * @param callerDisplayName The new display name.
1148 * @param presentation The presentation requirements for the handle.
1149 * See {@link TelecomManager} for valid values.
1150 * @hide
1151 */
Tyler Gunn5567d742019-10-31 13:04:37 -07001152 @SystemApi
Tyler Gunn5567d742019-10-31 13:04:37 -07001153 public final void setCallerDisplayName(@NonNull String callerDisplayName,
1154 @TelecomManager.Presentation int presentation) {
Tyler Gunn68a73a42018-10-03 15:38:57 -07001155 Log.d(this, "setCallerDisplayName %s", callerDisplayName);
Tyler Gunnac60f952019-05-31 07:23:16 -07001156 mCallerDisplayName = callerDisplayName;
1157 mCallerDisplayNamePresentation = presentation;
Tyler Gunn68a73a42018-10-03 15:38:57 -07001158 for (Listener l : mListeners) {
1159 l.onCallerDisplayNameChanged(this, callerDisplayName, presentation);
1160 }
1161 }
1162
1163 /**
Tyler Gunndee56a82016-03-23 16:06:34 -07001164 * Handles a change to extras received from Telecom.
1165 *
1166 * @param extras The new extras.
1167 * @hide
1168 */
1169 final void handleExtrasChanged(Bundle extras) {
Brad Ebinger4fa6a012016-06-14 17:04:01 -07001170 Bundle b = null;
1171 synchronized (mExtrasLock) {
1172 mExtras = extras;
1173 if (mExtras != null) {
1174 b = new Bundle(mExtras);
1175 }
1176 }
1177 onExtrasChanged(b);
Tyler Gunndee56a82016-03-23 16:06:34 -07001178 }
Hall Liuc9bc1c62019-04-16 14:00:55 -07001179
1180 /**
Tyler Gunnc9503d62020-01-27 10:30:51 -08001181 * Sends an event associated with this {@link Conference} with associated event extras to the
1182 * {@link InCallService}.
1183 * <p>
1184 * Connection events are used to communicate point in time information from a
1185 * {@link ConnectionService} to an {@link InCallService} implementation. An example of a
1186 * custom connection event includes notifying the UI when a WIFI call has been handed over to
1187 * LTE, which the InCall UI might use to inform the user that billing charges may apply. The
1188 * Android Telephony framework will send the {@link Connection#EVENT_MERGE_COMPLETE}
1189 * connection event when a call to {@link Call#mergeConference()} has completed successfully.
1190 * <p>
1191 * Events are exposed to {@link InCallService} implementations via
1192 * {@link Call.Callback#onConnectionEvent(Call, String, Bundle)}.
1193 * <p>
1194 * No assumptions should be made as to how an In-Call UI or service will handle these events.
1195 * The {@link ConnectionService} must assume that the In-Call UI could even chose to ignore
1196 * some events altogether.
1197 * <p>
1198 * Events should be fully qualified (e.g. {@code com.example.event.MY_EVENT}) to avoid
1199 * conflicts between {@link ConnectionService} implementations. Further, custom
1200 * {@link ConnectionService} implementations shall not re-purpose events in the
1201 * {@code android.*} namespace, nor shall they define new event types in this namespace. When
1202 * defining a custom event type, ensure the contents of the extras {@link Bundle} is clearly
1203 * defined. Extra keys for this bundle should be named similar to the event type (e.g.
1204 * {@code com.example.extra.MY_EXTRA}).
1205 * <p>
1206 * When defining events and the associated extras, it is important to keep their behavior
1207 * consistent when the associated {@link ConnectionService} is updated. Support for deprecated
1208 * events/extras should me maintained to ensure backwards compatibility with older
1209 * {@link InCallService} implementations which were built to support the older behavior.
1210 * <p>
1211 * Expected connection events from the Telephony stack are:
1212 * <p>
1213 * <ul>
1214 * <li>{@link Connection#EVENT_CALL_HOLD_FAILED} with {@code null} {@code extras} when the
1215 * {@link Conference} could not be held.</li>
1216 * <li>{@link Connection#EVENT_MERGE_START} with {@code null} {@code extras} when a new
1217 * call is being merged into the conference.</li>
1218 * <li>{@link Connection#EVENT_MERGE_COMPLETE} with {@code null} {@code extras} a new call
1219 * has completed being merged into the conference.</li>
1220 * <li>{@link Connection#EVENT_CALL_MERGE_FAILED} with {@code null} {@code extras} a new
1221 * call has failed to merge into the conference (the dialer app can determine which call
1222 * failed to merge based on the fact that the call still exists outside of the conference
1223 * at the end of the merge process).</li>
1224 * </ul>
Tyler Gunn5567d742019-10-31 13:04:37 -07001225 *
Tyler Gunnc9503d62020-01-27 10:30:51 -08001226 * @param event The conference event.
Tyler Gunn5567d742019-10-31 13:04:37 -07001227 * @param extras Optional bundle containing extra information associated with the event.
Hall Liuc9bc1c62019-04-16 14:00:55 -07001228 */
Tyler Gunn5567d742019-10-31 13:04:37 -07001229 public void sendConferenceEvent(@NonNull String event, @Nullable Bundle extras) {
Hall Liuc9bc1c62019-04-16 14:00:55 -07001230 for (Listener l : mListeners) {
1231 l.onConnectionEvent(this, event, extras);
1232 }
1233 }
Santos Cordon823fd3c2014-08-07 18:35:18 -07001234}