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.annotation.TestApi;
28 import android.net.Uri;
29 import android.os.Bundle;
30 import android.os.SystemClock;
31 import android.telecom.Connection.VideoProvider;
32 import android.util.ArraySet;
33 
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.Collections;
37 import java.util.List;
38 import java.util.Locale;
39 import java.util.Set;
40 import java.util.concurrent.CopyOnWriteArrayList;
41 import java.util.concurrent.CopyOnWriteArraySet;
42 
43 /**
44  * Represents a conference call which can contain any number of {@link Connection} objects.
45  */
46 public abstract class Conference extends Conferenceable {
47 
48     /**
49      * Used to indicate that the conference connection time is not specified.  If not specified,
50      * Telecom will set the connect time.
51      */
52     public static final long CONNECT_TIME_NOT_SPECIFIED = 0;
53 
54     /** @hide */
55     abstract static class Listener {
onStateChanged(Conference conference, int oldState, int newState)56         public void onStateChanged(Conference conference, int oldState, int newState) {}
onDisconnected(Conference conference, DisconnectCause disconnectCause)57         public void onDisconnected(Conference conference, DisconnectCause disconnectCause) {}
onConnectionAdded(Conference conference, Connection connection)58         public void onConnectionAdded(Conference conference, Connection connection) {}
onConnectionRemoved(Conference conference, Connection connection)59         public void onConnectionRemoved(Conference conference, Connection connection) {}
onConferenceableConnectionsChanged( Conference conference, List<Connection> conferenceableConnections)60         public void onConferenceableConnectionsChanged(
61                 Conference conference, List<Connection> conferenceableConnections) {}
onDestroyed(Conference conference)62         public void onDestroyed(Conference conference) {}
onConnectionCapabilitiesChanged( Conference conference, int connectionCapabilities)63         public void onConnectionCapabilitiesChanged(
64                 Conference conference, int connectionCapabilities) {}
onConnectionPropertiesChanged( Conference conference, int connectionProperties)65         public void onConnectionPropertiesChanged(
66                 Conference conference, int connectionProperties) {}
onVideoStateChanged(Conference c, int videoState)67         public void onVideoStateChanged(Conference c, int videoState) { }
onVideoProviderChanged(Conference c, Connection.VideoProvider videoProvider)68         public void onVideoProviderChanged(Conference c, Connection.VideoProvider videoProvider) {}
onStatusHintsChanged(Conference conference, StatusHints statusHints)69         public void onStatusHintsChanged(Conference conference, StatusHints statusHints) {}
onExtrasChanged(Conference c, Bundle extras)70         public void onExtrasChanged(Conference c, Bundle extras) {}
onExtrasRemoved(Conference c, List<String> keys)71         public void onExtrasRemoved(Conference c, List<String> keys) {}
onConferenceStateChanged(Conference c, boolean isConference)72         public void onConferenceStateChanged(Conference c, boolean isConference) {}
onAddressChanged(Conference c, Uri newAddress, int presentation)73         public void onAddressChanged(Conference c, Uri newAddress, int presentation) {}
onConnectionEvent(Conference c, String event, Bundle extras)74         public void onConnectionEvent(Conference c, String event, Bundle extras) {}
onCallerDisplayNameChanged( Conference c, String callerDisplayName, int presentation)75         public void onCallerDisplayNameChanged(
76                 Conference c, String callerDisplayName, int presentation) {}
onCallDirectionChanged(Conference c, int callDirection)77         public void onCallDirectionChanged(Conference c, int callDirection) {}
onRingbackRequested(Conference c, boolean ringback)78         public void onRingbackRequested(Conference c, boolean ringback) {}
79     }
80 
81     private final Set<Listener> mListeners = new CopyOnWriteArraySet<>();
82     private final List<Connection> mChildConnections = new CopyOnWriteArrayList<>();
83     private final List<Connection> mUnmodifiableChildConnections =
84             Collections.unmodifiableList(mChildConnections);
85     private final List<Connection> mConferenceableConnections = new ArrayList<>();
86     private final List<Connection> mUnmodifiableConferenceableConnections =
87             Collections.unmodifiableList(mConferenceableConnections);
88 
89     private String mTelecomCallId;
90     private PhoneAccountHandle mPhoneAccount;
91     private CallAudioState mCallAudioState;
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
140     @TestApi
getTelecomCallId()141     public final @NonNull String getTelecomCallId() {
142         return mTelecomCallId;
143     }
144 
145     /**
146      * Sets the telecom internal call ID associated with this conference.
147      *
148      * @param telecomCallId The telecom call ID.
149      * @hide
150      */
setTelecomCallId(String telecomCallId)151     public final void setTelecomCallId(String telecomCallId) {
152         mTelecomCallId = telecomCallId;
153     }
154 
155     /**
156      * Returns the {@link PhoneAccountHandle} the conference call is being placed through.
157      *
158      * @return A {@code PhoneAccountHandle} object representing the PhoneAccount of the conference.
159      */
getPhoneAccountHandle()160     public final PhoneAccountHandle getPhoneAccountHandle() {
161         return mPhoneAccount;
162     }
163 
164     /**
165      * Returns the list of connections currently associated with the conference call.
166      *
167      * @return A list of {@code Connection} objects which represent the children of the conference.
168      */
getConnections()169     public final List<Connection> getConnections() {
170         return mUnmodifiableChildConnections;
171     }
172 
173     /**
174      * Gets the state of the conference call. See {@link Connection} for valid values.
175      *
176      * @return A constant representing the state the conference call is currently in.
177      */
getState()178     public final int getState() {
179         return mState;
180     }
181 
182     /**
183      * Returns whether this conference is requesting that the system play a ringback tone
184      * on its behalf. A ringback tone may be played when an outgoing conference is in the process of
185      * connecting to give the user an audible indication of that process.
186      */
isRingbackRequested()187     public final boolean isRingbackRequested() {
188         return mRingbackRequested;
189     }
190 
191     /**
192      * Returns the capabilities of the conference. See {@code CAPABILITY_*} constants in class
193      * {@link Connection} for valid values.
194      *
195      * @return A bitmask of the capabilities of the conference call.
196      */
getConnectionCapabilities()197     public final int getConnectionCapabilities() {
198         return mConnectionCapabilities;
199     }
200 
201     /**
202      * Returns the properties of the conference. See {@code PROPERTY_*} constants in class
203      * {@link Connection} for valid values.
204      *
205      * @return A bitmask of the properties of the conference call.
206      */
getConnectionProperties()207     public final int getConnectionProperties() {
208         return mConnectionProperties;
209     }
210 
211     /**
212      * @return The audio state of the conference, describing how its audio is currently
213      *         being routed by the system. This is {@code null} if this Conference
214      *         does not directly know about its audio state.
215      * @deprecated Use {@link #getCallAudioState()} instead.
216      * @hide
217      */
218     @Deprecated
219     @SystemApi
getAudioState()220     public final AudioState getAudioState() {
221         return new AudioState(mCallAudioState);
222     }
223 
224     /**
225      * @return The audio state of the conference, describing how its audio is currently
226      *         being routed by the system. This is {@code null} if this Conference
227      *         does not directly know about its audio state.
228      */
getCallAudioState()229     public final CallAudioState getCallAudioState() {
230         return mCallAudioState;
231     }
232 
233     /**
234      * Returns VideoProvider of the primary call. This can be null.
235      */
getVideoProvider()236     public VideoProvider getVideoProvider() {
237         return null;
238     }
239 
240     /**
241      * Returns video state of the primary call.
242      */
getVideoState()243     public int getVideoState() {
244         return VideoProfile.STATE_AUDIO_ONLY;
245     }
246 
247     /**
248      * Notifies the {@link Conference} when the Conference and all it's {@link Connection}s should
249      * be disconnected.
250      */
onDisconnect()251     public void onDisconnect() {}
252 
253     /**
254      * Notifies the {@link Conference} when the specified {@link Connection} should be separated
255      * from the conference call.
256      *
257      * @param connection The connection to separate.
258      */
onSeparate(Connection connection)259     public void onSeparate(Connection connection) {}
260 
261     /**
262      * Notifies the {@link Conference} when the specified {@link Connection} should merged with the
263      * conference call.
264      *
265      * @param connection The {@code Connection} to merge.
266      */
onMerge(Connection connection)267     public void onMerge(Connection connection) {}
268 
269     /**
270      * Notifies the {@link Conference} when it should be put on hold.
271      */
onHold()272     public void onHold() {}
273 
274     /**
275      * Notifies the {@link Conference} when it should be moved from a held to active state.
276      */
onUnhold()277     public void onUnhold() {}
278 
279     /**
280      * Notifies the {@link Conference} when the child calls should be merged.  Only invoked if the
281      * conference contains the capability {@link Connection#CAPABILITY_MERGE_CONFERENCE}.
282      */
onMerge()283     public void onMerge() {}
284 
285     /**
286      * Notifies the {@link Conference} when the child calls should be swapped. Only invoked if the
287      * conference contains the capability {@link Connection#CAPABILITY_SWAP_CONFERENCE}.
288      */
onSwap()289     public void onSwap() {}
290 
291     /**
292      * Notifies the {@link Conference} of a request to play a DTMF tone.
293      *
294      * @param c A DTMF character.
295      */
onPlayDtmfTone(char c)296     public void onPlayDtmfTone(char c) {}
297 
298     /**
299      * Notifies the {@link Conference} of a request to stop any currently playing DTMF tones.
300      */
onStopDtmfTone()301     public void onStopDtmfTone() {}
302 
303     /**
304      * Notifies the {@link Conference} that the {@link #getAudioState()} property has a new value.
305      *
306      * @param state The new call audio state.
307      * @deprecated Use {@link #onCallAudioStateChanged(CallAudioState)} instead.
308      * @hide
309      */
310     @SystemApi
311     @Deprecated
onAudioStateChanged(AudioState state)312     public void onAudioStateChanged(AudioState state) {}
313 
314     /**
315      * Notifies the {@link Conference} that the {@link #getCallAudioState()} property has a new
316      * value.
317      *
318      * @param state The new call audio state.
319      */
onCallAudioStateChanged(CallAudioState state)320     public void onCallAudioStateChanged(CallAudioState state) {}
321 
322     /**
323      * Notifies the {@link Conference} that a {@link Connection} has been added to it.
324      *
325      * @param connection The newly added connection.
326      */
onConnectionAdded(Connection connection)327     public void onConnectionAdded(Connection connection) {}
328 
329     /**
330      * Notifies the {@link Conference} of a request to add a new participants to the conference call
331      * @param participants that will be added to this conference call
332      */
onAddConferenceParticipants(@onNull List<Uri> participants)333     public void onAddConferenceParticipants(@NonNull List<Uri> participants) {}
334 
335     /**
336      * Notifies this Conference, which is in {@code STATE_RINGING}, of
337      * a request to accept.
338      * For managed {@link ConnectionService}s, this will be called when the user answers a call via
339      * the default dialer's {@link InCallService}.
340      *
341      * @param videoState The video state in which to answer the connection.
342      */
onAnswer(int videoState)343     public void onAnswer(int videoState) {}
344 
345     /**
346      * Notifies this Conference, which is in {@code STATE_RINGING}, of
347      * a request to accept.
348      * For managed {@link ConnectionService}s, this will be called when the user answers a call via
349      * the default dialer's {@link InCallService}.
350      * @hide
351      */
onAnswer()352     public final void onAnswer() {
353          onAnswer(VideoProfile.STATE_AUDIO_ONLY);
354     }
355 
356     /**
357      * Notifies this Conference, which is in {@code STATE_RINGING}, of
358      * a request to reject.
359      * For managed {@link ConnectionService}s, this will be called when the user rejects a call via
360      * the default dialer's {@link InCallService}.
361      */
onReject()362     public void onReject() {}
363 
364     /**
365      * Sets state to be on hold.
366      */
setOnHold()367     public final void setOnHold() {
368         setState(Connection.STATE_HOLDING);
369     }
370 
371     /**
372      * Sets state to be dialing.
373      */
setDialing()374     public final void setDialing() {
375         setState(Connection.STATE_DIALING);
376     }
377 
378     /**
379      * Sets state to be ringing.
380      */
setRinging()381     public final void setRinging() {
382         setState(Connection.STATE_RINGING);
383     }
384 
385     /**
386      * Sets state to be active.
387      */
setActive()388     public final void setActive() {
389         setRingbackRequested(false);
390         setState(Connection.STATE_ACTIVE);
391     }
392 
393     /**
394      * Sets state to disconnected.
395      *
396      * @param disconnectCause The reason for the disconnection, as described by
397      *     {@link android.telecom.DisconnectCause}.
398      */
setDisconnected(DisconnectCause disconnectCause)399     public final void setDisconnected(DisconnectCause disconnectCause) {
400         mDisconnectCause = disconnectCause;;
401         setState(Connection.STATE_DISCONNECTED);
402         for (Listener l : mListeners) {
403             l.onDisconnected(this, mDisconnectCause);
404         }
405     }
406 
407     /**
408      * @return The {@link DisconnectCause} for this connection.
409      */
getDisconnectCause()410     public final DisconnectCause getDisconnectCause() {
411         return mDisconnectCause;
412     }
413 
414     /**
415      * Sets the capabilities of a conference. See {@code CAPABILITY_*} constants of class
416      * {@link Connection} for valid values.
417      *
418      * @param connectionCapabilities A bitmask of the {@code Capabilities} of the conference call.
419      */
setConnectionCapabilities(int connectionCapabilities)420     public final void setConnectionCapabilities(int connectionCapabilities) {
421         if (connectionCapabilities != mConnectionCapabilities) {
422             mConnectionCapabilities = connectionCapabilities;
423 
424             for (Listener l : mListeners) {
425                 l.onConnectionCapabilitiesChanged(this, mConnectionCapabilities);
426             }
427         }
428     }
429 
430     /**
431      * Sets the properties of a conference. See {@code PROPERTY_*} constants of class
432      * {@link Connection} for valid values.
433      *
434      * @param connectionProperties A bitmask of the {@code Properties} of the conference call.
435      */
setConnectionProperties(int connectionProperties)436     public final void setConnectionProperties(int connectionProperties) {
437         if (connectionProperties != mConnectionProperties) {
438             mConnectionProperties = connectionProperties;
439 
440             for (Listener l : mListeners) {
441                 l.onConnectionPropertiesChanged(this, mConnectionProperties);
442             }
443         }
444     }
445 
446     /**
447      * Adds the specified connection as a child of this conference.
448      *
449      * @param connection The connection to add.
450      * @return True if the connection was successfully added.
451      */
addConnection(Connection connection)452     public final boolean addConnection(Connection connection) {
453         Log.d(this, "Connection=%s, connection=", connection);
454         if (connection != null && !mChildConnections.contains(connection)) {
455             if (connection.setConference(this)) {
456                 mChildConnections.add(connection);
457                 onConnectionAdded(connection);
458                 for (Listener l : mListeners) {
459                     l.onConnectionAdded(this, connection);
460                 }
461                 return true;
462             }
463         }
464         return false;
465     }
466 
467     /**
468      * Removes the specified connection as a child of this conference.
469      *
470      * @param connection The connection to remove.
471      */
removeConnection(Connection connection)472     public final void removeConnection(Connection connection) {
473         Log.d(this, "removing %s from %s", connection, mChildConnections);
474         if (connection != null && mChildConnections.remove(connection)) {
475             connection.resetConference();
476             for (Listener l : mListeners) {
477                 l.onConnectionRemoved(this, connection);
478             }
479         }
480     }
481 
482     /**
483      * Sets the connections with which this connection can be conferenced.
484      *
485      * @param conferenceableConnections The set of connections this connection can conference with.
486      */
setConferenceableConnections(List<Connection> conferenceableConnections)487     public final void setConferenceableConnections(List<Connection> conferenceableConnections) {
488         clearConferenceableList();
489         for (Connection c : conferenceableConnections) {
490             // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a
491             // small amount of items here.
492             if (!mConferenceableConnections.contains(c)) {
493                 c.addConnectionListener(mConnectionDeathListener);
494                 mConferenceableConnections.add(c);
495             }
496         }
497         fireOnConferenceableConnectionsChanged();
498     }
499 
500     /**
501      * Requests that the framework play a ringback tone. This is to be invoked by implementations
502      * that do not play a ringback tone themselves in the conference's audio stream.
503      *
504      * @param ringback Whether the ringback tone is to be played.
505      */
setRingbackRequested(boolean ringback)506     public final void setRingbackRequested(boolean ringback) {
507         if (mRingbackRequested != ringback) {
508             mRingbackRequested = ringback;
509             for (Listener l : mListeners) {
510                 l.onRingbackRequested(this, ringback);
511             }
512         }
513     }
514 
515     /**
516      * Set the video state for the conference.
517      * Valid values: {@link VideoProfile#STATE_AUDIO_ONLY},
518      * {@link VideoProfile#STATE_BIDIRECTIONAL},
519      * {@link VideoProfile#STATE_TX_ENABLED},
520      * {@link VideoProfile#STATE_RX_ENABLED}.
521      *
522      * @param videoState The new video state.
523      */
setVideoState(Connection c, int videoState)524     public final void setVideoState(Connection c, int videoState) {
525         Log.d(this, "setVideoState Conference: %s Connection: %s VideoState: %s",
526                 this, c, videoState);
527         for (Listener l : mListeners) {
528             l.onVideoStateChanged(this, videoState);
529         }
530     }
531 
532     /**
533      * Sets the video connection provider.
534      *
535      * @param videoProvider The video provider.
536      */
setVideoProvider(Connection c, Connection.VideoProvider videoProvider)537     public final void setVideoProvider(Connection c, Connection.VideoProvider videoProvider) {
538         Log.d(this, "setVideoProvider Conference: %s Connection: %s VideoState: %s",
539                 this, c, videoProvider);
540         for (Listener l : mListeners) {
541             l.onVideoProviderChanged(this, videoProvider);
542         }
543     }
544 
fireOnConferenceableConnectionsChanged()545     private final void fireOnConferenceableConnectionsChanged() {
546         for (Listener l : mListeners) {
547             l.onConferenceableConnectionsChanged(this, getConferenceableConnections());
548         }
549     }
550 
551     /**
552      * Returns the connections with which this connection can be conferenced.
553      */
getConferenceableConnections()554     public final List<Connection> getConferenceableConnections() {
555         return mUnmodifiableConferenceableConnections;
556     }
557 
558     /**
559      * Tears down the conference object and any of its current connections.
560      */
destroy()561     public final void destroy() {
562         Log.d(this, "destroying conference : %s", this);
563         // Tear down the children.
564         for (Connection connection : mChildConnections) {
565             Log.d(this, "removing connection %s", connection);
566             removeConnection(connection);
567         }
568 
569         // If not yet disconnected, set the conference call as disconnected first.
570         if (mState != Connection.STATE_DISCONNECTED) {
571             Log.d(this, "setting to disconnected");
572             setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
573         }
574 
575         // ...and notify.
576         for (Listener l : mListeners) {
577             l.onDestroyed(this);
578         }
579     }
580 
581     /**
582      * Add a listener to be notified of a state change.
583      *
584      * @param listener The new listener.
585      * @return This conference.
586      * @hide
587      */
addListener(Listener listener)588     final Conference addListener(Listener listener) {
589         mListeners.add(listener);
590         return this;
591     }
592 
593     /**
594      * Removes the specified listener.
595      *
596      * @param listener The listener to remove.
597      * @return This conference.
598      * @hide
599      */
removeListener(Listener listener)600     final Conference removeListener(Listener listener) {
601         mListeners.remove(listener);
602         return this;
603     }
604 
605     /**
606      * Retrieves the primary connection associated with the conference.  The primary connection is
607      * the connection from which the conference will retrieve its current state.
608      *
609      * @return The primary connection.
610      * @hide
611      */
612     @TestApi
613     @SystemApi
getPrimaryConnection()614     public Connection getPrimaryConnection() {
615         if (mUnmodifiableChildConnections == null || mUnmodifiableChildConnections.isEmpty()) {
616             return null;
617         }
618         return mUnmodifiableChildConnections.get(0);
619     }
620 
621     /**
622      * @hide
623      * @deprecated Use {@link #setConnectionTime}.
624      */
625     @Deprecated
626     @SystemApi
setConnectTimeMillis(long connectTimeMillis)627     public final void setConnectTimeMillis(long connectTimeMillis) {
628         setConnectionTime(connectTimeMillis);
629     }
630 
631     /**
632      * Sets the connection start time of the {@code Conference}.  This is used in the call log to
633      * indicate the date and time when the conference took place.
634      * <p>
635      * Should be specified in wall-clock time returned by {@link System#currentTimeMillis()}.
636      * <p>
637      * When setting the connection time, you should always set the connection elapsed time via
638      * {@link #setConnectionStartElapsedRealtimeMillis(long)} to ensure the duration is reflected.
639      *
640      * @param connectionTimeMillis The connection time, in milliseconds, as returned by
641      *                             {@link System#currentTimeMillis()}.
642      */
setConnectionTime(@ntRangefrom = 0) long connectionTimeMillis)643     public final void setConnectionTime(@IntRange(from = 0) long connectionTimeMillis) {
644         mConnectTimeMillis = connectionTimeMillis;
645     }
646 
647     /**
648      * Sets the start time of the {@link Conference} which is the basis for the determining the
649      * duration of the {@link Conference}.
650      * <p>
651      * You should use a value returned by {@link SystemClock#elapsedRealtime()} to ensure that time
652      * zone changes do not impact the conference duration.
653      * <p>
654      * When setting this, you should also set the connection time via
655      * {@link #setConnectionTime(long)}.
656      *
657      * @param connectionStartElapsedRealTime The connection time, as measured by
658      * {@link SystemClock#elapsedRealtime()}.
659      * @deprecated use {@link #setConnectionStartElapsedRealtimeMillis(long)} instead.
660      */
661     @Deprecated
setConnectionStartElapsedRealTime(long connectionStartElapsedRealTime)662     public final void setConnectionStartElapsedRealTime(long connectionStartElapsedRealTime) {
663         setConnectionStartElapsedRealtimeMillis(connectionStartElapsedRealTime);
664     }
665 
666     /**
667      * Sets the start time of the {@link Conference} which is the basis for the determining the
668      * duration of the {@link Conference}.
669      * <p>
670      * You should use a value returned by {@link SystemClock#elapsedRealtime()} to ensure that time
671      * zone changes do not impact the conference duration.
672      * <p>
673      * When setting this, you should also set the connection time via
674      * {@link #setConnectionTime(long)}.
675      *
676      * @param connectionStartElapsedRealTime The connection time, as measured by
677      * {@link SystemClock#elapsedRealtime()}.
678      */
setConnectionStartElapsedRealtimeMillis( @lapsedRealtimeLong long connectionStartElapsedRealTime)679     public final void setConnectionStartElapsedRealtimeMillis(
680             @ElapsedRealtimeLong long connectionStartElapsedRealTime) {
681         mConnectionStartElapsedRealTime = connectionStartElapsedRealTime;
682     }
683 
684     /**
685      * @hide
686      * @deprecated Use {@link #getConnectionTime}.
687      */
688     @Deprecated
689     @SystemApi
getConnectTimeMillis()690     public final long getConnectTimeMillis() {
691         return getConnectionTime();
692     }
693 
694     /**
695      * Retrieves the connection start time of the {@code Conference}, if specified.  A value of
696      * {@link #CONNECT_TIME_NOT_SPECIFIED} indicates that Telecom should determine the start time
697      * of the conference.
698      *
699      * @return The time at which the {@code Conference} was connected.
700      */
getConnectionTime()701     public final @IntRange(from = 0) long getConnectionTime() {
702         return mConnectTimeMillis;
703     }
704 
705     /**
706      * Retrieves the connection start time of the {@link Conference}, if specified.  A value of
707      * {@link #CONNECT_TIME_NOT_SPECIFIED} indicates that Telecom should determine the start time
708      * of the conference.
709      * <p>
710      * This is based on the value of {@link SystemClock#elapsedRealtime()} to ensure that it is not
711      * impacted by wall clock changes (user initiated, network initiated, time zone change, etc).
712      * <p>
713      * Note: This is only exposed for use by the Telephony framework which needs it to copy
714      * conference start times among conference participants.  It is exposed as a system API since it
715      * has no general use other than to the Telephony framework.
716      *
717      * @return The elapsed time at which the {@link Conference} was connected.
718      */
getConnectionStartElapsedRealtimeMillis()719     public final @ElapsedRealtimeLong long getConnectionStartElapsedRealtimeMillis() {
720         return mConnectionStartElapsedRealTime;
721     }
722 
723     /**
724      * Inform this Conference that the state of its audio output has been changed externally.
725      *
726      * @param state The new audio state.
727      * @hide
728      */
setCallAudioState(CallAudioState state)729     final void setCallAudioState(CallAudioState state) {
730         Log.d(this, "setCallAudioState %s", state);
731         mCallAudioState = state;
732         onAudioStateChanged(getAudioState());
733         onCallAudioStateChanged(state);
734     }
735 
setState(int newState)736     private void setState(int newState) {
737         if (mState != newState) {
738             int oldState = mState;
739             mState = newState;
740             for (Listener l : mListeners) {
741                 l.onStateChanged(this, oldState, newState);
742             }
743         }
744     }
745 
746     private static class FailureSignalingConference extends Conference {
747         private boolean mImmutable = false;
FailureSignalingConference(DisconnectCause disconnectCause, PhoneAccountHandle phoneAccount)748         public FailureSignalingConference(DisconnectCause disconnectCause,
749                 PhoneAccountHandle phoneAccount) {
750             super(phoneAccount);
751             setDisconnected(disconnectCause);
752             mImmutable = true;
753         }
checkImmutable()754         public void checkImmutable() {
755             if (mImmutable) {
756                 throw new UnsupportedOperationException("Conference is immutable");
757             }
758         }
759     }
760 
761     /**
762      * Return a {@code Conference} which represents a failed conference attempt. The returned
763      * {@code Conference} will have a {@link android.telecom.DisconnectCause} and as specified,
764      * and a {@link #getState()} of {@code STATE_DISCONNECTED}.
765      * <p>
766      * The returned {@code Conference} can be assumed to {@link #destroy()} itself when appropriate,
767      * so users of this method need not maintain a reference to its return value to destroy it.
768      *
769      * @param disconnectCause The disconnect cause, ({@see android.telecomm.DisconnectCause}).
770      * @return A {@code Conference} which indicates failure.
771      */
createFailedConference( @onNull DisconnectCause disconnectCause, @NonNull PhoneAccountHandle phoneAccount)772     public @NonNull static Conference createFailedConference(
773             @NonNull DisconnectCause disconnectCause, @NonNull PhoneAccountHandle phoneAccount) {
774         return new FailureSignalingConference(disconnectCause, phoneAccount);
775     }
776 
clearConferenceableList()777     private final void clearConferenceableList() {
778         for (Connection c : mConferenceableConnections) {
779             c.removeConnectionListener(mConnectionDeathListener);
780         }
781         mConferenceableConnections.clear();
782     }
783 
784     @Override
toString()785     public String toString() {
786         return String.format(Locale.US,
787                 "[State: %s,Capabilites: %s, VideoState: %s, VideoProvider: %s,"
788                 + "isRingbackRequested: %s, ThisObject %s]",
789                 Connection.stateToString(mState),
790                 Call.Details.capabilitiesToString(mConnectionCapabilities),
791                 getVideoState(),
792                 getVideoProvider(),
793                 isRingbackRequested() ? "Y" : "N",
794                 super.toString());
795     }
796 
797     /**
798      * Sets the label and icon status to display in the InCall UI.
799      *
800      * @param statusHints The status label and icon to set.
801      */
setStatusHints(StatusHints statusHints)802     public final void setStatusHints(StatusHints statusHints) {
803         mStatusHints = statusHints;
804         for (Listener l : mListeners) {
805             l.onStatusHintsChanged(this, statusHints);
806         }
807     }
808 
809     /**
810      * @return The status hints for this conference.
811      */
getStatusHints()812     public final StatusHints getStatusHints() {
813         return mStatusHints;
814     }
815 
816     /**
817      * Replaces all the extras associated with this {@code Conference}.
818      * <p>
819      * New or existing keys are replaced in the {@code Conference} extras.  Keys which are no longer
820      * in the new extras, but were present the last time {@code setExtras} was called are removed.
821      * <p>
822      * Alternatively you may use the {@link #putExtras(Bundle)}, and
823      * {@link #removeExtras(String...)} methods to modify the extras.
824      * <p>
825      * No assumptions should be made as to how an In-Call UI or service will handle these extras.
826      * Keys should be fully qualified (e.g., com.example.extras.MY_EXTRA) to avoid conflicts.
827      *
828      * @param extras The extras associated with this {@code Conference}.
829      */
setExtras(@ullable Bundle extras)830     public final void setExtras(@Nullable Bundle extras) {
831         // Keeping putExtras and removeExtras in the same lock so that this operation happens as a
832         // block instead of letting other threads put/remove while this method is running.
833         synchronized (mExtrasLock) {
834             // Add/replace any new or changed extras values.
835             putExtras(extras);
836             // If we have used "setExtras" in the past, compare the key set from the last invocation
837             // to the current one and remove any keys that went away.
838             if (mPreviousExtraKeys != null) {
839                 List<String> toRemove = new ArrayList<String>();
840                 for (String oldKey : mPreviousExtraKeys) {
841                     if (extras == null || !extras.containsKey(oldKey)) {
842                         toRemove.add(oldKey);
843                     }
844                 }
845 
846                 if (!toRemove.isEmpty()) {
847                     removeExtras(toRemove);
848                 }
849             }
850 
851             // Track the keys the last time set called setExtras.  This way, the next time setExtras
852             // is called we can see if the caller has removed any extras values.
853             if (mPreviousExtraKeys == null) {
854                 mPreviousExtraKeys = new ArraySet<String>();
855             }
856             mPreviousExtraKeys.clear();
857             if (extras != null) {
858                 mPreviousExtraKeys.addAll(extras.keySet());
859             }
860         }
861     }
862 
863     /**
864      * Adds some extras to this {@link Conference}.  Existing keys are replaced and new ones are
865      * added.
866      * <p>
867      * No assumptions should be made as to how an In-Call UI or service will handle these extras.
868      * Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
869      *
870      * @param extras The extras to add.
871      */
putExtras(@onNull Bundle extras)872     public final void putExtras(@NonNull Bundle extras) {
873         if (extras == null) {
874             return;
875         }
876 
877         // Creating a Bundle clone so we don't have to synchronize on mExtrasLock while calling
878         // onExtrasChanged.
879         Bundle listenersBundle;
880         synchronized (mExtrasLock) {
881             if (mExtras == null) {
882                 mExtras = new Bundle();
883             }
884             mExtras.putAll(extras);
885             listenersBundle = new Bundle(mExtras);
886         }
887 
888         for (Listener l : mListeners) {
889             l.onExtrasChanged(this, new Bundle(listenersBundle));
890         }
891     }
892 
893     /**
894      * Adds a boolean extra to this {@link Conference}.
895      *
896      * @param key The extra key.
897      * @param value The value.
898      * @hide
899      */
putExtra(String key, boolean value)900     public final void putExtra(String key, boolean value) {
901         Bundle newExtras = new Bundle();
902         newExtras.putBoolean(key, value);
903         putExtras(newExtras);
904     }
905 
906     /**
907      * Adds an integer extra to this {@link Conference}.
908      *
909      * @param key The extra key.
910      * @param value The value.
911      * @hide
912      */
putExtra(String key, int value)913     public final void putExtra(String key, int value) {
914         Bundle newExtras = new Bundle();
915         newExtras.putInt(key, value);
916         putExtras(newExtras);
917     }
918 
919     /**
920      * Adds a string extra to this {@link Conference}.
921      *
922      * @param key The extra key.
923      * @param value The value.
924      * @hide
925      */
putExtra(String key, String value)926     public final void putExtra(String key, String value) {
927         Bundle newExtras = new Bundle();
928         newExtras.putString(key, value);
929         putExtras(newExtras);
930     }
931 
932     /**
933      * Removes extras from this {@link Conference}.
934      *
935      * @param keys The keys of the extras to remove.
936      */
removeExtras(List<String> keys)937     public final void removeExtras(List<String> keys) {
938         if (keys == null || keys.isEmpty()) {
939             return;
940         }
941 
942         synchronized (mExtrasLock) {
943             if (mExtras != null) {
944                 for (String key : keys) {
945                     mExtras.remove(key);
946                 }
947             }
948         }
949 
950         List<String> unmodifiableKeys = Collections.unmodifiableList(keys);
951         for (Listener l : mListeners) {
952             l.onExtrasRemoved(this, unmodifiableKeys);
953         }
954     }
955 
956     /**
957      * Removes extras from this {@link Conference}.
958      *
959      * @param keys The keys of the extras to remove.
960      */
removeExtras(String .... keys)961     public final void removeExtras(String ... keys) {
962         removeExtras(Arrays.asList(keys));
963     }
964 
965     /**
966      * Returns the extras associated with this conference.
967      * <p>
968      * Extras should be updated using {@link #putExtras(Bundle)} and {@link #removeExtras(List)}.
969      * <p>
970      * Telecom or an {@link InCallService} can also update the extras via
971      * {@link android.telecom.Call#putExtras(Bundle)}, and
972      * {@link Call#removeExtras(List)}.
973      * <p>
974      * The conference is notified of changes to the extras made by Telecom or an
975      * {@link InCallService} by {@link #onExtrasChanged(Bundle)}.
976      *
977      * @return The extras associated with this connection.
978      */
getExtras()979     public final Bundle getExtras() {
980         return mExtras;
981     }
982 
983     /**
984      * Notifies this {@link Conference} of a change to the extras made outside the
985      * {@link ConnectionService}.
986      * <p>
987      * These extras changes can originate from Telecom itself, or from an {@link InCallService} via
988      * {@link android.telecom.Call#putExtras(Bundle)}, and
989      * {@link Call#removeExtras(List)}.
990      *
991      * @param extras The new extras bundle.
992      */
onExtrasChanged(Bundle extras)993     public void onExtrasChanged(Bundle extras) {}
994 
995     /**
996      * Set whether Telecom should treat this {@link Conference} as a multiparty conference call or
997      * if it should treat it as a single-party call.
998      * This method is used as part of a workaround regarding IMS conference calls and user
999      * expectation.  In IMS, once a conference is formed, the UE is connected to an IMS conference
1000      * server.  If all participants of the conference drop out of the conference except for one, the
1001      * UE is still connected to the IMS conference server.  At this point, the user logically
1002      * assumes they're no longer in a conference, yet the underlying network actually is.
1003      * To help provide a better user experiece, IMS conference calls can pretend to actually be a
1004      * single-party call when the participant count drops to 1.  Although the dialer/phone app
1005      * could perform this trickery, it makes sense to do this in Telephony since a fix there will
1006      * ensure that bluetooth head units, auto and wearable apps all behave consistently.
1007      * <p>
1008      * This API is intended for use by the platform Telephony stack only.
1009      *
1010      * @param isConference {@code true} if this {@link Conference} should be treated like a
1011      *      conference call, {@code false} if it should be treated like a single-party call.
1012      * @hide
1013      */
1014     @SystemApi
1015     @TestApi
1016     @RequiresPermission(MODIFY_PHONE_STATE)
setConferenceState(boolean isConference)1017     public void setConferenceState(boolean isConference) {
1018         mIsMultiparty = isConference;
1019         for (Listener l : mListeners) {
1020             l.onConferenceStateChanged(this, isConference);
1021         }
1022     }
1023 
1024     /**
1025      * Sets the call direction of this {@link Conference}. By default, all {@link Conference}s have
1026      * a direction of {@link android.telecom.Call.Details.CallDirection#DIRECTION_UNKNOWN}. The
1027      * direction of a {@link Conference} is only applicable to the case where
1028      * {@link #setConferenceState(boolean)} has been set to {@code false}, otherwise the direction
1029      * will be ignored.
1030      * @param callDirection The direction of the conference.
1031      * @hide
1032      */
1033     @RequiresPermission(MODIFY_PHONE_STATE)
setCallDirection(@all.Details.CallDirection int callDirection)1034     public final void setCallDirection(@Call.Details.CallDirection int callDirection) {
1035         Log.d(this, "setDirection %d", callDirection);
1036         mCallDirection = callDirection;
1037         for (Listener l : mListeners) {
1038             l.onCallDirectionChanged(this, callDirection);
1039         }
1040     }
1041 
1042     /**
1043      * Determines if the {@link Conference} is considered "multiparty" or not.  By default all
1044      * conferences are considered multiparty.  A multiparty conference is one where there are
1045      * multiple conference participants (other than the host) in the conference.
1046      * This is tied to {@link #setConferenceState(boolean)}, which is used for some use cases to
1047      * have a conference appear as if it is a standalone call, in which case the conference will
1048      * no longer be multiparty.
1049      * @return {@code true} if conference is treated as a conference (i.e. it is multiparty),
1050      * {@code false} if it should emulate a standalone call (i.e. not multiparty).
1051      * @hide
1052      */
isMultiparty()1053     public boolean isMultiparty() {
1054         return mIsMultiparty;
1055     }
1056 
1057     /**
1058      * Sets the address of this {@link Conference}.  Used when {@link #setConferenceState(boolean)}
1059      * is called to mark a conference temporarily as NOT a conference.
1060      * <p>
1061      * Note: This is a Telephony-specific implementation detail related to IMS conferences.  It is
1062      * not intended for use outside of the Telephony stack.
1063      *
1064      * @param address The new address.
1065      * @param presentation The presentation requirements for the address.
1066      *        See {@link TelecomManager} for valid values.
1067      * @hide
1068      */
1069     @SystemApi
1070     @TestApi
1071     @RequiresPermission(MODIFY_PHONE_STATE)
setAddress(@onNull Uri address, @TelecomManager.Presentation int presentation)1072     public final void setAddress(@NonNull Uri address,
1073             @TelecomManager.Presentation int presentation) {
1074         Log.d(this, "setAddress %s", address);
1075         mAddress = address;
1076         mAddressPresentation = presentation;
1077         for (Listener l : mListeners) {
1078             l.onAddressChanged(this, address, presentation);
1079         }
1080     }
1081 
1082     /**
1083      * Returns the "address" associated with the conference.  This is applicable in two cases:
1084      * <ol>
1085      *     <li>When {@link #setConferenceState(boolean)} is used to mark a conference as
1086      *     temporarily "not a conference"; we need to present the correct address in the in-call
1087      *     UI.</li>
1088      *     <li>When the conference is not hosted on the current device, we need to know the address
1089      *     information for the purpose of showing the original address to the user, as well as for
1090      *     logging to the call log.</li>
1091      * </ol>
1092      * @return The address of the conference, or {@code null} if not applicable.
1093      * @hide
1094      */
getAddress()1095     public final Uri getAddress() {
1096         return mAddress;
1097     }
1098 
1099     /**
1100      * Returns the address presentation associated with the conference.
1101      * <p>
1102      * This is applicable in two cases:
1103      * <ol>
1104      *     <li>When {@link #setConferenceState(boolean)} is used to mark a conference as
1105      *     temporarily "not a conference"; we need to present the correct address presentation in
1106      *     the in-call UI.</li>
1107      *     <li>When the conference is not hosted on the current device, we need to know the address
1108      *     presentation information for the purpose of showing the original address to the user, as
1109      *     well as for logging to the call log.</li>
1110      * </ol>
1111      * @return The address presentation of the conference.
1112      * @hide
1113      */
getAddressPresentation()1114     public final @TelecomManager.Presentation int getAddressPresentation() {
1115         return mAddressPresentation;
1116     }
1117 
1118     /**
1119      * @return The caller display name (CNAP).
1120      * @hide
1121      */
getCallerDisplayName()1122     public final String getCallerDisplayName() {
1123         return mCallerDisplayName;
1124     }
1125 
1126     /**
1127      * @return The presentation requirements for the handle.
1128      *         See {@link TelecomManager} for valid values.
1129      * @hide
1130      */
getCallerDisplayNamePresentation()1131     public final int getCallerDisplayNamePresentation() {
1132         return mCallerDisplayNamePresentation;
1133     }
1134 
1135     /**
1136      * @return The call direction of this conference. Only applicable when
1137      * {@link #setConferenceState(boolean)} is set to false.
1138      * @hide
1139      */
getCallDirection()1140     public final @Call.Details.CallDirection int getCallDirection() {
1141         return mCallDirection;
1142     }
1143 
1144     /**
1145      * Sets the caller display name (CNAP) of this {@link Conference}.  Used when
1146      * {@link #setConferenceState(boolean)} is called to mark a conference temporarily as NOT a
1147      * conference.
1148      * <p>
1149      * Note: This is a Telephony-specific implementation detail related to IMS conferences.  It is
1150      * not intended for use outside of the Telephony stack.
1151      *
1152      * @param callerDisplayName The new display name.
1153      * @param presentation The presentation requirements for the handle.
1154      *        See {@link TelecomManager} for valid values.
1155      * @hide
1156      */
1157     @SystemApi
1158     @TestApi
setCallerDisplayName(@onNull String callerDisplayName, @TelecomManager.Presentation int presentation)1159     public final void setCallerDisplayName(@NonNull String callerDisplayName,
1160             @TelecomManager.Presentation int presentation) {
1161         Log.d(this, "setCallerDisplayName %s", callerDisplayName);
1162         mCallerDisplayName = callerDisplayName;
1163         mCallerDisplayNamePresentation = presentation;
1164         for (Listener l : mListeners) {
1165             l.onCallerDisplayNameChanged(this, callerDisplayName, presentation);
1166         }
1167     }
1168 
1169     /**
1170      * Handles a change to extras received from Telecom.
1171      *
1172      * @param extras The new extras.
1173      * @hide
1174      */
handleExtrasChanged(Bundle extras)1175     final void handleExtrasChanged(Bundle extras) {
1176         Bundle b = null;
1177         synchronized (mExtrasLock) {
1178             mExtras = extras;
1179             if (mExtras != null) {
1180                 b = new Bundle(mExtras);
1181             }
1182         }
1183         onExtrasChanged(b);
1184     }
1185 
1186     /**
1187      * Sends an event associated with this {@link Conference} with associated event extras to the
1188      * {@link InCallService}.
1189      * <p>
1190      * Connection events are used to communicate point in time information from a
1191      * {@link ConnectionService} to an {@link InCallService} implementation.  An example of a
1192      * custom connection event includes notifying the UI when a WIFI call has been handed over to
1193      * LTE, which the InCall UI might use to inform the user that billing charges may apply.  The
1194      * Android Telephony framework will send the {@link Connection#EVENT_MERGE_COMPLETE}
1195      * connection event when a call to {@link Call#mergeConference()} has completed successfully.
1196      * <p>
1197      * Events are exposed to {@link InCallService} implementations via
1198      * {@link Call.Callback#onConnectionEvent(Call, String, Bundle)}.
1199      * <p>
1200      * No assumptions should be made as to how an In-Call UI or service will handle these events.
1201      * The {@link ConnectionService} must assume that the In-Call UI could even chose to ignore
1202      * some events altogether.
1203      * <p>
1204      * Events should be fully qualified (e.g. {@code com.example.event.MY_EVENT}) to avoid
1205      * conflicts between {@link ConnectionService} implementations.  Further, custom
1206      * {@link ConnectionService} implementations shall not re-purpose events in the
1207      * {@code android.*} namespace, nor shall they define new event types in this namespace.  When
1208      * defining a custom event type, ensure the contents of the extras {@link Bundle} is clearly
1209      * defined.  Extra keys for this bundle should be named similar to the event type (e.g.
1210      * {@code com.example.extra.MY_EXTRA}).
1211      * <p>
1212      * When defining events and the associated extras, it is important to keep their behavior
1213      * consistent when the associated {@link ConnectionService} is updated.  Support for deprecated
1214      * events/extras should me maintained to ensure backwards compatibility with older
1215      * {@link InCallService} implementations which were built to support the older behavior.
1216      * <p>
1217      * Expected connection events from the Telephony stack are:
1218      * <p>
1219      * <ul>
1220      *      <li>{@link Connection#EVENT_CALL_HOLD_FAILED} with {@code null} {@code extras} when the
1221      *      {@link Conference} could not be held.</li>
1222      *      <li>{@link Connection#EVENT_MERGE_START} with {@code null} {@code extras} when a new
1223      *      call is being merged into the conference.</li>
1224      *      <li>{@link Connection#EVENT_MERGE_COMPLETE} with {@code null} {@code extras} a new call
1225      *      has completed being merged into the conference.</li>
1226      *      <li>{@link Connection#EVENT_CALL_MERGE_FAILED} with {@code null} {@code extras} a new
1227      *      call has failed to merge into the conference (the dialer app can determine which call
1228      *      failed to merge based on the fact that the call still exists outside of the conference
1229      *      at the end of the merge process).</li>
1230      * </ul>
1231      *
1232      * @param event The conference event.
1233      * @param extras Optional bundle containing extra information associated with the event.
1234      */
sendConferenceEvent(@onNull String event, @Nullable Bundle extras)1235     public void sendConferenceEvent(@NonNull String event, @Nullable Bundle extras) {
1236         for (Listener l : mListeners) {
1237             l.onConnectionEvent(this, event, extras);
1238         }
1239     }
1240 }
1241