• 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 com.android.internal.telecom.IConnectionService;
20 
21 import android.annotation.Nullable;
22 import android.annotation.SystemApi;
23 import android.os.Bundle;
24 import android.os.Handler;
25 import android.os.RemoteException;
26 
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.List;
30 import java.util.Set;
31 import java.util.concurrent.CopyOnWriteArrayList;
32 import java.util.concurrent.CopyOnWriteArraySet;
33 
34 /**
35  * A conference provided to a {@link ConnectionService} by another {@code ConnectionService} through
36  * {@link ConnectionService#conferenceRemoteConnections}. Once created, a {@code RemoteConference}
37  * can be used to control the conference call or monitor changes through
38  * {@link RemoteConnection.Callback}.
39  *
40  * @see ConnectionService#onRemoteConferenceAdded
41  */
42 public final class RemoteConference {
43 
44     /**
45      * Callback base class for {@link RemoteConference}.
46      */
47     public abstract static class Callback {
48         /**
49          * Invoked when the state of this {@code RemoteConferece} has changed. See
50          * {@link #getState()}.
51          *
52          * @param conference The {@code RemoteConference} invoking this method.
53          * @param oldState The previous state of the {@code RemoteConference}.
54          * @param newState The new state of the {@code RemoteConference}.
55          */
onStateChanged(RemoteConference conference, int oldState, int newState)56         public void onStateChanged(RemoteConference conference, int oldState, int newState) {}
57 
58         /**
59          * Invoked when this {@code RemoteConference} is disconnected.
60          *
61          * @param conference The {@code RemoteConference} invoking this method.
62          * @param disconnectCause The ({@see DisconnectCause}) associated with this failed
63          *     conference.
64          */
onDisconnected(RemoteConference conference, DisconnectCause disconnectCause)65         public void onDisconnected(RemoteConference conference, DisconnectCause disconnectCause) {}
66 
67         /**
68          * Invoked when a {@link RemoteConnection} is added to the conference call.
69          *
70          * @param conference The {@code RemoteConference} invoking this method.
71          * @param connection The {@link RemoteConnection} being added.
72          */
onConnectionAdded(RemoteConference conference, RemoteConnection connection)73         public void onConnectionAdded(RemoteConference conference, RemoteConnection connection) {}
74 
75         /**
76          * Invoked when a {@link RemoteConnection} is removed from the conference call.
77          *
78          * @param conference The {@code RemoteConference} invoking this method.
79          * @param connection The {@link RemoteConnection} being removed.
80          */
onConnectionRemoved(RemoteConference conference, RemoteConnection connection)81         public void onConnectionRemoved(RemoteConference conference, RemoteConnection connection) {}
82 
83         /**
84          * Indicates that the call capabilities of this {@code RemoteConference} have changed.
85          * See {@link #getConnectionCapabilities()}.
86          *
87          * @param conference The {@code RemoteConference} invoking this method.
88          * @param connectionCapabilities The new capabilities of the {@code RemoteConference}.
89          */
onConnectionCapabilitiesChanged( RemoteConference conference, int connectionCapabilities)90         public void onConnectionCapabilitiesChanged(
91                 RemoteConference conference,
92                 int connectionCapabilities) {}
93 
94         /**
95          * Invoked when the set of {@link RemoteConnection}s which can be added to this conference
96          * call have changed.
97          *
98          * @param conference The {@code RemoteConference} invoking this method.
99          * @param conferenceableConnections The list of conferenceable {@link RemoteConnection}s.
100          */
onConferenceableConnectionsChanged( RemoteConference conference, List<RemoteConnection> conferenceableConnections)101         public void onConferenceableConnectionsChanged(
102                 RemoteConference conference,
103                 List<RemoteConnection> conferenceableConnections) {}
104 
105         /**
106          * Indicates that this {@code RemoteConference} has been destroyed. No further requests
107          * should be made to the {@code RemoteConference}, and references to it should be cleared.
108          *
109          * @param conference The {@code RemoteConference} invoking this method.
110          */
onDestroyed(RemoteConference conference)111         public void onDestroyed(RemoteConference conference) {}
112 
113         /**
114          * Handles changes to the {@code RemoteConference} extras.
115          *
116          * @param conference The {@code RemoteConference} invoking this method.
117          * @param extras The extras containing other information associated with the conference.
118          */
onExtrasChanged(RemoteConference conference, @Nullable Bundle extras)119         public void onExtrasChanged(RemoteConference conference, @Nullable Bundle extras) {}
120     }
121 
122     private final String mId;
123     private final IConnectionService mConnectionService;
124 
125     private final Set<CallbackRecord<Callback>> mCallbackRecords = new CopyOnWriteArraySet<>();
126     private final List<RemoteConnection> mChildConnections = new CopyOnWriteArrayList<>();
127     private final List<RemoteConnection> mUnmodifiableChildConnections =
128             Collections.unmodifiableList(mChildConnections);
129     private final List<RemoteConnection> mConferenceableConnections = new ArrayList<>();
130     private final List<RemoteConnection> mUnmodifiableConferenceableConnections =
131             Collections.unmodifiableList(mConferenceableConnections);
132 
133     private int mState = Connection.STATE_NEW;
134     private DisconnectCause mDisconnectCause;
135     private int mConnectionCapabilities;
136     private Bundle mExtras;
137 
138     /** @hide */
RemoteConference(String id, IConnectionService connectionService)139     RemoteConference(String id, IConnectionService connectionService) {
140         mId = id;
141         mConnectionService = connectionService;
142     }
143 
144     /** @hide */
getId()145     String getId() {
146         return mId;
147     }
148 
149     /** @hide */
setDestroyed()150     void setDestroyed() {
151         for (RemoteConnection connection : mChildConnections) {
152             connection.setConference(null);
153         }
154         for (CallbackRecord<Callback> record : mCallbackRecords) {
155             final RemoteConference conference = this;
156             final Callback callback = record.getCallback();
157             record.getHandler().post(new Runnable() {
158                 @Override
159                 public void run() {
160                     callback.onDestroyed(conference);
161                 }
162             });
163         }
164     }
165 
166     /** @hide */
setState(final int newState)167     void setState(final int newState) {
168         if (newState != Connection.STATE_ACTIVE &&
169                 newState != Connection.STATE_HOLDING &&
170                 newState != Connection.STATE_DISCONNECTED) {
171             Log.w(this, "Unsupported state transition for Conference call.",
172                     Connection.stateToString(newState));
173             return;
174         }
175 
176         if (mState != newState) {
177             final int oldState = mState;
178             mState = newState;
179             for (CallbackRecord<Callback> record : mCallbackRecords) {
180                 final RemoteConference conference = this;
181                 final Callback callback = record.getCallback();
182                 record.getHandler().post(new Runnable() {
183                     @Override
184                     public void run() {
185                         callback.onStateChanged(conference, oldState, newState);
186                     }
187                 });
188             }
189         }
190     }
191 
192     /** @hide */
addConnection(final RemoteConnection connection)193     void addConnection(final RemoteConnection connection) {
194         if (!mChildConnections.contains(connection)) {
195             mChildConnections.add(connection);
196             connection.setConference(this);
197             for (CallbackRecord<Callback> record : mCallbackRecords) {
198                 final RemoteConference conference = this;
199                 final Callback callback = record.getCallback();
200                 record.getHandler().post(new Runnable() {
201                     @Override
202                     public void run() {
203                         callback.onConnectionAdded(conference, connection);
204                     }
205                 });
206             }
207         }
208     }
209 
210     /** @hide */
removeConnection(final RemoteConnection connection)211     void removeConnection(final RemoteConnection connection) {
212         if (mChildConnections.contains(connection)) {
213             mChildConnections.remove(connection);
214             connection.setConference(null);
215             for (CallbackRecord<Callback> record : mCallbackRecords) {
216                 final RemoteConference conference = this;
217                 final Callback callback = record.getCallback();
218                 record.getHandler().post(new Runnable() {
219                     @Override
220                     public void run() {
221                         callback.onConnectionRemoved(conference, connection);
222                     }
223                 });
224             }
225         }
226     }
227 
228     /** @hide */
setConnectionCapabilities(final int connectionCapabilities)229     void setConnectionCapabilities(final int connectionCapabilities) {
230         if (mConnectionCapabilities != connectionCapabilities) {
231             mConnectionCapabilities = connectionCapabilities;
232             for (CallbackRecord<Callback> record : mCallbackRecords) {
233                 final RemoteConference conference = this;
234                 final Callback callback = record.getCallback();
235                 record.getHandler().post(new Runnable() {
236                     @Override
237                     public void run() {
238                         callback.onConnectionCapabilitiesChanged(
239                                 conference, mConnectionCapabilities);
240                     }
241                 });
242             }
243         }
244     }
245 
246     /** @hide */
setConferenceableConnections(List<RemoteConnection> conferenceableConnections)247     void setConferenceableConnections(List<RemoteConnection> conferenceableConnections) {
248         mConferenceableConnections.clear();
249         mConferenceableConnections.addAll(conferenceableConnections);
250         for (CallbackRecord<Callback> record : mCallbackRecords) {
251             final RemoteConference conference = this;
252             final Callback callback = record.getCallback();
253             record.getHandler().post(new Runnable() {
254                 @Override
255                 public void run() {
256                     callback.onConferenceableConnectionsChanged(
257                             conference, mUnmodifiableConferenceableConnections);
258                 }
259             });
260         }
261     }
262 
263     /** @hide */
setDisconnected(final DisconnectCause disconnectCause)264     void setDisconnected(final DisconnectCause disconnectCause) {
265         if (mState != Connection.STATE_DISCONNECTED) {
266             mDisconnectCause = disconnectCause;
267             setState(Connection.STATE_DISCONNECTED);
268             for (CallbackRecord<Callback> record : mCallbackRecords) {
269                 final RemoteConference conference = this;
270                 final Callback callback = record.getCallback();
271                 record.getHandler().post(new Runnable() {
272                     @Override
273                     public void run() {
274                         callback.onDisconnected(conference, disconnectCause);
275                     }
276                 });
277             }
278         }
279     }
280 
281     /** @hide */
setExtras(final Bundle extras)282     void setExtras(final Bundle extras) {
283         mExtras = extras;
284         for (CallbackRecord<Callback> record : mCallbackRecords) {
285             final RemoteConference conference = this;
286             final Callback callback = record.getCallback();
287             record.getHandler().post(new Runnable() {
288                 @Override
289                 public void run() {
290                     callback.onExtrasChanged(conference, extras);
291                 }
292             });
293         }
294     }
295 
296     /**
297      * Returns the list of {@link RemoteConnection}s contained in this conference.
298      *
299      * @return A list of child connections.
300      */
getConnections()301     public final List<RemoteConnection> getConnections() {
302         return mUnmodifiableChildConnections;
303     }
304 
305     /**
306      * Gets the state of the conference call. See {@link Connection} for valid values.
307      *
308      * @return A constant representing the state the conference call is currently in.
309      */
getState()310     public final int getState() {
311         return mState;
312     }
313 
314     /**
315      * Returns the capabilities of the conference. See {@code CAPABILITY_*} constants in class
316      * {@link Connection} for valid values.
317      *
318      * @return A bitmask of the capabilities of the conference call.
319      */
getConnectionCapabilities()320     public final int getConnectionCapabilities() {
321         return mConnectionCapabilities;
322     }
323 
324     /**
325      * Obtain the extras associated with this {@code RemoteConnection}.
326      *
327      * @return The extras for this connection.
328      */
getExtras()329     public final Bundle getExtras() {
330         return mExtras;
331     }
332 
333     /**
334      * Disconnects the conference call as well as the child {@link RemoteConnection}s.
335      */
disconnect()336     public void disconnect() {
337         try {
338             mConnectionService.disconnect(mId);
339         } catch (RemoteException e) {
340         }
341     }
342 
343     /**
344      * Removes the specified {@link RemoteConnection} from the conference. This causes the
345      * {@link RemoteConnection} to become a standalone connection. This is a no-op if the
346      * {@link RemoteConnection} does not belong to this conference.
347      *
348      * @param connection The remote-connection to remove.
349      */
separate(RemoteConnection connection)350     public void separate(RemoteConnection connection) {
351         if (mChildConnections.contains(connection)) {
352             try {
353                 mConnectionService.splitFromConference(connection.getId());
354             } catch (RemoteException e) {
355             }
356         }
357     }
358 
359     /**
360      * Merges all {@link RemoteConnection}s of this conference into a single call. This should be
361      * invoked only if the conference contains the capability
362      * {@link Connection#CAPABILITY_MERGE_CONFERENCE}, otherwise it is a no-op. The presence of said
363      * capability indicates that the connections of this conference, despite being part of the
364      * same conference object, are yet to have their audio streams merged; this is a common pattern
365      * for CDMA conference calls, but the capability is not used for GSM and SIP conference calls.
366      * Invoking this method will cause the unmerged child connections to merge their audio
367      * streams.
368      */
merge()369     public void merge() {
370         try {
371             mConnectionService.mergeConference(mId);
372         } catch (RemoteException e) {
373         }
374     }
375 
376     /**
377      * Swaps the active audio stream between the conference's child {@link RemoteConnection}s.
378      * This should be invoked only if the conference contains the capability
379      * {@link Connection#CAPABILITY_SWAP_CONFERENCE}, otherwise it is a no-op. This is only used by
380      * {@link ConnectionService}s that create conferences for connections that do not yet have
381      * their audio streams merged; this is a common pattern for CDMA conference calls, but the
382      * capability is not used for GSM and SIP conference calls. Invoking this method will change the
383      * active audio stream to a different child connection.
384      */
swap()385     public void swap() {
386         try {
387             mConnectionService.swapConference(mId);
388         } catch (RemoteException e) {
389         }
390     }
391 
392     /**
393      * Puts the conference on hold.
394      */
hold()395     public void hold() {
396         try {
397             mConnectionService.hold(mId);
398         } catch (RemoteException e) {
399         }
400     }
401 
402     /**
403      * Unholds the conference call.
404      */
unhold()405     public void unhold() {
406         try {
407             mConnectionService.unhold(mId);
408         } catch (RemoteException e) {
409         }
410     }
411 
412     /**
413      * Returns the {@link DisconnectCause} for the conference if it is in the state
414      * {@link Connection#STATE_DISCONNECTED}. If the conference is not disconnected, this will
415      * return null.
416      *
417      * @return The disconnect cause.
418      */
getDisconnectCause()419     public DisconnectCause getDisconnectCause() {
420         return mDisconnectCause;
421     }
422 
423     /**
424      * Requests that the conference start playing the specified DTMF tone.
425      *
426      * @param digit The digit for which to play a DTMF tone.
427      */
playDtmfTone(char digit)428     public void playDtmfTone(char digit) {
429         try {
430             mConnectionService.playDtmfTone(mId, digit);
431         } catch (RemoteException e) {
432         }
433     }
434 
435     /**
436      * Stops the most recent request to play a DTMF tone.
437      *
438      * @see #playDtmfTone
439      */
stopDtmfTone()440     public void stopDtmfTone() {
441         try {
442             mConnectionService.stopDtmfTone(mId);
443         } catch (RemoteException e) {
444         }
445     }
446 
447     /**
448      * Request to change the conference's audio routing to the specified state. The specified state
449      * can include audio routing (Bluetooth, Speaker, etc) and muting state.
450      *
451      * @see android.telecom.AudioState
452      * @deprecated Use {@link #setCallAudioState(CallAudioState)} instead.
453      * @hide
454      */
455     @SystemApi
456     @Deprecated
setAudioState(AudioState state)457     public void setAudioState(AudioState state) {
458         setCallAudioState(new CallAudioState(state));
459     }
460 
461     /**
462      * Request to change the conference's audio routing to the specified state. The specified state
463      * can include audio routing (Bluetooth, Speaker, etc) and muting state.
464      */
setCallAudioState(CallAudioState state)465     public void setCallAudioState(CallAudioState state) {
466         try {
467             mConnectionService.onCallAudioStateChanged(mId, state);
468         } catch (RemoteException e) {
469         }
470     }
471 
472 
473     /**
474      * Returns a list of independent connections that can me merged with this conference.
475      *
476      * @return A list of conferenceable connections.
477      */
getConferenceableConnections()478     public List<RemoteConnection> getConferenceableConnections() {
479         return mUnmodifiableConferenceableConnections;
480     }
481 
482     /**
483      * Register a callback through which to receive state updates for this conference.
484      *
485      * @param callback The callback to notify of state changes.
486      */
registerCallback(Callback callback)487     public final void registerCallback(Callback callback) {
488         registerCallback(callback, new Handler());
489     }
490 
491     /**
492      * Registers a callback through which to receive state updates for this conference.
493      * Callbacks will be notified using the specified handler, if provided.
494      *
495      * @param callback The callback to notify of state changes.
496      * @param handler The handler on which to execute the callbacks.
497      */
registerCallback(Callback callback, Handler handler)498     public final void registerCallback(Callback callback, Handler handler) {
499         unregisterCallback(callback);
500         if (callback != null && handler != null) {
501             mCallbackRecords.add(new CallbackRecord(callback, handler));
502         }
503     }
504 
505     /**
506      * Unregisters a previously registered callback.
507      *
508      * @see #registerCallback
509      *
510      * @param callback The callback to unregister.
511      */
unregisterCallback(Callback callback)512     public final void unregisterCallback(Callback callback) {
513         if (callback != null) {
514             for (CallbackRecord<Callback> record : mCallbackRecords) {
515                 if (record.getCallback() == callback) {
516                     mCallbackRecords.remove(record);
517                     break;
518                 }
519             }
520         }
521     }
522 }
523