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