1 /*
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 
17 package android.telecom;
18 
19 import static android.Manifest.permission.MODIFY_PHONE_STATE;
20 
21 import android.annotation.ElapsedRealtimeLong;
22 import android.annotation.IntRange;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.annotation.RequiresPermission;
26 import android.annotation.SystemApi;
27 import android.net.Uri;
28 import android.os.Bundle;
29 import android.os.SystemClock;
30 import android.telecom.Connection.VideoProvider;
31 import android.util.ArraySet;
32 
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.Collections;
36 import java.util.List;
37 import java.util.Locale;
38 import java.util.Set;
39 import java.util.concurrent.CopyOnWriteArrayList;
40 import java.util.concurrent.CopyOnWriteArraySet;
41 
42 /**
43  * Represents a conference call which can contain any number of {@link Connection} objects.
44  */
45 public abstract class Conference extends Conferenceable {
46 
47     /**
48      * Used to indicate that the conference connection time is not specified.  If not specified,
49      * Telecom will set the connect time.
50      */
51     public static final long CONNECT_TIME_NOT_SPECIFIED = 0;
52 
53     /** @hide */
54     abstract static class Listener {
onStateChanged(Conference conference, int oldState, int newState)55         public void onStateChanged(Conference conference, int oldState, int newState) {}
onDisconnected(Conference conference, DisconnectCause disconnectCause)56         public void onDisconnected(Conference conference, DisconnectCause disconnectCause) {}
onConnectionAdded(Conference conference, Connection connection)57         public void onConnectionAdded(Conference conference, Connection connection) {}
onConnectionRemoved(Conference conference, Connection connection)58         public void onConnectionRemoved(Conference conference, Connection connection) {}
onConferenceableConnectionsChanged( Conference conference, List<Connection> conferenceableConnections)59         public void onConferenceableConnectionsChanged(
60                 Conference conference, List<Connection> conferenceableConnections) {}
onDestroyed(Conference conference)61         public void onDestroyed(Conference conference) {}
onConnectionCapabilitiesChanged( Conference conference, int connectionCapabilities)62         public void onConnectionCapabilitiesChanged(
63                 Conference conference, int connectionCapabilities) {}
onConnectionPropertiesChanged( Conference conference, int connectionProperties)64         public void onConnectionPropertiesChanged(
65                 Conference conference, int connectionProperties) {}
onVideoStateChanged(Conference c, int videoState)66         public void onVideoStateChanged(Conference c, int videoState) { }
onVideoProviderChanged(Conference c, Connection.VideoProvider videoProvider)67         public void onVideoProviderChanged(Conference c, Connection.VideoProvider videoProvider) {}
onStatusHintsChanged(Conference conference, StatusHints statusHints)68         public void onStatusHintsChanged(Conference conference, StatusHints statusHints) {}
onExtrasChanged(Conference c, Bundle extras)69         public void onExtrasChanged(Conference c, Bundle extras) {}
onExtrasRemoved(Conference c, List<String> keys)70         public void onExtrasRemoved(Conference c, List<String> keys) {}
onConferenceStateChanged(Conference c, boolean isConference)71         public void onConferenceStateChanged(Conference c, boolean isConference) {}
onAddressChanged(Conference c, Uri newAddress, int presentation)72         public void onAddressChanged(Conference c, Uri newAddress, int presentation) {}
onConnectionEvent(Conference c, String event, Bundle extras)73         public void onConnectionEvent(Conference c, String event, Bundle extras) {}
onCallerDisplayNameChanged( Conference c, String callerDisplayName, int presentation)74         public void onCallerDisplayNameChanged(
75                 Conference c, String callerDisplayName, int presentation) {}
onCallDirectionChanged(Conference c, int callDirection)76         public void onCallDirectionChanged(Conference c, int callDirection) {}
onRingbackRequested(Conference c, boolean ringback)77         public void onRingbackRequested(Conference c, boolean ringback) {}
78     }
79 
80     private final Set<Listener> mListeners = new CopyOnWriteArraySet<>();
81     private final List<Connection> mChildConnections = new CopyOnWriteArrayList<>();
82     private final List<Connection> mUnmodifiableChildConnections =
83             Collections.unmodifiableList(mChildConnections);
84     private final List<Connection> mConferenceableConnections = new ArrayList<>();
85     private final List<Connection> mUnmodifiableConferenceableConnections =
86             Collections.unmodifiableList(mConferenceableConnections);
87 
88     private String mTelecomCallId;
89     private PhoneAccountHandle mPhoneAccount;
90     private CallAudioState mCallAudioState;
91     private CallEndpoint mCallEndpoint;
92     private int mState = Connection.STATE_NEW;
93     private DisconnectCause mDisconnectCause;
94     private int mConnectionCapabilities;
95     private int mConnectionProperties;
96     private String mDisconnectMessage;
97     private long mConnectTimeMillis = CONNECT_TIME_NOT_SPECIFIED;
98     private long mConnectionStartElapsedRealTime = CONNECT_TIME_NOT_SPECIFIED;
99     private StatusHints mStatusHints;
100     private Bundle mExtras;
101     private Set<String> mPreviousExtraKeys;
102     private final Object mExtrasLock = new Object();
103     private Uri mAddress;
104     private int mAddressPresentation;
105     private String mCallerDisplayName;
106     private int mCallerDisplayNamePresentation;
107     private int mCallDirection;
108     private boolean mRingbackRequested = false;
109     private boolean mIsMultiparty = true;
110 
111     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 
120     /**
121      * Constructs a new Conference with a mandatory {@link PhoneAccountHandle}
122      *
123      * @param phoneAccount The {@code PhoneAccountHandle} associated with the conference.
124      */
Conference(PhoneAccountHandle phoneAccount)125     public Conference(PhoneAccountHandle phoneAccount) {
126         mPhoneAccount = phoneAccount;
127     }
128 
129     /**
130      * Returns the telecom internal call ID associated with this conference.
131      * <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.
135      *
136      * @return The telecom call ID.
137      * @hide
138      */
139     @SystemApi
getTelecomCallId()140     public final @NonNull String getTelecomCallId() {
141         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      */
setTelecomCallId(String telecomCallId)150     public final void setTelecomCallId(String telecomCallId) {
151         mTelecomCallId = telecomCallId;
152     }
153 
154     /**
155      * 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      */
getPhoneAccountHandle()159     public final PhoneAccountHandle getPhoneAccountHandle() {
160         return mPhoneAccount;
161     }
162 
163     /**
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      */
getConnections()168     public final List<Connection> getConnections() {
169         return mUnmodifiableChildConnections;
170     }
171 
172     /**
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      */
getState()177     public final int getState() {
178         return mState;
179     }
180 
181     /**
182      * Returns whether this conference is requesting that the system play a ringback tone
183      * 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.
185      */
isRingbackRequested()186     public final boolean isRingbackRequested() {
187         return mRingbackRequested;
188     }
189 
190     /**
191      * Returns the capabilities of the conference. See {@code CAPABILITY_*} constants in class
192      * {@link Connection} for valid values.
193      *
194      * @return A bitmask of the capabilities of the conference call.
195      */
getConnectionCapabilities()196     public final int getConnectionCapabilities() {
197         return mConnectionCapabilities;
198     }
199 
200     /**
201      * 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      */
getConnectionProperties()206     public final int getConnectionProperties() {
207         return mConnectionProperties;
208     }
209 
210     /**
211      * @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.
214      * @deprecated Use {@link #getCallAudioState()} instead.
215      * @hide
216      */
217     @Deprecated
218     @SystemApi
getAudioState()219     public final AudioState getAudioState() {
220         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.
227      * @deprecated Use {@link #getCurrentCallEndpoint()},
228      * {@link #onAvailableCallEndpointsChanged(List)} and
229      * {@link #onMuteStateChanged(boolean)} instead.
230      */
231     @Deprecated
getCallAudioState()232     public final CallAudioState getCallAudioState() {
233         return mCallAudioState;
234     }
235 
236     /**
237      * Obtains the current CallEndpoint.
238      *
239      * @return An object encapsulating the CallEndpoint.
240      */
241     @NonNull
getCurrentCallEndpoint()242     public final CallEndpoint getCurrentCallEndpoint() {
243         return mCallEndpoint;
244     }
245 
246     /**
247      * Returns VideoProvider of the primary call. This can be null.
248      */
getVideoProvider()249     public VideoProvider getVideoProvider() {
250         return null;
251     }
252 
253     /**
254      * Returns video state of the primary call.
255      */
getVideoState()256     public int getVideoState() {
257         return VideoProfile.STATE_AUDIO_ONLY;
258     }
259 
260     /**
261      * Notifies the {@link Conference} when the Conference and all it's {@link Connection}s should
262      * be disconnected.
263      */
onDisconnect()264     public void onDisconnect() {}
265 
266     /**
267      * Notifies the {@link Conference} when the specified {@link Connection} should be separated
268      * from the conference call.
269      *
270      * @param connection The connection to separate.
271      */
onSeparate(Connection connection)272     public void onSeparate(Connection connection) {}
273 
274     /**
275      * Notifies the {@link Conference} when the specified {@link Connection} should merged with the
276      * conference call.
277      *
278      * @param connection The {@code Connection} to merge.
279      */
onMerge(Connection connection)280     public void onMerge(Connection connection) {}
281 
282     /**
283      * Notifies the {@link Conference} when it should be put on hold.
284      */
onHold()285     public void onHold() {}
286 
287     /**
288      * Notifies the {@link Conference} when it should be moved from a held to active state.
289      */
onUnhold()290     public void onUnhold() {}
291 
292     /**
293      * 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}.
295      */
onMerge()296     public void onMerge() {}
297 
298     /**
299      * 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}.
301      */
onSwap()302     public void onSwap() {}
303 
304     /**
305      * Notifies the {@link Conference} of a request to play a DTMF tone.
306      *
307      * @param c A DTMF character.
308      */
onPlayDtmfTone(char c)309     public void onPlayDtmfTone(char c) {}
310 
311     /**
312      * Notifies the {@link Conference} of a request to stop any currently playing DTMF tones.
313      */
onStopDtmfTone()314     public void onStopDtmfTone() {}
315 
316     /**
317      * Notifies the {@link Conference} that the {@link #getAudioState()} property has a new value.
318      *
319      * @param state The new call audio state.
320      * @deprecated Use {@link #onCallAudioStateChanged(CallAudioState)} instead.
321      * @hide
322      */
323     @SystemApi
324     @Deprecated
onAudioStateChanged(AudioState state)325     public void onAudioStateChanged(AudioState state) {}
326 
327     /**
328      * Notifies the {@link Conference} that the {@link #getCallAudioState()} property has a new
329      * value.
330      *
331      * @param state The new call audio state.
332      * @deprecated Use {@link #onCallEndpointChanged(CallEndpoint)},
333      * {@link #onAvailableCallEndpointsChanged(List)} and
334      * {@link #onMuteStateChanged(boolean)} instead.
335      */
336     @Deprecated
onCallAudioStateChanged(CallAudioState state)337     public void onCallAudioStateChanged(CallAudioState state) {}
338 
339     /**
340      * Notifies the {@link Conference} that the audio endpoint has been changed.
341      *
342      * @param callEndpoint The new call endpoint.
343      */
onCallEndpointChanged(@onNull CallEndpoint callEndpoint)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      */
onAvailableCallEndpointsChanged(@onNull List<CallEndpoint> availableEndpoints)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      */
onMuteStateChanged(boolean isMuted)358     public void onMuteStateChanged(boolean isMuted) {}
359 
360     /**
361      * Notifies the {@link Conference} that a {@link Connection} has been added to it.
362      *
363      * @param connection The newly added connection.
364      */
onConnectionAdded(Connection connection)365     public void onConnectionAdded(Connection connection) {}
366 
367     /**
368      * 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
370      */
onAddConferenceParticipants(@onNull List<Uri> participants)371     public void onAddConferenceParticipants(@NonNull List<Uri> participants) {}
372 
373     /**
374      * 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      */
onAnswer(@ideoProfile.VideoState int videoState)381     public void onAnswer(@VideoProfile.VideoState int videoState) {}
382 
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      */
onAnswer()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      */
onReject()400     public void onReject() {}
401 
402     /**
403      * Sets state to be on hold.
404      */
setOnHold()405     public final void setOnHold() {
406         setState(Connection.STATE_HOLDING);
407     }
408 
409     /**
410      * Sets state to be dialing.
411      */
setDialing()412     public final void setDialing() {
413         setState(Connection.STATE_DIALING);
414     }
415 
416     /**
417      * Sets state to be ringing.
418      */
setRinging()419     public final void setRinging() {
420         setState(Connection.STATE_RINGING);
421     }
422 
423     /**
424      * Sets state to be active.
425      */
setActive()426     public final void setActive() {
427         setRingbackRequested(false);
428         setState(Connection.STATE_ACTIVE);
429     }
430 
431     /**
432      * Sets state to disconnected.
433      *
434      * @param disconnectCause The reason for the disconnection, as described by
435      *     {@link android.telecom.DisconnectCause}.
436      */
setDisconnected(DisconnectCause disconnectCause)437     public final void setDisconnected(DisconnectCause disconnectCause) {
438         mDisconnectCause = disconnectCause;;
439         setState(Connection.STATE_DISCONNECTED);
440         for (Listener l : mListeners) {
441             l.onDisconnected(this, mDisconnectCause);
442         }
443     }
444 
445     /**
446      * @return The {@link DisconnectCause} for this connection.
447      */
getDisconnectCause()448     public final DisconnectCause getDisconnectCause() {
449         return mDisconnectCause;
450     }
451 
452     /**
453      * Sets the capabilities of a conference. See {@code CAPABILITY_*} constants of class
454      * {@link Connection} for valid values.
455      *
456      * @param connectionCapabilities A bitmask of the {@code Capabilities} of the conference call.
457      */
setConnectionCapabilities(int connectionCapabilities)458     public final void setConnectionCapabilities(int connectionCapabilities) {
459         if (connectionCapabilities != mConnectionCapabilities) {
460             mConnectionCapabilities = connectionCapabilities;
461 
462             for (Listener l : mListeners) {
463                 l.onConnectionCapabilitiesChanged(this, mConnectionCapabilities);
464             }
465         }
466     }
467 
468     /**
469      * 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      */
setConnectionProperties(int connectionProperties)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     /**
485      * 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      */
addConnection(Connection connection)490     public final boolean addConnection(Connection connection) {
491         Log.d(this, "Connection=%s, connection=", connection);
492         if (connection != null && !mChildConnections.contains(connection)) {
493             if (connection.setConference(this)) {
494                 mChildConnections.add(connection);
495                 onConnectionAdded(connection);
496                 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.
509      */
removeConnection(Connection connection)510     public final void removeConnection(Connection connection) {
511         Log.d(this, "removing %s from %s", connection, mChildConnections);
512         if (connection != null && mChildConnections.remove(connection)) {
513             connection.resetConference();
514             for (Listener l : mListeners) {
515                 l.onConnectionRemoved(this, connection);
516             }
517         }
518     }
519 
520     /**
521      * Sets the connections with which this connection can be conferenced.
522      *
523      * @param conferenceableConnections The set of connections this connection can conference with.
524      */
setConferenceableConnections(List<Connection> conferenceableConnections)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 
538     /**
539      * 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      */
setRingbackRequested(boolean ringback)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     /**
554      * Set the video state for the conference.
555      * Valid values: {@link VideoProfile#STATE_AUDIO_ONLY},
556      * {@link VideoProfile#STATE_BIDIRECTIONAL},
557      * {@link VideoProfile#STATE_TX_ENABLED},
558      * {@link VideoProfile#STATE_RX_ENABLED}.
559      *
560      * @param videoState The new video state.
561      */
setVideoState(Connection c, int videoState)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.
574      */
setVideoProvider(Connection c, Connection.VideoProvider videoProvider)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 
fireOnConferenceableConnectionsChanged()583     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      */
getConferenceableConnections()592     public final List<Connection> getConferenceableConnections() {
593         return mUnmodifiableConferenceableConnections;
594     }
595 
596     /**
597      * Tears down the conference object and any of its current connections.
598      */
destroy()599     public final void destroy() {
600         Log.d(this, "destroying conference : %s", this);
601         // Tear down the children.
602         for (Connection connection : mChildConnections) {
603             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");
610             setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
611         }
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      */
addListener(Listener listener)626     final Conference addListener(Listener listener) {
627         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      */
removeListener(Listener listener)638     final Conference removeListener(Listener listener) {
639         mListeners.remove(listener);
640         return this;
641     }
642 
643     /**
644      * 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.
648      * @hide
649      */
650     @SystemApi
getPrimaryConnection()651     public Connection getPrimaryConnection() {
652         if (mUnmodifiableChildConnections == null || mUnmodifiableChildConnections.isEmpty()) {
653             return null;
654         }
655         return mUnmodifiableChildConnections.get(0);
656     }
657 
658     /**
659      * @hide
660      * @deprecated Use {@link #setConnectionTime}.
661      */
662     @Deprecated
663     @SystemApi
setConnectTimeMillis(long connectTimeMillis)664     public final void setConnectTimeMillis(long connectTimeMillis) {
665         setConnectionTime(connectTimeMillis);
666     }
667 
668     /**
669      * 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()}.
673      * <p>
674      * When setting the connection time, you should always set the connection elapsed time via
675      * {@link #setConnectionStartElapsedRealtimeMillis(long)} to ensure the duration is reflected.
676      *
677      * @param connectionTimeMillis The connection time, in milliseconds, as returned by
678      *                             {@link System#currentTimeMillis()}.
679      */
setConnectionTime(@ntRangefrom = 0) long connectionTimeMillis)680     public final void setConnectionTime(@IntRange(from = 0) long connectionTimeMillis) {
681         mConnectTimeMillis = connectionTimeMillis;
682     }
683 
684     /**
685      * Sets the start time of the {@link Conference} which is the basis for the determining the
686      * duration of the {@link Conference}.
687      * <p>
688      * 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
692      * {@link #setConnectionTime(long)}.
693      *
694      * @param connectionStartElapsedRealTime The connection time, as measured by
695      * {@link SystemClock#elapsedRealtime()}.
696      * @deprecated use {@link #setConnectionStartElapsedRealtimeMillis(long)} instead.
697      */
698     @Deprecated
setConnectionStartElapsedRealTime(long connectionStartElapsedRealTime)699     public final void setConnectionStartElapsedRealTime(long connectionStartElapsedRealTime) {
700         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      */
setConnectionStartElapsedRealtimeMillis( @lapsedRealtimeLong long connectionStartElapsedRealTime)716     public final void setConnectionStartElapsedRealtimeMillis(
717             @ElapsedRealtimeLong long connectionStartElapsedRealTime) {
718         mConnectionStartElapsedRealTime = connectionStartElapsedRealTime;
719     }
720 
721     /**
722      * @hide
723      * @deprecated Use {@link #getConnectionTime}.
724      */
725     @Deprecated
726     @SystemApi
getConnectTimeMillis()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
733      * {@link #CONNECT_TIME_NOT_SPECIFIED} indicates that Telecom should determine the start time
734      * of the conference.
735      *
736      * @return The time at which the {@code Conference} was connected.
737      */
getConnectionTime()738     public final @IntRange(from = 0) long getConnectionTime() {
739         return mConnectTimeMillis;
740     }
741 
742     /**
743      * 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.
746      * <p>
747      * 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).
749      * <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.
753      *
754      * @return The elapsed time at which the {@link Conference} was connected.
755      */
getConnectionStartElapsedRealtimeMillis()756     public final @ElapsedRealtimeLong long getConnectionStartElapsedRealtimeMillis() {
757         return mConnectionStartElapsedRealTime;
758     }
759 
760     /**
761      * 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      */
setCallAudioState(CallAudioState state)766     final void setCallAudioState(CallAudioState state) {
767         Log.d(this, "setCallAudioState %s", state);
768         mCallAudioState = state;
769         onAudioStateChanged(getAudioState());
770         onCallAudioStateChanged(state);
771     }
772 
773     /**
774      * Inform this Conference that the audio endpoint has been changed.
775      *
776      * @param endpoint The new call endpoint.
777      * @hide
778      */
setCallEndpoint(CallEndpoint endpoint)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      */
setAvailableCallEndpoints(List<CallEndpoint> availableEndpoints)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      */
setMuteState(boolean isMuted)802     final void setMuteState(boolean isMuted) {
803         Log.d(this, "setMuteState %s", isMuted);
804         onMuteStateChanged(isMuted);
805     }
806 
setState(int newState)807     private void setState(int newState) {
808         if (mState != newState) {
809             int oldState = mState;
810             mState = newState;
811             for (Listener l : mListeners) {
812                 l.onStateChanged(this, oldState, newState);
813             }
814         }
815     }
816 
817     private static class FailureSignalingConference extends Conference {
818         private boolean mImmutable = false;
FailureSignalingConference(DisconnectCause disconnectCause, PhoneAccountHandle phoneAccount)819         public FailureSignalingConference(DisconnectCause disconnectCause,
820                 PhoneAccountHandle phoneAccount) {
821             super(phoneAccount);
822             setDisconnected(disconnectCause);
823             mImmutable = true;
824         }
checkImmutable()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      */
createFailedConference( @onNull DisconnectCause disconnectCause, @NonNull PhoneAccountHandle phoneAccount)843     public @NonNull static Conference createFailedConference(
844             @NonNull DisconnectCause disconnectCause, @NonNull PhoneAccountHandle phoneAccount) {
845         return new FailureSignalingConference(disconnectCause, phoneAccount);
846     }
847 
clearConferenceableList()848     private final void clearConferenceableList() {
849         for (Connection c : mConferenceableConnections) {
850             c.removeConnectionListener(mConnectionDeathListener);
851         }
852         mConferenceableConnections.clear();
853     }
854 
855     @Override
toString()856     public String toString() {
857         return String.format(Locale.US,
858                 "[State: %s,Capabilites: %s, VideoState: %s, VideoProvider: %s,"
859                 + "isRingbackRequested: %s, ThisObject %s]",
860                 Connection.stateToString(mState),
861                 Call.Details.capabilitiesToString(mConnectionCapabilities),
862                 getVideoState(),
863                 getVideoProvider(),
864                 isRingbackRequested() ? "Y" : "N",
865                 super.toString());
866     }
867 
868     /**
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      */
setStatusHints(StatusHints statusHints)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      */
getStatusHints()883     public final StatusHints getStatusHints() {
884         return mStatusHints;
885     }
886 
887     /**
888      * 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>
893      * Alternatively you may use the {@link #putExtras(Bundle)}, and
894      * {@link #removeExtras(String...)} methods to modify the extras.
895      * <p>
896      * No assumptions should be made as to how an In-Call UI or service will handle these extras.
897      * Keys should be fully qualified (e.g., com.example.extras.MY_EXTRA) to avoid conflicts.
898      *
899      * @param extras The extras associated with this {@code Conference}.
900      */
setExtras(@ullable Bundle extras)901     public final void setExtras(@Nullable Bundle extras) {
902         // 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                 }
916 
917                 if (!toRemove.isEmpty()) {
918                     removeExtras(toRemove);
919                 }
920             }
921 
922             // 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>();
926             }
927             mPreviousExtraKeys.clear();
928             if (extras != null) {
929                 mPreviousExtraKeys.addAll(extras.keySet());
930             }
931         }
932     }
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      */
putExtras(@onNull Bundle extras)943     public final void putExtras(@NonNull Bundle extras) {
944         if (extras == null) {
945             return;
946         }
947 
948         // 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);
957         }
958 
959         for (Listener l : mListeners) {
960             l.onExtrasChanged(this, new Bundle(listenersBundle));
961         }
962     }
963 
964     /**
965      * Adds a boolean extra to this {@link Conference}.
966      *
967      * @param key The extra key.
968      * @param value The value.
969      * @hide
970      */
putExtra(String key, boolean value)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      */
putExtra(String key, int value)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      */
putExtra(String key, String value)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     /**
1004      * Removes extras from this {@link Conference}.
1005      *
1006      * @param keys The keys of the extras to remove.
1007      */
removeExtras(List<String> keys)1008     public final void removeExtras(List<String> keys) {
1009         if (keys == null || keys.isEmpty()) {
1010             return;
1011         }
1012 
1013         synchronized (mExtrasLock) {
1014             if (mExtras != null) {
1015                 for (String key : keys) {
1016                     mExtras.remove(key);
1017                 }
1018             }
1019         }
1020 
1021         List<String> unmodifiableKeys = Collections.unmodifiableList(keys);
1022         for (Listener l : mListeners) {
1023             l.onExtrasRemoved(this, unmodifiableKeys);
1024         }
1025     }
1026 
1027     /**
1028      * Removes extras from this {@link Conference}.
1029      *
1030      * @param keys The keys of the extras to remove.
1031      */
removeExtras(String .... keys)1032     public final void removeExtras(String ... keys) {
1033         removeExtras(Arrays.asList(keys));
1034     }
1035 
1036     /**
1037      * Returns the extras associated with this conference.
1038      * <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)}.
1047      *
1048      * @return The extras associated with this connection.
1049      */
getExtras()1050     public final Bundle getExtras() {
1051         return mExtras;
1052     }
1053 
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      */
onExtrasChanged(Bundle extras)1064     public void onExtrasChanged(Bundle extras) {}
1065 
1066     /**
1067      * 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.
1069      * 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.
1078      * <p>
1079      * This API is intended for use by the platform Telephony stack only.
1080      *
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      */
1085     @SystemApi
1086     @RequiresPermission(MODIFY_PHONE_STATE)
setConferenceState(boolean isConference)1087     public void setConferenceState(boolean isConference) {
1088         mIsMultiparty = isConference;
1089         for (Listener l : mListeners) {
1090             l.onConferenceStateChanged(this, isConference);
1091         }
1092     }
1093 
1094     /**
1095      * 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)
setCallDirection(@all.Details.CallDirection int callDirection)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 
1112     /**
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      */
isMultiparty()1123     public boolean isMultiparty() {
1124         return mIsMultiparty;
1125     }
1126 
1127     /**
1128      * Sets the address of this {@link Conference}.  Used when {@link #setConferenceState(boolean)}
1129      * is called to mark a conference temporarily as NOT a conference.
1130      * <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.
1133      *
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      */
1139     @SystemApi
1140     @RequiresPermission(MODIFY_PHONE_STATE)
setAddress(@onNull Uri address, @TelecomManager.Presentation int presentation)1141     public final void setAddress(@NonNull Uri address,
1142             @TelecomManager.Presentation int presentation) {
1143         Log.d(this, "setAddress %s", address);
1144         mAddress = address;
1145         mAddressPresentation = presentation;
1146         for (Listener l : mListeners) {
1147             l.onAddressChanged(this, address, presentation);
1148         }
1149     }
1150 
1151     /**
1152      * 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      */
getAddress()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
1174      *     temporarily "not a conference"; we need to present the correct address presentation in
1175      *     the in-call UI.</li>
1176      *     <li>When the conference is not hosted on the current device, we need to know the address
1177      *     presentation information for the purpose of showing the original address to the user, as
1178      *     well as for logging to the call log.</li>
1179      * </ol>
1180      * @return The address presentation of the conference.
1181      * @hide
1182      */
getAddressPresentation()1183     public final @TelecomManager.Presentation int getAddressPresentation() {
1184         return mAddressPresentation;
1185     }
1186 
1187     /**
1188      * @return The caller display name (CNAP).
1189      * @hide
1190      */
getCallerDisplayName()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      */
getCallerDisplayNamePresentation()1200     public final int getCallerDisplayNamePresentation() {
1201         return mCallerDisplayNamePresentation;
1202     }
1203 
1204     /**
1205      * @return The call direction of this conference. Only applicable when
1206      * {@link #setConferenceState(boolean)} is set to false.
1207      * @hide
1208      */
getCallDirection()1209     public final @Call.Details.CallDirection int getCallDirection() {
1210         return mCallDirection;
1211     }
1212 
1213     /**
1214      * 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.
1217      * <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.
1220      *
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      */
1226     @SystemApi
setCallerDisplayName(@onNull String callerDisplayName, @TelecomManager.Presentation int presentation)1227     public final void setCallerDisplayName(@NonNull String callerDisplayName,
1228             @TelecomManager.Presentation int presentation) {
1229         Log.d(this, "setCallerDisplayName %s", callerDisplayName);
1230         mCallerDisplayName = callerDisplayName;
1231         mCallerDisplayNamePresentation = presentation;
1232         for (Listener l : mListeners) {
1233             l.onCallerDisplayNameChanged(this, callerDisplayName, presentation);
1234         }
1235     }
1236 
1237     /**
1238      * Handles a change to extras received from Telecom.
1239      *
1240      * @param extras The new extras.
1241      * @hide
1242      */
handleExtrasChanged(Bundle extras)1243     final void handleExtrasChanged(Bundle extras) {
1244         Bundle b = null;
1245         synchronized (mExtrasLock) {
1246             mExtras = extras;
1247             if (mExtras != null) {
1248                 b = new Bundle(mExtras);
1249             }
1250         }
1251         onExtrasChanged(b);
1252     }
1253 
1254     /**
1255      * 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>
1299      *
1300      * @param event The conference event.
1301      * @param extras Optional bundle containing extra information associated with the event.
1302      */
sendConferenceEvent(@onNull String event, @Nullable Bundle extras)1303     public void sendConferenceEvent(@NonNull String event, @Nullable Bundle extras) {
1304         for (Listener l : mListeners) {
1305             l.onConnectionEvent(this, event, extras);
1306         }
1307     }
1308 }
1309