blob: 7adcd467529730f12c1b56922da5233b320e9b73 [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;
Junhoedf3d822022-11-24 09:26:37 +000091 private CallEndpoint mCallEndpoint;
Santos Cordon823fd3c2014-08-07 18:35:18 -070092 private int mState = Connection.STATE_NEW;
Andrew Lee7f3d41f2014-09-11 17:33:16 -070093 private DisconnectCause mDisconnectCause;
Ihab Awad5c9c86e2014-11-12 13:41:16 -080094 private int mConnectionCapabilities;
Tyler Gunn720c6642016-03-22 09:02:47 -070095 private int mConnectionProperties;
Santos Cordon823fd3c2014-08-07 18:35:18 -070096 private String mDisconnectMessage;
Tyler Gunncd5d33c2015-01-12 09:02:01 -080097 private long mConnectTimeMillis = CONNECT_TIME_NOT_SPECIFIED;
Tyler Gunn17541392018-02-01 08:58:38 -080098 private long mConnectionStartElapsedRealTime = CONNECT_TIME_NOT_SPECIFIED;
Andrew Leeedc625f2015-04-14 13:38:12 -070099 private StatusHints mStatusHints;
Santos Cordon6b7f9552015-05-27 17:21:45 -0700100 private Bundle mExtras;
Tyler Gunndee56a82016-03-23 16:06:34 -0700101 private Set<String> mPreviousExtraKeys;
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700102 private final Object mExtrasLock = new Object();
Tyler Gunnac60f952019-05-31 07:23:16 -0700103 private Uri mAddress;
Grant Menkee2647142024-10-23 23:22:13 +0000104 private int mAddressPresentation = TelecomManager.PRESENTATION_UNKNOWN;
Tyler Gunnac60f952019-05-31 07:23:16 -0700105 private String mCallerDisplayName;
106 private int mCallerDisplayNamePresentation;
Brad Ebingere0c12f42020-04-08 16:25:12 -0700107 private int mCallDirection;
Ravi Paluri80aa2142019-12-02 11:57:37 +0530108 private boolean mRingbackRequested = false;
Tyler Gunn10362372020-04-08 13:12:30 -0700109 private boolean mIsMultiparty = true;
Santos Cordon823fd3c2014-08-07 18:35:18 -0700110
Ihab Awad50e35062014-09-30 09:17:03 -0700111 private final Connection.Listener mConnectionDeathListener = new Connection.Listener() {
112 @Override
113 public void onDestroyed(Connection c) {
114 if (mConferenceableConnections.remove(c)) {
115 fireOnConferenceableConnectionsChanged();
116 }
117 }
118 };
119
Nancy Chen56fc25d2014-09-09 12:24:51 -0700120 /**
121 * Constructs a new Conference with a mandatory {@link PhoneAccountHandle}
122 *
123 * @param phoneAccount The {@code PhoneAccountHandle} associated with the conference.
124 */
Santos Cordon823fd3c2014-08-07 18:35:18 -0700125 public Conference(PhoneAccountHandle phoneAccount) {
126 mPhoneAccount = phoneAccount;
127 }
128
Nancy Chen56fc25d2014-09-09 12:24:51 -0700129 /**
Jack Yu67140302015-12-10 12:27:58 -0800130 * Returns the telecom internal call ID associated with this conference.
Tyler Gunn5567d742019-10-31 13:04:37 -0700131 * <p>
132 * Note: This is ONLY used for debugging purposes so that the Telephony stack can better
133 * associate logs in Telephony with those in Telecom.
134 * The ID returned should not be used for any other purpose.
Jack Yu67140302015-12-10 12:27:58 -0800135 *
136 * @return The telecom call ID.
137 * @hide
138 */
Tyler Gunn5567d742019-10-31 13:04:37 -0700139 @SystemApi
Tyler Gunn5567d742019-10-31 13:04:37 -0700140 public final @NonNull String getTelecomCallId() {
Jack Yu67140302015-12-10 12:27:58 -0800141 return mTelecomCallId;
142 }
143
144 /**
145 * Sets the telecom internal call ID associated with this conference.
146 *
147 * @param telecomCallId The telecom call ID.
148 * @hide
149 */
150 public final void setTelecomCallId(String telecomCallId) {
151 mTelecomCallId = telecomCallId;
152 }
153
154 /**
Nancy Chen56fc25d2014-09-09 12:24:51 -0700155 * Returns the {@link PhoneAccountHandle} the conference call is being placed through.
156 *
157 * @return A {@code PhoneAccountHandle} object representing the PhoneAccount of the conference.
158 */
Nancy Chenea38cca2014-09-05 16:38:49 -0700159 public final PhoneAccountHandle getPhoneAccountHandle() {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700160 return mPhoneAccount;
161 }
162
Nancy Chen56fc25d2014-09-09 12:24:51 -0700163 /**
164 * Returns the list of connections currently associated with the conference call.
165 *
166 * @return A list of {@code Connection} objects which represent the children of the conference.
167 */
Santos Cordon823fd3c2014-08-07 18:35:18 -0700168 public final List<Connection> getConnections() {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700169 return mUnmodifiableChildConnections;
Santos Cordon823fd3c2014-08-07 18:35:18 -0700170 }
171
Nancy Chen56fc25d2014-09-09 12:24:51 -0700172 /**
173 * Gets the state of the conference call. See {@link Connection} for valid values.
174 *
175 * @return A constant representing the state the conference call is currently in.
176 */
Santos Cordon823fd3c2014-08-07 18:35:18 -0700177 public final int getState() {
178 return mState;
179 }
180
Nancy Chen56fc25d2014-09-09 12:24:51 -0700181 /**
Ravi Paluri80aa2142019-12-02 11:57:37 +0530182 * Returns whether this conference is requesting that the system play a ringback tone
Grace Jia8587ee52020-07-10 15:42:32 -0700183 * on its behalf. A ringback tone may be played when an outgoing conference is in the process of
184 * connecting to give the user an audible indication of that process.
Ravi Paluri80aa2142019-12-02 11:57:37 +0530185 */
186 public final boolean isRingbackRequested() {
187 return mRingbackRequested;
188 }
189
190 /**
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700191 * Returns the capabilities of the conference. See {@code CAPABILITY_*} constants in class
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800192 * {@link Connection} for valid values.
Nancy Chen56fc25d2014-09-09 12:24:51 -0700193 *
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800194 * @return A bitmask of the capabilities of the conference call.
Nancy Chen56fc25d2014-09-09 12:24:51 -0700195 */
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800196 public final int getConnectionCapabilities() {
197 return mConnectionCapabilities;
198 }
199
200 /**
Tyler Gunn720c6642016-03-22 09:02:47 -0700201 * Returns the properties of the conference. See {@code PROPERTY_*} constants in class
202 * {@link Connection} for valid values.
203 *
204 * @return A bitmask of the properties of the conference call.
205 */
206 public final int getConnectionProperties() {
207 return mConnectionProperties;
208 }
209
210 /**
Yorke Leea0d3ca92014-09-15 19:18:13 -0700211 * @return The audio state of the conference, describing how its audio is currently
212 * being routed by the system. This is {@code null} if this Conference
213 * does not directly know about its audio state.
Yorke Lee4af59352015-05-13 14:14:54 -0700214 * @deprecated Use {@link #getCallAudioState()} instead.
215 * @hide
Yorke Leea0d3ca92014-09-15 19:18:13 -0700216 */
Yorke Lee4af59352015-05-13 14:14:54 -0700217 @Deprecated
218 @SystemApi
Yorke Leea0d3ca92014-09-15 19:18:13 -0700219 public final AudioState getAudioState() {
Yorke Lee4af59352015-05-13 14:14:54 -0700220 return new AudioState(mCallAudioState);
221 }
222
223 /**
224 * @return The audio state of the conference, describing how its audio is currently
225 * being routed by the system. This is {@code null} if this Conference
226 * does not directly know about its audio state.
Junhoedf3d822022-11-24 09:26:37 +0000227 * @deprecated Use {@link #getCurrentCallEndpoint()},
228 * {@link #onAvailableCallEndpointsChanged(List)} and
229 * {@link #onMuteStateChanged(boolean)} instead.
Yorke Lee4af59352015-05-13 14:14:54 -0700230 */
Junhoedf3d822022-11-24 09:26:37 +0000231 @Deprecated
Yorke Lee4af59352015-05-13 14:14:54 -0700232 public final CallAudioState getCallAudioState() {
233 return mCallAudioState;
Yorke Leea0d3ca92014-09-15 19:18:13 -0700234 }
235
236 /**
Junhoedf3d822022-11-24 09:26:37 +0000237 * Obtains the current CallEndpoint.
238 *
239 * @return An object encapsulating the CallEndpoint.
240 */
241 @NonNull
242 public final CallEndpoint getCurrentCallEndpoint() {
243 return mCallEndpoint;
244 }
245
246 /**
Rekha Kumar07366812015-03-24 16:42:31 -0700247 * Returns VideoProvider of the primary call. This can be null.
Rekha Kumar07366812015-03-24 16:42:31 -0700248 */
249 public VideoProvider getVideoProvider() {
250 return null;
251 }
252
253 /**
254 * Returns video state of the primary call.
Rekha Kumar07366812015-03-24 16:42:31 -0700255 */
256 public int getVideoState() {
Tyler Gunn87b73f32015-06-03 10:09:59 -0700257 return VideoProfile.STATE_AUDIO_ONLY;
Rekha Kumar07366812015-03-24 16:42:31 -0700258 }
259
260 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700261 * Notifies the {@link Conference} when the Conference and all it's {@link Connection}s should
262 * be disconnected.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700263 */
264 public void onDisconnect() {}
265
266 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700267 * Notifies the {@link Conference} when the specified {@link Connection} should be separated
268 * from the conference call.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700269 *
270 * @param connection The connection to separate.
271 */
272 public void onSeparate(Connection connection) {}
273
274 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700275 * Notifies the {@link Conference} when the specified {@link Connection} should merged with the
276 * conference call.
Ihab Awad50e35062014-09-30 09:17:03 -0700277 *
278 * @param connection The {@code Connection} to merge.
279 */
280 public void onMerge(Connection connection) {}
281
282 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700283 * Notifies the {@link Conference} when it should be put on hold.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700284 */
285 public void onHold() {}
286
287 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700288 * Notifies the {@link Conference} when it should be moved from a held to active state.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700289 */
290 public void onUnhold() {}
291
292 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700293 * Notifies the {@link Conference} when the child calls should be merged. Only invoked if the
294 * conference contains the capability {@link Connection#CAPABILITY_MERGE_CONFERENCE}.
Santos Cordona4868042014-09-04 17:39:22 -0700295 */
296 public void onMerge() {}
297
298 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700299 * Notifies the {@link Conference} when the child calls should be swapped. Only invoked if the
300 * conference contains the capability {@link Connection#CAPABILITY_SWAP_CONFERENCE}.
Santos Cordona4868042014-09-04 17:39:22 -0700301 */
302 public void onSwap() {}
303
304 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700305 * Notifies the {@link Conference} of a request to play a DTMF tone.
Yorke Leea0d3ca92014-09-15 19:18:13 -0700306 *
307 * @param c A DTMF character.
308 */
309 public void onPlayDtmfTone(char c) {}
310
311 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700312 * Notifies the {@link Conference} of a request to stop any currently playing DTMF tones.
Yorke Leea0d3ca92014-09-15 19:18:13 -0700313 */
314 public void onStopDtmfTone() {}
315
316 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700317 * Notifies the {@link Conference} that the {@link #getAudioState()} property has a new value.
Yorke Leea0d3ca92014-09-15 19:18:13 -0700318 *
319 * @param state The new call audio state.
Yorke Lee4af59352015-05-13 14:14:54 -0700320 * @deprecated Use {@link #onCallAudioStateChanged(CallAudioState)} instead.
321 * @hide
Yorke Leea0d3ca92014-09-15 19:18:13 -0700322 */
Yorke Lee4af59352015-05-13 14:14:54 -0700323 @SystemApi
324 @Deprecated
Yorke Leea0d3ca92014-09-15 19:18:13 -0700325 public void onAudioStateChanged(AudioState state) {}
326
327 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700328 * Notifies the {@link Conference} that the {@link #getCallAudioState()} property has a new
329 * value.
Yorke Lee4af59352015-05-13 14:14:54 -0700330 *
331 * @param state The new call audio state.
Junhoedf3d822022-11-24 09:26:37 +0000332 * @deprecated Use {@link #onCallEndpointChanged(CallEndpoint)},
333 * {@link #onAvailableCallEndpointsChanged(List)} and
334 * {@link #onMuteStateChanged(boolean)} instead.
Yorke Lee4af59352015-05-13 14:14:54 -0700335 */
Junhoedf3d822022-11-24 09:26:37 +0000336 @Deprecated
Yorke Lee4af59352015-05-13 14:14:54 -0700337 public void onCallAudioStateChanged(CallAudioState state) {}
338
339 /**
Junhoedf3d822022-11-24 09:26:37 +0000340 * Notifies the {@link Conference} that the audio endpoint has been changed.
341 *
342 * @param callEndpoint The new call endpoint.
343 */
344 public void onCallEndpointChanged(@NonNull CallEndpoint callEndpoint) {}
345
346 /**
347 * Notifies the {@link Conference} that the available call endpoints have been changed.
348 *
349 * @param availableEndpoints The available call endpoints.
350 */
351 public void onAvailableCallEndpointsChanged(@NonNull List<CallEndpoint> availableEndpoints) {}
352
353 /**
354 * Notifies the {@link Conference} that its audio mute state has been changed.
355 *
356 * @param isMuted The new mute state.
357 */
358 public void onMuteStateChanged(boolean isMuted) {}
359
360 /**
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700361 * Notifies the {@link Conference} that a {@link Connection} has been added to it.
Andrew Lee46f7f5d2014-11-06 17:00:25 -0800362 *
363 * @param connection The newly added connection.
364 */
365 public void onConnectionAdded(Connection connection) {}
366
367 /**
Tyler Gunn0c62ef02020-02-11 14:39:43 -0800368 * Notifies the {@link Conference} of a request to add a new participants to the conference call
369 * @param participants that will be added to this conference call
Ravi Paluri404babb2020-01-23 19:02:44 +0530370 */
371 public void onAddConferenceParticipants(@NonNull List<Uri> participants) {}
372
373 /**
Ravi Paluri80aa2142019-12-02 11:57:37 +0530374 * Notifies this Conference, which is in {@code STATE_RINGING}, of
375 * a request to accept.
376 * For managed {@link ConnectionService}s, this will be called when the user answers a call via
377 * the default dialer's {@link InCallService}.
378 *
379 * @param videoState The video state in which to answer the connection.
380 */
Grace Jia41895152021-01-19 13:57:51 -0800381 public void onAnswer(@VideoProfile.VideoState int videoState) {}
Ravi Paluri80aa2142019-12-02 11:57:37 +0530382
383 /**
384 * Notifies this Conference, which is in {@code STATE_RINGING}, of
385 * a request to accept.
386 * For managed {@link ConnectionService}s, this will be called when the user answers a call via
387 * the default dialer's {@link InCallService}.
388 * @hide
389 */
390 public final void onAnswer() {
391 onAnswer(VideoProfile.STATE_AUDIO_ONLY);
392 }
393
394 /**
395 * Notifies this Conference, which is in {@code STATE_RINGING}, of
396 * a request to reject.
397 * For managed {@link ConnectionService}s, this will be called when the user rejects a call via
398 * the default dialer's {@link InCallService}.
399 */
400 public void onReject() {}
401
402 /**
Santos Cordon823fd3c2014-08-07 18:35:18 -0700403 * Sets state to be on hold.
404 */
405 public final void setOnHold() {
406 setState(Connection.STATE_HOLDING);
407 }
408
409 /**
Tyler Gunnd46595a2015-06-01 14:29:11 -0700410 * Sets state to be dialing.
411 */
412 public final void setDialing() {
413 setState(Connection.STATE_DIALING);
414 }
415
416 /**
Ravi Paluri80aa2142019-12-02 11:57:37 +0530417 * Sets state to be ringing.
418 */
419 public final void setRinging() {
420 setState(Connection.STATE_RINGING);
421 }
422
423 /**
Santos Cordon823fd3c2014-08-07 18:35:18 -0700424 * Sets state to be active.
425 */
426 public final void setActive() {
Ravi Paluri80aa2142019-12-02 11:57:37 +0530427 setRingbackRequested(false);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700428 setState(Connection.STATE_ACTIVE);
429 }
430
431 /**
432 * Sets state to disconnected.
433 *
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700434 * @param disconnectCause The reason for the disconnection, as described by
435 * {@link android.telecom.DisconnectCause}.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700436 */
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700437 public final void setDisconnected(DisconnectCause disconnectCause) {
438 mDisconnectCause = disconnectCause;;
Santos Cordon823fd3c2014-08-07 18:35:18 -0700439 setState(Connection.STATE_DISCONNECTED);
440 for (Listener l : mListeners) {
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700441 l.onDisconnected(this, mDisconnectCause);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700442 }
443 }
444
445 /**
mike dooley1cf14ac2014-11-04 10:59:53 -0800446 * @return The {@link DisconnectCause} for this connection.
447 */
448 public final DisconnectCause getDisconnectCause() {
449 return mDisconnectCause;
450 }
451
452 /**
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800453 * Sets the capabilities of a conference. See {@code CAPABILITY_*} constants of class
454 * {@link Connection} for valid values.
Nancy Chen56fc25d2014-09-09 12:24:51 -0700455 *
Tyler Gunn720c6642016-03-22 09:02:47 -0700456 * @param connectionCapabilities A bitmask of the {@code Capabilities} of the conference call.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700457 */
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800458 public final void setConnectionCapabilities(int connectionCapabilities) {
459 if (connectionCapabilities != mConnectionCapabilities) {
460 mConnectionCapabilities = connectionCapabilities;
Santos Cordon823fd3c2014-08-07 18:35:18 -0700461
462 for (Listener l : mListeners) {
Ihab Awad5c9c86e2014-11-12 13:41:16 -0800463 l.onConnectionCapabilitiesChanged(this, mConnectionCapabilities);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700464 }
465 }
466 }
467
468 /**
Tyler Gunn720c6642016-03-22 09:02:47 -0700469 * Sets the properties of a conference. See {@code PROPERTY_*} constants of class
470 * {@link Connection} for valid values.
471 *
472 * @param connectionProperties A bitmask of the {@code Properties} of the conference call.
473 */
474 public final void setConnectionProperties(int connectionProperties) {
475 if (connectionProperties != mConnectionProperties) {
476 mConnectionProperties = connectionProperties;
477
478 for (Listener l : mListeners) {
479 l.onConnectionPropertiesChanged(this, mConnectionProperties);
480 }
481 }
482 }
483
484 /**
Santos Cordon823fd3c2014-08-07 18:35:18 -0700485 * Adds the specified connection as a child of this conference.
486 *
487 * @param connection The connection to add.
488 * @return True if the connection was successfully added.
489 */
Santos Cordona4868042014-09-04 17:39:22 -0700490 public final boolean addConnection(Connection connection) {
Rekha Kumar07366812015-03-24 16:42:31 -0700491 Log.d(this, "Connection=%s, connection=", connection);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700492 if (connection != null && !mChildConnections.contains(connection)) {
493 if (connection.setConference(this)) {
494 mChildConnections.add(connection);
Andrew Lee46f7f5d2014-11-06 17:00:25 -0800495 onConnectionAdded(connection);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700496 for (Listener l : mListeners) {
497 l.onConnectionAdded(this, connection);
498 }
499 return true;
500 }
501 }
502 return false;
503 }
504
505 /**
506 * Removes the specified connection as a child of this conference.
507 *
508 * @param connection The connection to remove.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700509 */
Santos Cordona4868042014-09-04 17:39:22 -0700510 public final void removeConnection(Connection connection) {
Santos Cordon0159ac02014-08-21 14:28:11 -0700511 Log.d(this, "removing %s from %s", connection, mChildConnections);
Santos Cordon823fd3c2014-08-07 18:35:18 -0700512 if (connection != null && mChildConnections.remove(connection)) {
513 connection.resetConference();
514 for (Listener l : mListeners) {
515 l.onConnectionRemoved(this, connection);
516 }
517 }
518 }
519
520 /**
Ihab Awad50e35062014-09-30 09:17:03 -0700521 * Sets the connections with which this connection can be conferenced.
522 *
523 * @param conferenceableConnections The set of connections this connection can conference with.
524 */
525 public final void setConferenceableConnections(List<Connection> conferenceableConnections) {
526 clearConferenceableList();
527 for (Connection c : conferenceableConnections) {
528 // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a
529 // small amount of items here.
530 if (!mConferenceableConnections.contains(c)) {
531 c.addConnectionListener(mConnectionDeathListener);
532 mConferenceableConnections.add(c);
533 }
534 }
535 fireOnConferenceableConnectionsChanged();
536 }
537
Rekha Kumar07366812015-03-24 16:42:31 -0700538 /**
Ravi Paluri80aa2142019-12-02 11:57:37 +0530539 * Requests that the framework play a ringback tone. This is to be invoked by implementations
540 * that do not play a ringback tone themselves in the conference's audio stream.
541 *
542 * @param ringback Whether the ringback tone is to be played.
543 */
544 public final void setRingbackRequested(boolean ringback) {
545 if (mRingbackRequested != ringback) {
546 mRingbackRequested = ringback;
547 for (Listener l : mListeners) {
548 l.onRingbackRequested(this, ringback);
549 }
550 }
551 }
552
553 /**
Rekha Kumar07366812015-03-24 16:42:31 -0700554 * Set the video state for the conference.
Yorke Lee32f24732015-05-12 16:18:03 -0700555 * Valid values: {@link VideoProfile#STATE_AUDIO_ONLY},
556 * {@link VideoProfile#STATE_BIDIRECTIONAL},
557 * {@link VideoProfile#STATE_TX_ENABLED},
558 * {@link VideoProfile#STATE_RX_ENABLED}.
Rekha Kumar07366812015-03-24 16:42:31 -0700559 *
560 * @param videoState The new video state.
Rekha Kumar07366812015-03-24 16:42:31 -0700561 */
562 public final void setVideoState(Connection c, int videoState) {
563 Log.d(this, "setVideoState Conference: %s Connection: %s VideoState: %s",
564 this, c, videoState);
565 for (Listener l : mListeners) {
566 l.onVideoStateChanged(this, videoState);
567 }
568 }
569
570 /**
571 * Sets the video connection provider.
572 *
573 * @param videoProvider The video provider.
Rekha Kumar07366812015-03-24 16:42:31 -0700574 */
575 public final void setVideoProvider(Connection c, Connection.VideoProvider videoProvider) {
576 Log.d(this, "setVideoProvider Conference: %s Connection: %s VideoState: %s",
577 this, c, videoProvider);
578 for (Listener l : mListeners) {
579 l.onVideoProviderChanged(this, videoProvider);
580 }
581 }
582
Ihab Awad50e35062014-09-30 09:17:03 -0700583 private final void fireOnConferenceableConnectionsChanged() {
584 for (Listener l : mListeners) {
585 l.onConferenceableConnectionsChanged(this, getConferenceableConnections());
586 }
587 }
588
589 /**
590 * Returns the connections with which this connection can be conferenced.
591 */
592 public final List<Connection> getConferenceableConnections() {
593 return mUnmodifiableConferenceableConnections;
594 }
595
596 /**
Nancy Chenea38cca2014-09-05 16:38:49 -0700597 * Tears down the conference object and any of its current connections.
Santos Cordon823fd3c2014-08-07 18:35:18 -0700598 */
Santos Cordona4868042014-09-04 17:39:22 -0700599 public final void destroy() {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700600 Log.d(this, "destroying conference : %s", this);
601 // Tear down the children.
Santos Cordon0159ac02014-08-21 14:28:11 -0700602 for (Connection connection : mChildConnections) {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700603 Log.d(this, "removing connection %s", connection);
604 removeConnection(connection);
605 }
606
607 // If not yet disconnected, set the conference call as disconnected first.
608 if (mState != Connection.STATE_DISCONNECTED) {
609 Log.d(this, "setting to disconnected");
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700610 setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
Santos Cordon823fd3c2014-08-07 18:35:18 -0700611 }
612
613 // ...and notify.
614 for (Listener l : mListeners) {
615 l.onDestroyed(this);
616 }
617 }
618
619 /**
620 * Add a listener to be notified of a state change.
621 *
622 * @param listener The new listener.
623 * @return This conference.
624 * @hide
625 */
Tyler Gunn5567d742019-10-31 13:04:37 -0700626 final Conference addListener(Listener listener) {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700627 mListeners.add(listener);
628 return this;
629 }
630
631 /**
632 * Removes the specified listener.
633 *
634 * @param listener The listener to remove.
635 * @return This conference.
636 * @hide
637 */
Tyler Gunn5567d742019-10-31 13:04:37 -0700638 final Conference removeListener(Listener listener) {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700639 mListeners.remove(listener);
640 return this;
641 }
642
Yorke Leea0d3ca92014-09-15 19:18:13 -0700643 /**
Tyler Gunn4a57b9b2014-10-30 14:27:48 -0700644 * Retrieves the primary connection associated with the conference. The primary connection is
645 * the connection from which the conference will retrieve its current state.
646 *
647 * @return The primary connection.
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700648 * @hide
Tyler Gunn4a57b9b2014-10-30 14:27:48 -0700649 */
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700650 @SystemApi
Santos Cordon4055d642015-05-12 14:19:24 -0700651 public Connection getPrimaryConnection() {
Tyler Gunn4a57b9b2014-10-30 14:27:48 -0700652 if (mUnmodifiableChildConnections == null || mUnmodifiableChildConnections.isEmpty()) {
653 return null;
654 }
655 return mUnmodifiableChildConnections.get(0);
656 }
657
658 /**
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700659 * @hide
660 * @deprecated Use {@link #setConnectionTime}.
Tyler Gunncd5d33c2015-01-12 09:02:01 -0800661 */
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700662 @Deprecated
663 @SystemApi
664 public final void setConnectTimeMillis(long connectTimeMillis) {
665 setConnectionTime(connectTimeMillis);
Tyler Gunncd5d33c2015-01-12 09:02:01 -0800666 }
667
668 /**
Tyler Gunn17541392018-02-01 08:58:38 -0800669 * Sets the connection start time of the {@code Conference}. This is used in the call log to
670 * indicate the date and time when the conference took place.
671 * <p>
672 * Should be specified in wall-clock time returned by {@link System#currentTimeMillis()}.
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700673 * <p>
674 * When setting the connection time, you should always set the connection elapsed time via
Tyler Gunnc9503d62020-01-27 10:30:51 -0800675 * {@link #setConnectionStartElapsedRealtimeMillis(long)} to ensure the duration is reflected.
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700676 *
Tyler Gunn17541392018-02-01 08:58:38 -0800677 * @param connectionTimeMillis The connection time, in milliseconds, as returned by
678 * {@link System#currentTimeMillis()}.
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700679 */
Tyler Gunnc9503d62020-01-27 10:30:51 -0800680 public final void setConnectionTime(@IntRange(from = 0) long connectionTimeMillis) {
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700681 mConnectTimeMillis = connectionTimeMillis;
682 }
683
684 /**
Tyler Gunn17541392018-02-01 08:58:38 -0800685 * Sets the start time of the {@link Conference} which is the basis for the determining the
686 * duration of the {@link Conference}.
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700687 * <p>
Tyler Gunn17541392018-02-01 08:58:38 -0800688 * You should use a value returned by {@link SystemClock#elapsedRealtime()} to ensure that time
689 * zone changes do not impact the conference duration.
690 * <p>
691 * When setting this, you should also set the connection time via
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700692 * {@link #setConnectionTime(long)}.
693 *
Tyler Gunn17541392018-02-01 08:58:38 -0800694 * @param connectionStartElapsedRealTime The connection time, as measured by
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700695 * {@link SystemClock#elapsedRealtime()}.
Tyler Gunnc9503d62020-01-27 10:30:51 -0800696 * @deprecated use {@link #setConnectionStartElapsedRealtimeMillis(long)} instead.
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700697 */
Tyler Gunnc9503d62020-01-27 10:30:51 -0800698 @Deprecated
Tyler Gunn17541392018-02-01 08:58:38 -0800699 public final void setConnectionStartElapsedRealTime(long connectionStartElapsedRealTime) {
Tyler Gunnc9503d62020-01-27 10:30:51 -0800700 setConnectionStartElapsedRealtimeMillis(connectionStartElapsedRealTime);
701 }
702
703 /**
704 * Sets the start time of the {@link Conference} which is the basis for the determining the
705 * duration of the {@link Conference}.
706 * <p>
707 * You should use a value returned by {@link SystemClock#elapsedRealtime()} to ensure that time
708 * zone changes do not impact the conference duration.
709 * <p>
710 * When setting this, you should also set the connection time via
711 * {@link #setConnectionTime(long)}.
712 *
713 * @param connectionStartElapsedRealTime The connection time, as measured by
714 * {@link SystemClock#elapsedRealtime()}.
715 */
716 public final void setConnectionStartElapsedRealtimeMillis(
717 @ElapsedRealtimeLong long connectionStartElapsedRealTime) {
Tyler Gunn17541392018-02-01 08:58:38 -0800718 mConnectionStartElapsedRealTime = connectionStartElapsedRealTime;
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700719 }
720
721 /**
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700722 * @hide
723 * @deprecated Use {@link #getConnectionTime}.
724 */
725 @Deprecated
726 @SystemApi
727 public final long getConnectTimeMillis() {
728 return getConnectionTime();
729 }
730
731 /**
732 * Retrieves the connection start time of the {@code Conference}, if specified. A value of
Tyler Gunncd5d33c2015-01-12 09:02:01 -0800733 * {@link #CONNECT_TIME_NOT_SPECIFIED} indicates that Telecom should determine the start time
734 * of the conference.
735 *
Santos Cordon5d2e4f22015-05-12 12:32:51 -0700736 * @return The time at which the {@code Conference} was connected.
Tyler Gunncd5d33c2015-01-12 09:02:01 -0800737 */
Tyler Gunnc9503d62020-01-27 10:30:51 -0800738 public final @IntRange(from = 0) long getConnectionTime() {
Tyler Gunncd5d33c2015-01-12 09:02:01 -0800739 return mConnectTimeMillis;
740 }
741
742 /**
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700743 * Retrieves the connection start time of the {@link Conference}, if specified. A value of
744 * {@link #CONNECT_TIME_NOT_SPECIFIED} indicates that Telecom should determine the start time
745 * of the conference.
Tyler Gunn5567d742019-10-31 13:04:37 -0700746 * <p>
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700747 * This is based on the value of {@link SystemClock#elapsedRealtime()} to ensure that it is not
748 * impacted by wall clock changes (user initiated, network initiated, time zone change, etc).
Tyler Gunn5567d742019-10-31 13:04:37 -0700749 * <p>
750 * Note: This is only exposed for use by the Telephony framework which needs it to copy
751 * conference start times among conference participants. It is exposed as a system API since it
752 * has no general use other than to the Telephony framework.
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700753 *
754 * @return The elapsed time at which the {@link Conference} was connected.
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700755 */
Tyler Gunnc9503d62020-01-27 10:30:51 -0800756 public final @ElapsedRealtimeLong long getConnectionStartElapsedRealtimeMillis() {
Tyler Gunn17541392018-02-01 08:58:38 -0800757 return mConnectionStartElapsedRealTime;
Tyler Gunn3fa819c2017-08-04 09:27:26 -0700758 }
759
760 /**
Yorke Leea0d3ca92014-09-15 19:18:13 -0700761 * Inform this Conference that the state of its audio output has been changed externally.
762 *
763 * @param state The new audio state.
764 * @hide
765 */
Yorke Lee4af59352015-05-13 14:14:54 -0700766 final void setCallAudioState(CallAudioState state) {
767 Log.d(this, "setCallAudioState %s", state);
768 mCallAudioState = state;
769 onAudioStateChanged(getAudioState());
770 onCallAudioStateChanged(state);
Yorke Leea0d3ca92014-09-15 19:18:13 -0700771 }
772
Junhoedf3d822022-11-24 09:26:37 +0000773 /**
774 * Inform this Conference that the audio endpoint has been changed.
775 *
776 * @param endpoint The new call endpoint.
777 * @hide
778 */
779 final void setCallEndpoint(CallEndpoint endpoint) {
780 Log.d(this, "setCallEndpoint %s", endpoint);
781 mCallEndpoint = endpoint;
782 onCallEndpointChanged(endpoint);
783 }
784
785 /**
786 * Inform this Conference that the available call endpoints have been changed.
787 *
788 * @param availableEndpoints The available call endpoints.
789 * @hide
790 */
791 final void setAvailableCallEndpoints(List<CallEndpoint> availableEndpoints) {
792 Log.d(this, "setAvailableCallEndpoints");
793 onAvailableCallEndpointsChanged(availableEndpoints);
794 }
795
796 /**
797 * Inform this Conference that its audio mute state has been changed.
798 *
799 * @param isMuted The new mute state.
800 * @hide
801 */
802 final void setMuteState(boolean isMuted) {
803 Log.d(this, "setMuteState %s", isMuted);
804 onMuteStateChanged(isMuted);
805 }
806
Santos Cordon823fd3c2014-08-07 18:35:18 -0700807 private void setState(int newState) {
Santos Cordon823fd3c2014-08-07 18:35:18 -0700808 if (mState != newState) {
809 int oldState = mState;
810 mState = newState;
811 for (Listener l : mListeners) {
812 l.onStateChanged(this, oldState, newState);
813 }
814 }
815 }
Ihab Awad50e35062014-09-30 09:17:03 -0700816
Ravi Paluri80aa2142019-12-02 11:57:37 +0530817 private static class FailureSignalingConference extends Conference {
818 private boolean mImmutable = false;
819 public FailureSignalingConference(DisconnectCause disconnectCause,
820 PhoneAccountHandle phoneAccount) {
821 super(phoneAccount);
822 setDisconnected(disconnectCause);
823 mImmutable = true;
824 }
825 public void checkImmutable() {
826 if (mImmutable) {
827 throw new UnsupportedOperationException("Conference is immutable");
828 }
829 }
830 }
831
832 /**
833 * Return a {@code Conference} which represents a failed conference attempt. The returned
834 * {@code Conference} will have a {@link android.telecom.DisconnectCause} and as specified,
835 * and a {@link #getState()} of {@code STATE_DISCONNECTED}.
836 * <p>
837 * The returned {@code Conference} can be assumed to {@link #destroy()} itself when appropriate,
838 * so users of this method need not maintain a reference to its return value to destroy it.
839 *
840 * @param disconnectCause The disconnect cause, ({@see android.telecomm.DisconnectCause}).
841 * @return A {@code Conference} which indicates failure.
842 */
843 public @NonNull static Conference createFailedConference(
844 @NonNull DisconnectCause disconnectCause, @NonNull PhoneAccountHandle phoneAccount) {
845 return new FailureSignalingConference(disconnectCause, phoneAccount);
846 }
847
Ihab Awad50e35062014-09-30 09:17:03 -0700848 private final void clearConferenceableList() {
849 for (Connection c : mConferenceableConnections) {
850 c.removeConnectionListener(mConnectionDeathListener);
851 }
852 mConferenceableConnections.clear();
853 }
Rekha Kumar07366812015-03-24 16:42:31 -0700854
855 @Override
856 public String toString() {
857 return String.format(Locale.US,
Ravi Paluri80aa2142019-12-02 11:57:37 +0530858 "[State: %s,Capabilites: %s, VideoState: %s, VideoProvider: %s,"
859 + "isRingbackRequested: %s, ThisObject %s]",
Rekha Kumar07366812015-03-24 16:42:31 -0700860 Connection.stateToString(mState),
861 Call.Details.capabilitiesToString(mConnectionCapabilities),
862 getVideoState(),
863 getVideoProvider(),
Ravi Paluri80aa2142019-12-02 11:57:37 +0530864 isRingbackRequested() ? "Y" : "N",
Rekha Kumar07366812015-03-24 16:42:31 -0700865 super.toString());
866 }
Andrew Lee0f51da32015-04-16 13:11:55 -0700867
Andrew Leeedc625f2015-04-14 13:38:12 -0700868 /**
869 * Sets the label and icon status to display in the InCall UI.
870 *
871 * @param statusHints The status label and icon to set.
872 */
873 public final void setStatusHints(StatusHints statusHints) {
874 mStatusHints = statusHints;
875 for (Listener l : mListeners) {
876 l.onStatusHintsChanged(this, statusHints);
877 }
878 }
879
880 /**
881 * @return The status hints for this conference.
882 */
883 public final StatusHints getStatusHints() {
884 return mStatusHints;
885 }
Santos Cordon6b7f9552015-05-27 17:21:45 -0700886
887 /**
Tyler Gunndee56a82016-03-23 16:06:34 -0700888 * Replaces all the extras associated with this {@code Conference}.
889 * <p>
890 * New or existing keys are replaced in the {@code Conference} extras. Keys which are no longer
891 * in the new extras, but were present the last time {@code setExtras} was called are removed.
892 * <p>
Tyler Gunn9c0eb0b2016-06-29 11:23:25 -0700893 * Alternatively you may use the {@link #putExtras(Bundle)}, and
894 * {@link #removeExtras(String...)} methods to modify the extras.
895 * <p>
Tyler Gunndee56a82016-03-23 16:06:34 -0700896 * 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 -0700897 * Keys should be fully qualified (e.g., com.example.extras.MY_EXTRA) to avoid conflicts.
Santos Cordon6b7f9552015-05-27 17:21:45 -0700898 *
Tyler Gunndee56a82016-03-23 16:06:34 -0700899 * @param extras The extras associated with this {@code Conference}.
Santos Cordon6b7f9552015-05-27 17:21:45 -0700900 */
901 public final void setExtras(@Nullable Bundle extras) {
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700902 // Keeping putExtras and removeExtras in the same lock so that this operation happens as a
903 // block instead of letting other threads put/remove while this method is running.
904 synchronized (mExtrasLock) {
905 // Add/replace any new or changed extras values.
906 putExtras(extras);
907 // If we have used "setExtras" in the past, compare the key set from the last invocation
908 // to the current one and remove any keys that went away.
909 if (mPreviousExtraKeys != null) {
910 List<String> toRemove = new ArrayList<String>();
911 for (String oldKey : mPreviousExtraKeys) {
912 if (extras == null || !extras.containsKey(oldKey)) {
913 toRemove.add(oldKey);
914 }
915 }
Tyler Gunndee56a82016-03-23 16:06:34 -0700916
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700917 if (!toRemove.isEmpty()) {
918 removeExtras(toRemove);
Tyler Gunndee56a82016-03-23 16:06:34 -0700919 }
920 }
921
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700922 // Track the keys the last time set called setExtras. This way, the next time setExtras
923 // is called we can see if the caller has removed any extras values.
924 if (mPreviousExtraKeys == null) {
925 mPreviousExtraKeys = new ArraySet<String>();
Tyler Gunndee56a82016-03-23 16:06:34 -0700926 }
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700927 mPreviousExtraKeys.clear();
928 if (extras != null) {
929 mPreviousExtraKeys.addAll(extras.keySet());
930 }
Tyler Gunna8fb8ab2016-03-29 10:24:22 -0700931 }
Tyler Gunndee56a82016-03-23 16:06:34 -0700932 }
933
934 /**
935 * Adds some extras to this {@link Conference}. Existing keys are replaced and new ones are
936 * added.
937 * <p>
938 * No assumptions should be made as to how an In-Call UI or service will handle these extras.
939 * Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
940 *
941 * @param extras The extras to add.
942 */
943 public final void putExtras(@NonNull Bundle extras) {
944 if (extras == null) {
945 return;
946 }
947
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700948 // Creating a Bundle clone so we don't have to synchronize on mExtrasLock while calling
949 // onExtrasChanged.
950 Bundle listenersBundle;
951 synchronized (mExtrasLock) {
952 if (mExtras == null) {
953 mExtras = new Bundle();
954 }
955 mExtras.putAll(extras);
956 listenersBundle = new Bundle(mExtras);
Tyler Gunndee56a82016-03-23 16:06:34 -0700957 }
Tyler Gunndee56a82016-03-23 16:06:34 -0700958
Santos Cordon6b7f9552015-05-27 17:21:45 -0700959 for (Listener l : mListeners) {
Brad Ebinger4fa6a012016-06-14 17:04:01 -0700960 l.onExtrasChanged(this, new Bundle(listenersBundle));
Santos Cordon6b7f9552015-05-27 17:21:45 -0700961 }
962 }
963
964 /**
Tyler Gunndee56a82016-03-23 16:06:34 -0700965 * Adds a boolean extra to this {@link Conference}.
966 *
967 * @param key The extra key.
968 * @param value The value.
969 * @hide
970 */
971 public final void putExtra(String key, boolean value) {
972 Bundle newExtras = new Bundle();
973 newExtras.putBoolean(key, value);
974 putExtras(newExtras);
975 }
976
977 /**
978 * Adds an integer extra to this {@link Conference}.
979 *
980 * @param key The extra key.
981 * @param value The value.
982 * @hide
983 */
984 public final void putExtra(String key, int value) {
985 Bundle newExtras = new Bundle();
986 newExtras.putInt(key, value);
987 putExtras(newExtras);
988 }
989
990 /**
991 * Adds a string extra to this {@link Conference}.
992 *
993 * @param key The extra key.
994 * @param value The value.
995 * @hide
996 */
997 public final void putExtra(String key, String value) {
998 Bundle newExtras = new Bundle();
999 newExtras.putString(key, value);
1000 putExtras(newExtras);
1001 }
1002
1003 /**
Tyler Gunn071be6f2016-05-10 14:52:33 -07001004 * Removes extras from this {@link Conference}.
Tyler Gunndee56a82016-03-23 16:06:34 -07001005 *
Tyler Gunn071be6f2016-05-10 14:52:33 -07001006 * @param keys The keys of the extras to remove.
Tyler Gunndee56a82016-03-23 16:06:34 -07001007 */
1008 public final void removeExtras(List<String> keys) {
1009 if (keys == null || keys.isEmpty()) {
1010 return;
1011 }
1012
Brad Ebinger4fa6a012016-06-14 17:04:01 -07001013 synchronized (mExtrasLock) {
1014 if (mExtras != null) {
1015 for (String key : keys) {
1016 mExtras.remove(key);
1017 }
Tyler Gunndee56a82016-03-23 16:06:34 -07001018 }
1019 }
1020
Brad Ebinger4fa6a012016-06-14 17:04:01 -07001021 List<String> unmodifiableKeys = Collections.unmodifiableList(keys);
Tyler Gunndee56a82016-03-23 16:06:34 -07001022 for (Listener l : mListeners) {
Brad Ebinger4fa6a012016-06-14 17:04:01 -07001023 l.onExtrasRemoved(this, unmodifiableKeys);
Tyler Gunndee56a82016-03-23 16:06:34 -07001024 }
1025 }
1026
1027 /**
Tyler Gunn071be6f2016-05-10 14:52:33 -07001028 * Removes extras from this {@link Conference}.
1029 *
1030 * @param keys The keys of the extras to remove.
1031 */
1032 public final void removeExtras(String ... keys) {
1033 removeExtras(Arrays.asList(keys));
1034 }
1035
1036 /**
Tyler Gunndee56a82016-03-23 16:06:34 -07001037 * Returns the extras associated with this conference.
Tyler Gunn2cbe2b52016-05-04 15:48:10 +00001038 * <p>
1039 * Extras should be updated using {@link #putExtras(Bundle)} and {@link #removeExtras(List)}.
1040 * <p>
1041 * Telecom or an {@link InCallService} can also update the extras via
1042 * {@link android.telecom.Call#putExtras(Bundle)}, and
1043 * {@link Call#removeExtras(List)}.
1044 * <p>
1045 * The conference is notified of changes to the extras made by Telecom or an
1046 * {@link InCallService} by {@link #onExtrasChanged(Bundle)}.
Tyler Gunndee56a82016-03-23 16:06:34 -07001047 *
1048 * @return The extras associated with this connection.
Santos Cordon6b7f9552015-05-27 17:21:45 -07001049 */
1050 public final Bundle getExtras() {
1051 return mExtras;
1052 }
Tyler Gunndee56a82016-03-23 16:06:34 -07001053
1054 /**
1055 * Notifies this {@link Conference} of a change to the extras made outside the
1056 * {@link ConnectionService}.
1057 * <p>
1058 * These extras changes can originate from Telecom itself, or from an {@link InCallService} via
1059 * {@link android.telecom.Call#putExtras(Bundle)}, and
1060 * {@link Call#removeExtras(List)}.
1061 *
1062 * @param extras The new extras bundle.
1063 */
1064 public void onExtrasChanged(Bundle extras) {}
1065
1066 /**
Tyler Gunn10362372020-04-08 13:12:30 -07001067 * Set whether Telecom should treat this {@link Conference} as a multiparty conference call or
1068 * if it should treat it as a single-party call.
Tyler Gunn68a73a42018-10-03 15:38:57 -07001069 * This method is used as part of a workaround regarding IMS conference calls and user
1070 * expectation. In IMS, once a conference is formed, the UE is connected to an IMS conference
1071 * server. If all participants of the conference drop out of the conference except for one, the
1072 * UE is still connected to the IMS conference server. At this point, the user logically
1073 * assumes they're no longer in a conference, yet the underlying network actually is.
1074 * To help provide a better user experiece, IMS conference calls can pretend to actually be a
1075 * single-party call when the participant count drops to 1. Although the dialer/phone app
1076 * could perform this trickery, it makes sense to do this in Telephony since a fix there will
1077 * ensure that bluetooth head units, auto and wearable apps all behave consistently.
Tyler Gunn5567d742019-10-31 13:04:37 -07001078 * <p>
1079 * This API is intended for use by the platform Telephony stack only.
Tyler Gunn68a73a42018-10-03 15:38:57 -07001080 *
1081 * @param isConference {@code true} if this {@link Conference} should be treated like a
1082 * conference call, {@code false} if it should be treated like a single-party call.
1083 * @hide
1084 */
Tyler Gunn5567d742019-10-31 13:04:37 -07001085 @SystemApi
Tyler Gunnc9503d62020-01-27 10:30:51 -08001086 @RequiresPermission(MODIFY_PHONE_STATE)
Tyler Gunn68a73a42018-10-03 15:38:57 -07001087 public void setConferenceState(boolean isConference) {
Tyler Gunn10362372020-04-08 13:12:30 -07001088 mIsMultiparty = isConference;
Tyler Gunn68a73a42018-10-03 15:38:57 -07001089 for (Listener l : mListeners) {
1090 l.onConferenceStateChanged(this, isConference);
1091 }
1092 }
1093
1094 /**
Brad Ebingere0c12f42020-04-08 16:25:12 -07001095 * Sets the call direction of this {@link Conference}. By default, all {@link Conference}s have
1096 * a direction of {@link android.telecom.Call.Details.CallDirection#DIRECTION_UNKNOWN}. The
1097 * direction of a {@link Conference} is only applicable to the case where
1098 * {@link #setConferenceState(boolean)} has been set to {@code false}, otherwise the direction
1099 * will be ignored.
1100 * @param callDirection The direction of the conference.
1101 * @hide
1102 */
1103 @RequiresPermission(MODIFY_PHONE_STATE)
1104 public final void setCallDirection(@Call.Details.CallDirection int callDirection) {
1105 Log.d(this, "setDirection %d", callDirection);
1106 mCallDirection = callDirection;
1107 for (Listener l : mListeners) {
1108 l.onCallDirectionChanged(this, callDirection);
1109 }
1110 }
1111
Tyler Gunn10362372020-04-08 13:12:30 -07001112 /**
1113 * Determines if the {@link Conference} is considered "multiparty" or not. By default all
1114 * conferences are considered multiparty. A multiparty conference is one where there are
1115 * multiple conference participants (other than the host) in the conference.
1116 * This is tied to {@link #setConferenceState(boolean)}, which is used for some use cases to
1117 * have a conference appear as if it is a standalone call, in which case the conference will
1118 * no longer be multiparty.
1119 * @return {@code true} if conference is treated as a conference (i.e. it is multiparty),
1120 * {@code false} if it should emulate a standalone call (i.e. not multiparty).
1121 * @hide
1122 */
1123 public boolean isMultiparty() {
1124 return mIsMultiparty;
1125 }
Brad Ebingere0c12f42020-04-08 16:25:12 -07001126
1127 /**
Tyler Gunn68a73a42018-10-03 15:38:57 -07001128 * Sets the address of this {@link Conference}. Used when {@link #setConferenceState(boolean)}
1129 * is called to mark a conference temporarily as NOT a conference.
Tyler Gunn5567d742019-10-31 13:04:37 -07001130 * <p>
1131 * Note: This is a Telephony-specific implementation detail related to IMS conferences. It is
1132 * not intended for use outside of the Telephony stack.
Tyler Gunn68a73a42018-10-03 15:38:57 -07001133 *
1134 * @param address The new address.
1135 * @param presentation The presentation requirements for the address.
1136 * See {@link TelecomManager} for valid values.
1137 * @hide
1138 */
Tyler Gunn5567d742019-10-31 13:04:37 -07001139 @SystemApi
Tyler Gunnc9503d62020-01-27 10:30:51 -08001140 @RequiresPermission(MODIFY_PHONE_STATE)
Tyler Gunn5567d742019-10-31 13:04:37 -07001141 public final void setAddress(@NonNull Uri address,
1142 @TelecomManager.Presentation int presentation) {
Tyler Gunn68a73a42018-10-03 15:38:57 -07001143 Log.d(this, "setAddress %s", address);
Tyler Gunnac60f952019-05-31 07:23:16 -07001144 mAddress = address;
1145 mAddressPresentation = presentation;
Tyler Gunn68a73a42018-10-03 15:38:57 -07001146 for (Listener l : mListeners) {
1147 l.onAddressChanged(this, address, presentation);
1148 }
1149 }
1150
1151 /**
Tyler Gunnac60f952019-05-31 07:23:16 -07001152 * Returns the "address" associated with the conference. This is applicable in two cases:
1153 * <ol>
1154 * <li>When {@link #setConferenceState(boolean)} is used to mark a conference as
1155 * temporarily "not a conference"; we need to present the correct address in the in-call
1156 * UI.</li>
1157 * <li>When the conference is not hosted on the current device, we need to know the address
1158 * information for the purpose of showing the original address to the user, as well as for
1159 * logging to the call log.</li>
1160 * </ol>
1161 * @return The address of the conference, or {@code null} if not applicable.
1162 * @hide
1163 */
1164 public final Uri getAddress() {
1165 return mAddress;
1166 }
1167
1168 /**
1169 * Returns the address presentation associated with the conference.
1170 * <p>
1171 * This is applicable in two cases:
1172 * <ol>
1173 * <li>When {@link #setConferenceState(boolean)} is used to mark a conference as
Brad Ebinger0ae44ed2020-04-09 15:30:57 -07001174 * temporarily "not a conference"; we need to present the correct address presentation in
1175 * the in-call UI.</li>
Tyler Gunnac60f952019-05-31 07:23:16 -07001176 * <li>When the conference is not hosted on the current device, we need to know the address
Brad Ebinger0ae44ed2020-04-09 15:30:57 -07001177 * presentation information for the purpose of showing the original address to the user, as
1178 * well as for logging to the call log.</li>
Tyler Gunnac60f952019-05-31 07:23:16 -07001179 * </ol>
Brad Ebinger0ae44ed2020-04-09 15:30:57 -07001180 * @return The address presentation of the conference.
Tyler Gunnac60f952019-05-31 07:23:16 -07001181 * @hide
1182 */
Brad Ebinger0ae44ed2020-04-09 15:30:57 -07001183 public final @TelecomManager.Presentation int getAddressPresentation() {
Tyler Gunnac60f952019-05-31 07:23:16 -07001184 return mAddressPresentation;
1185 }
1186
1187 /**
1188 * @return The caller display name (CNAP).
1189 * @hide
1190 */
1191 public final String getCallerDisplayName() {
1192 return mCallerDisplayName;
1193 }
1194
1195 /**
1196 * @return The presentation requirements for the handle.
1197 * See {@link TelecomManager} for valid values.
1198 * @hide
1199 */
1200 public final int getCallerDisplayNamePresentation() {
1201 return mCallerDisplayNamePresentation;
1202 }
1203
1204 /**
Brad Ebingere0c12f42020-04-08 16:25:12 -07001205 * @return The call direction of this conference. Only applicable when
1206 * {@link #setConferenceState(boolean)} is set to false.
1207 * @hide
1208 */
1209 public final @Call.Details.CallDirection int getCallDirection() {
1210 return mCallDirection;
1211 }
1212
1213 /**
Tyler Gunn68a73a42018-10-03 15:38:57 -07001214 * Sets the caller display name (CNAP) of this {@link Conference}. Used when
1215 * {@link #setConferenceState(boolean)} is called to mark a conference temporarily as NOT a
1216 * conference.
Tyler Gunn5567d742019-10-31 13:04:37 -07001217 * <p>
1218 * Note: This is a Telephony-specific implementation detail related to IMS conferences. It is
1219 * not intended for use outside of the Telephony stack.
Tyler Gunn68a73a42018-10-03 15:38:57 -07001220 *
1221 * @param callerDisplayName The new display name.
1222 * @param presentation The presentation requirements for the handle.
1223 * See {@link TelecomManager} for valid values.
1224 * @hide
1225 */
Tyler Gunn5567d742019-10-31 13:04:37 -07001226 @SystemApi
Tyler Gunn5567d742019-10-31 13:04:37 -07001227 public final void setCallerDisplayName(@NonNull String callerDisplayName,
1228 @TelecomManager.Presentation int presentation) {
Tyler Gunn68a73a42018-10-03 15:38:57 -07001229 Log.d(this, "setCallerDisplayName %s", callerDisplayName);
Tyler Gunnac60f952019-05-31 07:23:16 -07001230 mCallerDisplayName = callerDisplayName;
1231 mCallerDisplayNamePresentation = presentation;
Tyler Gunn68a73a42018-10-03 15:38:57 -07001232 for (Listener l : mListeners) {
1233 l.onCallerDisplayNameChanged(this, callerDisplayName, presentation);
1234 }
1235 }
1236
1237 /**
Tyler Gunndee56a82016-03-23 16:06:34 -07001238 * Handles a change to extras received from Telecom.
1239 *
1240 * @param extras The new extras.
1241 * @hide
1242 */
1243 final void handleExtrasChanged(Bundle extras) {
Brad Ebinger4fa6a012016-06-14 17:04:01 -07001244 Bundle b = null;
1245 synchronized (mExtrasLock) {
1246 mExtras = extras;
1247 if (mExtras != null) {
1248 b = new Bundle(mExtras);
1249 }
1250 }
1251 onExtrasChanged(b);
Tyler Gunndee56a82016-03-23 16:06:34 -07001252 }
Hall Liuc9bc1c62019-04-16 14:00:55 -07001253
1254 /**
Tyler Gunnc9503d62020-01-27 10:30:51 -08001255 * Sends an event associated with this {@link Conference} with associated event extras to the
1256 * {@link InCallService}.
1257 * <p>
1258 * Connection events are used to communicate point in time information from a
1259 * {@link ConnectionService} to an {@link InCallService} implementation. An example of a
1260 * custom connection event includes notifying the UI when a WIFI call has been handed over to
1261 * LTE, which the InCall UI might use to inform the user that billing charges may apply. The
1262 * Android Telephony framework will send the {@link Connection#EVENT_MERGE_COMPLETE}
1263 * connection event when a call to {@link Call#mergeConference()} has completed successfully.
1264 * <p>
1265 * Events are exposed to {@link InCallService} implementations via
1266 * {@link Call.Callback#onConnectionEvent(Call, String, Bundle)}.
1267 * <p>
1268 * No assumptions should be made as to how an In-Call UI or service will handle these events.
1269 * The {@link ConnectionService} must assume that the In-Call UI could even chose to ignore
1270 * some events altogether.
1271 * <p>
1272 * Events should be fully qualified (e.g. {@code com.example.event.MY_EVENT}) to avoid
1273 * conflicts between {@link ConnectionService} implementations. Further, custom
1274 * {@link ConnectionService} implementations shall not re-purpose events in the
1275 * {@code android.*} namespace, nor shall they define new event types in this namespace. When
1276 * defining a custom event type, ensure the contents of the extras {@link Bundle} is clearly
1277 * defined. Extra keys for this bundle should be named similar to the event type (e.g.
1278 * {@code com.example.extra.MY_EXTRA}).
1279 * <p>
1280 * When defining events and the associated extras, it is important to keep their behavior
1281 * consistent when the associated {@link ConnectionService} is updated. Support for deprecated
1282 * events/extras should me maintained to ensure backwards compatibility with older
1283 * {@link InCallService} implementations which were built to support the older behavior.
1284 * <p>
1285 * Expected connection events from the Telephony stack are:
1286 * <p>
1287 * <ul>
1288 * <li>{@link Connection#EVENT_CALL_HOLD_FAILED} with {@code null} {@code extras} when the
1289 * {@link Conference} could not be held.</li>
1290 * <li>{@link Connection#EVENT_MERGE_START} with {@code null} {@code extras} when a new
1291 * call is being merged into the conference.</li>
1292 * <li>{@link Connection#EVENT_MERGE_COMPLETE} with {@code null} {@code extras} a new call
1293 * has completed being merged into the conference.</li>
1294 * <li>{@link Connection#EVENT_CALL_MERGE_FAILED} with {@code null} {@code extras} a new
1295 * call has failed to merge into the conference (the dialer app can determine which call
1296 * failed to merge based on the fact that the call still exists outside of the conference
1297 * at the end of the merge process).</li>
1298 * </ul>
Tyler Gunn5567d742019-10-31 13:04:37 -07001299 *
Tyler Gunnc9503d62020-01-27 10:30:51 -08001300 * @param event The conference event.
Tyler Gunn5567d742019-10-31 13:04:37 -07001301 * @param extras Optional bundle containing extra information associated with the event.
Hall Liuc9bc1c62019-04-16 14:00:55 -07001302 */
Tyler Gunn5567d742019-10-31 13:04:37 -07001303 public void sendConferenceEvent(@NonNull String event, @Nullable Bundle extras) {
Hall Liuc9bc1c62019-04-16 14:00:55 -07001304 for (Listener l : mListeners) {
1305 l.onConnectionEvent(this, event, extras);
1306 }
1307 }
Santos Cordon823fd3c2014-08-07 18:35:18 -07001308}