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