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.os.SomeArgs;
     20 import com.android.internal.telecom.IVideoCallback;
     21 import com.android.internal.telecom.IVideoProvider;
     22 
     23 import android.annotation.IntDef;
     24 import android.annotation.NonNull;
     25 import android.annotation.Nullable;
     26 import android.annotation.SystemApi;
     27 import android.app.Notification;
     28 import android.content.Intent;
     29 import android.hardware.camera2.CameraManager;
     30 import android.net.Uri;
     31 import android.os.Binder;
     32 import android.os.Bundle;
     33 import android.os.Handler;
     34 import android.os.IBinder;
     35 import android.os.Looper;
     36 import android.os.Message;
     37 import android.os.ParcelFileDescriptor;
     38 import android.os.RemoteException;
     39 import android.util.ArraySet;
     40 import android.view.Surface;
     41 
     42 import java.io.IOException;
     43 import java.io.InputStreamReader;
     44 import java.io.OutputStreamWriter;
     45 import java.lang.annotation.Retention;
     46 import java.lang.annotation.RetentionPolicy;
     47 import java.util.ArrayList;
     48 import java.util.Arrays;
     49 import java.util.Collections;
     50 import java.util.List;
     51 import java.util.Set;
     52 import java.util.concurrent.ConcurrentHashMap;
     53 
     54 /**
     55  * Represents a phone call or connection to a remote endpoint that carries voice and/or video
     56  * traffic.
     57  * <p>
     58  * Implementations create a custom subclass of {@code Connection} and return it to the framework
     59  * as the return value of
     60  * {@link ConnectionService#onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)}
     61  * or
     62  * {@link ConnectionService#onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
     63  * Implementations are then responsible for updating the state of the {@code Connection}, and
     64  * must call {@link #destroy()} to signal to the framework that the {@code Connection} is no
     65  * longer used and associated resources may be recovered.
     66  * <p>
     67  * Subclasses of {@code Connection} override the {@code on*} methods to provide the the
     68  * {@link ConnectionService}'s implementation of calling functionality.  The {@code on*} methods are
     69  * called by Telecom to inform an instance of a {@code Connection} of actions specific to that
     70  * {@code Connection} instance.
     71  * <p>
     72  * Basic call support requires overriding the following methods: {@link #onAnswer()},
     73  * {@link #onDisconnect()}, {@link #onReject()}, {@link #onAbort()}
     74  * <p>
     75  * Where a {@code Connection} has {@link #CAPABILITY_SUPPORT_HOLD}, the {@link #onHold()} and
     76  * {@link #onUnhold()} methods should be overridden to provide hold support for the
     77  * {@code Connection}.
     78  * <p>
     79  * Where a {@code Connection} supports a variation of video calling (e.g. the
     80  * {@code CAPABILITY_SUPPORTS_VT_*} capability bits), {@link #onAnswer(int)} should be overridden
     81  * to support answering a call as a video call.
     82  * <p>
     83  * Where a {@code Connection} has {@link #PROPERTY_IS_EXTERNAL_CALL} and
     84  * {@link #CAPABILITY_CAN_PULL_CALL}, {@link #onPullExternalCall()} should be overridden to provide
     85  * support for pulling the external call.
     86  * <p>
     87  * Where a {@code Connection} supports conference calling {@link #onSeparate()} should be
     88  * overridden.
     89  * <p>
     90  * There are a number of other {@code on*} methods which a {@code Connection} can choose to
     91  * implement, depending on whether it is concerned with the associated calls from Telecom.  If,
     92  * for example, call events from a {@link InCallService} are handled,
     93  * {@link #onCallEvent(String, Bundle)} should be overridden.  Another example is
     94  * {@link #onExtrasChanged(Bundle)}, which should be overridden if the {@code Connection} wishes to
     95  * make use of extra information provided via the {@link Call#putExtras(Bundle)} and
     96  * {@link Call#removeExtras(String...)} methods.
     97  */
     98 public abstract class Connection extends Conferenceable {
     99 
    100     /**
    101      * The connection is initializing. This is generally the first state for a {@code Connection}
    102      * returned by a {@link ConnectionService}.
    103      */
    104     public static final int STATE_INITIALIZING = 0;
    105 
    106     /**
    107      * The connection is new and not connected.
    108      */
    109     public static final int STATE_NEW = 1;
    110 
    111     /**
    112      * An incoming connection is in the ringing state. During this state, the user's ringer or
    113      * vibration feature will be activated.
    114      */
    115     public static final int STATE_RINGING = 2;
    116 
    117     /**
    118      * An outgoing connection is in the dialing state. In this state the other party has not yet
    119      * answered the call and the user traditionally hears a ringback tone.
    120      */
    121     public static final int STATE_DIALING = 3;
    122 
    123     /**
    124      * A connection is active. Both parties are connected to the call and can actively communicate.
    125      */
    126     public static final int STATE_ACTIVE = 4;
    127 
    128     /**
    129      * A connection is on hold.
    130      */
    131     public static final int STATE_HOLDING = 5;
    132 
    133     /**
    134      * A connection has been disconnected. This is the final state once the user has been
    135      * disconnected from a call either locally, remotely or by an error in the service.
    136      */
    137     public static final int STATE_DISCONNECTED = 6;
    138 
    139     /**
    140      * The state of an external connection which is in the process of being pulled from a remote
    141      * device to the local device.
    142      * <p>
    143      * A connection can only be in this state if the {@link #PROPERTY_IS_EXTERNAL_CALL} property and
    144      * {@link #CAPABILITY_CAN_PULL_CALL} capability bits are set on the connection.
    145      */
    146     public static final int STATE_PULLING_CALL = 7;
    147 
    148     /**
    149      * Connection can currently be put on hold or unheld. This is distinct from
    150      * {@link #CAPABILITY_SUPPORT_HOLD} in that although a connection may support 'hold' most times,
    151      * it does not at the moment support the function. This can be true while the call is in the
    152      * state {@link #STATE_DIALING}, for example. During this condition, an in-call UI may
    153      * display a disabled 'hold' button.
    154      */
    155     public static final int CAPABILITY_HOLD = 0x00000001;
    156 
    157     /** Connection supports the hold feature. */
    158     public static final int CAPABILITY_SUPPORT_HOLD = 0x00000002;
    159 
    160     /**
    161      * Connections within a conference can be merged. A {@link ConnectionService} has the option to
    162      * add a {@link Conference} before the child {@link Connection}s are merged. This is how
    163      * CDMA-based {@link Connection}s are implemented. For these unmerged {@link Conference}s, this
    164      * capability allows a merge button to be shown while the conference is in the foreground
    165      * of the in-call UI.
    166      * <p>
    167      * This is only intended for use by a {@link Conference}.
    168      */
    169     public static final int CAPABILITY_MERGE_CONFERENCE = 0x00000004;
    170 
    171     /**
    172      * Connections within a conference can be swapped between foreground and background.
    173      * See {@link #CAPABILITY_MERGE_CONFERENCE} for additional information.
    174      * <p>
    175      * This is only intended for use by a {@link Conference}.
    176      */
    177     public static final int CAPABILITY_SWAP_CONFERENCE = 0x00000008;
    178 
    179     /**
    180      * @hide
    181      */
    182     public static final int CAPABILITY_UNUSED = 0x00000010;
    183 
    184     /** Connection supports responding via text option. */
    185     public static final int CAPABILITY_RESPOND_VIA_TEXT = 0x00000020;
    186 
    187     /** Connection can be muted. */
    188     public static final int CAPABILITY_MUTE = 0x00000040;
    189 
    190     /**
    191      * Connection supports conference management. This capability only applies to
    192      * {@link Conference}s which can have {@link Connection}s as children.
    193      */
    194     public static final int CAPABILITY_MANAGE_CONFERENCE = 0x00000080;
    195 
    196     /**
    197      * Local device supports receiving video.
    198      */
    199     public static final int CAPABILITY_SUPPORTS_VT_LOCAL_RX = 0x00000100;
    200 
    201     /**
    202      * Local device supports transmitting video.
    203      */
    204     public static final int CAPABILITY_SUPPORTS_VT_LOCAL_TX = 0x00000200;
    205 
    206     /**
    207      * Local device supports bidirectional video calling.
    208      */
    209     public static final int CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL =
    210             CAPABILITY_SUPPORTS_VT_LOCAL_RX | CAPABILITY_SUPPORTS_VT_LOCAL_TX;
    211 
    212     /**
    213      * Remote device supports receiving video.
    214      */
    215     public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 0x00000400;
    216 
    217     /**
    218      * Remote device supports transmitting video.
    219      */
    220     public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 0x00000800;
    221 
    222     /**
    223      * Remote device supports bidirectional video calling.
    224      */
    225     public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL =
    226             CAPABILITY_SUPPORTS_VT_REMOTE_RX | CAPABILITY_SUPPORTS_VT_REMOTE_TX;
    227 
    228     /**
    229      * Connection is able to be separated from its parent {@code Conference}, if any.
    230      */
    231     public static final int CAPABILITY_SEPARATE_FROM_CONFERENCE = 0x00001000;
    232 
    233     /**
    234      * Connection is able to be individually disconnected when in a {@code Conference}.
    235      */
    236     public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 0x00002000;
    237 
    238     /**
    239      * Un-used.
    240      * @hide
    241      */
    242     public static final int CAPABILITY_UNUSED_2 = 0x00004000;
    243 
    244     /**
    245      * Un-used.
    246      * @hide
    247      */
    248     public static final int CAPABILITY_UNUSED_3 = 0x00008000;
    249 
    250     /**
    251      * Un-used.
    252      * @hide
    253      */
    254     public static final int CAPABILITY_UNUSED_4 = 0x00010000;
    255 
    256     /**
    257      * Un-used.
    258      * @hide
    259      */
    260     public static final int CAPABILITY_UNUSED_5 = 0x00020000;
    261 
    262     /**
    263      * Speed up audio setup for MT call.
    264      * @hide
    265      */
    266     public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 0x00040000;
    267 
    268     /**
    269      * Call can be upgraded to a video call.
    270      */
    271     public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 0x00080000;
    272 
    273     /**
    274      * For video calls, indicates whether the outgoing video for the call can be paused using
    275      * the {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState.
    276      */
    277     public static final int CAPABILITY_CAN_PAUSE_VIDEO = 0x00100000;
    278 
    279     /**
    280      * For a conference, indicates the conference will not have child connections.
    281      * <p>
    282      * An example of a conference with child connections is a GSM conference call, where the radio
    283      * retains connections to the individual participants of the conference.  Another example is an
    284      * IMS conference call where conference event package functionality is supported; in this case
    285      * the conference server ensures the radio is aware of the participants in the conference, which
    286      * are represented by child connections.
    287      * <p>
    288      * An example of a conference with no child connections is an IMS conference call with no
    289      * conference event package support.  Such a conference is represented by the radio as a single
    290      * connection to the IMS conference server.
    291      * <p>
    292      * Indicating whether a conference has children or not is important to help user interfaces
    293      * visually represent a conference.  A conference with no children, for example, will have the
    294      * conference connection shown in the list of calls on a Bluetooth device, where if the
    295      * conference has children, only the children will be shown in the list of calls on a Bluetooth
    296      * device.
    297      * @hide
    298      */
    299     public static final int CAPABILITY_CONFERENCE_HAS_NO_CHILDREN = 0x00200000;
    300 
    301     /**
    302      * Indicates that the connection itself wants to handle any sort of reply response, rather than
    303      * relying on SMS.
    304      */
    305     public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 0x00400000;
    306 
    307     /**
    308      * When set, prevents a video call from being downgraded to an audio-only call.
    309      * <p>
    310      * Should be set when the VideoState has the {@link VideoProfile#STATE_TX_ENABLED} or
    311      * {@link VideoProfile#STATE_RX_ENABLED} bits set to indicate that the connection cannot be
    312      * downgraded from a video call back to a VideoState of
    313      * {@link VideoProfile#STATE_AUDIO_ONLY}.
    314      * <p>
    315      * Intuitively, a call which can be downgraded to audio should also have local and remote
    316      * video
    317      * capabilities (see {@link #CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL} and
    318      * {@link #CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL}).
    319      */
    320     public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 0x00800000;
    321 
    322     /**
    323      * When set for an external connection, indicates that this {@code Connection} can be pulled
    324      * from a remote device to the current device.
    325      * <p>
    326      * Should only be set on a {@code Connection} where {@link #PROPERTY_IS_EXTERNAL_CALL}
    327      * is set.
    328      */
    329     public static final int CAPABILITY_CAN_PULL_CALL = 0x01000000;
    330 
    331     //**********************************************************************************************
    332     // Next CAPABILITY value: 0x02000000
    333     //**********************************************************************************************
    334 
    335     /**
    336      * Indicates that the current device callback number should be shown.
    337      *
    338      * @hide
    339      */
    340     public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 1<<0;
    341 
    342     /**
    343      * Whether the call is a generic conference, where we do not know the precise state of
    344      * participants in the conference (eg. on CDMA).
    345      *
    346      * @hide
    347      */
    348     public static final int PROPERTY_GENERIC_CONFERENCE = 1<<1;
    349 
    350     /**
    351      * Connection is using high definition audio.
    352      * @hide
    353      */
    354     public static final int PROPERTY_HIGH_DEF_AUDIO = 1<<2;
    355 
    356     /**
    357      * Connection is using WIFI.
    358      * @hide
    359      */
    360     public static final int PROPERTY_WIFI = 1<<3;
    361 
    362     /**
    363      * When set, indicates that the {@code Connection} does not actually exist locally for the
    364      * {@link ConnectionService}.
    365      * <p>
    366      * Consider, for example, a scenario where a user has two devices with the same phone number.
    367      * When a user places a call on one devices, the telephony stack can represent that call on the
    368      * other device by adding is to the {@link ConnectionService} with the
    369      * {@link #PROPERTY_IS_EXTERNAL_CALL} capability set.
    370      * <p>
    371      * An {@link ConnectionService} should not assume that all {@link InCallService}s will handle
    372      * external connections.  Only those {@link InCallService}s which have the
    373      * {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true} in its
    374      * manifest will see external connections.
    375      */
    376     public static final int PROPERTY_IS_EXTERNAL_CALL = 1<<4;
    377 
    378     /**
    379      * Indicates that the connection has CDMA Enhanced Voice Privacy enabled.
    380      */
    381     public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 1<<5;
    382 
    383     /**
    384      * Indicates that the connection represents a downgraded IMS conference.
    385      * @hide
    386      */
    387     public static final int PROPERTY_IS_DOWNGRADED_CONFERENCE = 1<<6;
    388 
    389     /**
    390      * Set by the framework to indicate that the {@link Connection} originated from a self-managed
    391      * {@link ConnectionService}.
    392      * <p>
    393      * See {@link PhoneAccount#CAPABILITY_SELF_MANAGED}.
    394      */
    395     public static final int PROPERTY_SELF_MANAGED = 1<<7;
    396 
    397     /**
    398      * When set, indicates that a connection has an active RTT session associated with it.
    399      * @hide
    400      */
    401     public static final int PROPERTY_IS_RTT = 1 << 8;
    402 
    403     //**********************************************************************************************
    404     // Next PROPERTY value: 1<<9
    405     //**********************************************************************************************
    406 
    407     /**
    408      * Connection extra key used to store the last forwarded number associated with the current
    409      * connection.  Used to communicate to the user interface that the connection was forwarded via
    410      * the specified number.
    411      */
    412     public static final String EXTRA_LAST_FORWARDED_NUMBER =
    413             "android.telecom.extra.LAST_FORWARDED_NUMBER";
    414 
    415     /**
    416      * Connection extra key used to store a child number associated with the current connection.
    417      * Used to communicate to the user interface that the connection was received via
    418      * a child address (i.e. phone number) associated with the {@link PhoneAccount}'s primary
    419      * address.
    420      */
    421     public static final String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
    422 
    423     /**
    424      * Connection extra key used to store the subject for an incoming call.  The user interface can
    425      * query this extra and display its contents for incoming calls.  Will only be used if the
    426      * {@link PhoneAccount} supports the capability {@link PhoneAccount#CAPABILITY_CALL_SUBJECT}.
    427      */
    428     public static final String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
    429 
    430     /**
    431      * Boolean connection extra key set on a {@link Connection} in
    432      * {@link Connection#STATE_RINGING} state to indicate that answering the call will cause the
    433      * current active foreground call to be dropped.
    434      */
    435     public static final String EXTRA_ANSWERING_DROPS_FG_CALL =
    436             "android.telecom.extra.ANSWERING_DROPS_FG_CALL";
    437 
    438     /**
    439      * String connection extra key set on a {@link Connection} in {@link Connection#STATE_RINGING}
    440      * state to indicate the name of the third-party app which is responsible for the current
    441      * foreground call.
    442      * <p>
    443      * Used when {@link #EXTRA_ANSWERING_DROPS_FG_CALL} is true to ensure that the default Phone app
    444      * is able to inform the user that answering the new incoming call will cause a call owned by
    445      * another app to be dropped when the incoming call is answered.
    446      */
    447     public static final String EXTRA_ANSWERING_DROPS_FG_CALL_APP_NAME =
    448             "android.telecom.extra.ANSWERING_DROPS_FG_CALL_APP_NAME";
    449 
    450     /**
    451      * Boolean connection extra key on a {@link Connection} which indicates that adding an
    452      * additional call is disallowed.
    453      * @hide
    454      */
    455     public static final String EXTRA_DISABLE_ADD_CALL =
    456             "android.telecom.extra.DISABLE_ADD_CALL";
    457 
    458     /**
    459      * String connection extra key on a {@link Connection} or {@link Conference} which contains the
    460      * original Connection ID associated with the connection.  Used in
    461      * {@link RemoteConnectionService} to track the Connection ID which was originally assigned to a
    462      * connection/conference added via
    463      * {@link ConnectionService#addExistingConnection(PhoneAccountHandle, Connection)} and
    464      * {@link ConnectionService#addConference(Conference)} APIs.  This is important to pass to
    465      * Telecom for when it deals with RemoteConnections.  When the ConnectionManager wraps the
    466      * {@link RemoteConnection} and {@link RemoteConference} and adds it to Telecom, there needs to
    467      * be a way to ensure that we don't add the connection again as a duplicate.
    468      * <p>
    469      * For example, the TelephonyCS calls addExistingConnection for a Connection with ID
    470      * {@code TelephonyCS@1}.  The ConnectionManager learns of this via
    471      * {@link ConnectionService#onRemoteExistingConnectionAdded(RemoteConnection)}, and wraps this
    472      * in a new {@link Connection} which it adds to Telecom via
    473      * {@link ConnectionService#addExistingConnection(PhoneAccountHandle, Connection)}.  As part of
    474      * this process, the wrapped RemoteConnection gets assigned a new ID (e.g. {@code ConnMan@1}).
    475      * The TelephonyCS will ALSO try to add the existing connection to Telecom, except with the
    476      * ID it originally referred to the connection as.  Thus Telecom needs to know that the
    477      * Connection with ID {@code ConnMan@1} is really the same as {@code TelephonyCS@1}.
    478      * @hide
    479      */
    480     public static final String EXTRA_ORIGINAL_CONNECTION_ID =
    481             "android.telecom.extra.ORIGINAL_CONNECTION_ID";
    482 
    483     /**
    484      * Connection event used to inform Telecom that it should play the on hold tone.  This is used
    485      * to play a tone when the peer puts the current call on hold.  Sent to Telecom via
    486      * {@link #sendConnectionEvent(String, Bundle)}.
    487      * @hide
    488      */
    489     public static final String EVENT_ON_HOLD_TONE_START =
    490             "android.telecom.event.ON_HOLD_TONE_START";
    491 
    492     /**
    493      * Connection event used to inform Telecom that it should stop the on hold tone.  This is used
    494      * to stop a tone when the peer puts the current call on hold.  Sent to Telecom via
    495      * {@link #sendConnectionEvent(String, Bundle)}.
    496      * @hide
    497      */
    498     public static final String EVENT_ON_HOLD_TONE_END =
    499             "android.telecom.event.ON_HOLD_TONE_END";
    500 
    501     /**
    502      * Connection event used to inform {@link InCallService}s when pulling of an external call has
    503      * failed.  The user interface should inform the user of the error.
    504      * <p>
    505      * Expected to be used by the {@link ConnectionService} when the {@link Call#pullExternalCall()}
    506      * API is called on a {@link Call} with the properties
    507      * {@link Call.Details#PROPERTY_IS_EXTERNAL_CALL} and
    508      * {@link Call.Details#CAPABILITY_CAN_PULL_CALL}, but the {@link ConnectionService} could not
    509      * pull the external call due to an error condition.
    510      * <p>
    511      * Sent via {@link #sendConnectionEvent(String, Bundle)}.  The {@link Bundle} parameter is
    512      * expected to be null when this connection event is used.
    513      */
    514     public static final String EVENT_CALL_PULL_FAILED = "android.telecom.event.CALL_PULL_FAILED";
    515 
    516     /**
    517      * Connection event used to inform {@link InCallService}s when the merging of two calls has
    518      * failed. The User Interface should use this message to inform the user of the error.
    519      * <p>
    520      * Sent via {@link #sendConnectionEvent(String, Bundle)}.  The {@link Bundle} parameter is
    521      * expected to be null when this connection event is used.
    522      */
    523     public static final String EVENT_CALL_MERGE_FAILED = "android.telecom.event.CALL_MERGE_FAILED";
    524 
    525     /**
    526      * Connection event used to inform {@link InCallService}s when the process of merging a
    527      * Connection into a conference has begun.
    528      * <p>
    529      * Sent via {@link #sendConnectionEvent(String, Bundle)}.  The {@link Bundle} parameter is
    530      * expected to be null when this connection event is used.
    531      * @hide
    532      */
    533     public static final String EVENT_MERGE_START = "android.telecom.event.MERGE_START";
    534 
    535     /**
    536      * Connection event used to inform {@link InCallService}s when the process of merging a
    537      * Connection into a conference has completed.
    538      * <p>
    539      * Sent via {@link #sendConnectionEvent(String, Bundle)}.  The {@link Bundle} parameter is
    540      * expected to be null when this connection event is used.
    541      * @hide
    542      */
    543     public static final String EVENT_MERGE_COMPLETE = "android.telecom.event.MERGE_COMPLETE";
    544 
    545     /**
    546      * Connection event used to inform {@link InCallService}s when a call has been put on hold by
    547      * the remote party.
    548      * <p>
    549      * This is different than the {@link Connection#STATE_HOLDING} state which indicates that the
    550      * call is being held locally on the device.  When a capable {@link ConnectionService} receives
    551      * signalling to indicate that the remote party has put the call on hold, it can send this
    552      * connection event.
    553      * @hide
    554      */
    555     public static final String EVENT_CALL_REMOTELY_HELD =
    556             "android.telecom.event.CALL_REMOTELY_HELD";
    557 
    558     /**
    559      * Connection event used to inform {@link InCallService}s when a call which was remotely held
    560      * (see {@link #EVENT_CALL_REMOTELY_HELD}) has been un-held by the remote party.
    561      * <p>
    562      * This is different than the {@link Connection#STATE_HOLDING} state which indicates that the
    563      * call is being held locally on the device.  When a capable {@link ConnectionService} receives
    564      * signalling to indicate that the remote party has taken the call off hold, it can send this
    565      * connection event.
    566      * @hide
    567      */
    568     public static final String EVENT_CALL_REMOTELY_UNHELD =
    569             "android.telecom.event.CALL_REMOTELY_UNHELD";
    570 
    571     // Flag controlling whether PII is emitted into the logs
    572     private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
    573 
    574     /**
    575      * Whether the given capabilities support the specified capability.
    576      *
    577      * @param capabilities A capability bit field.
    578      * @param capability The capability to check capabilities for.
    579      * @return Whether the specified capability is supported.
    580      * @hide
    581      */
    582     public static boolean can(int capabilities, int capability) {
    583         return (capabilities & capability) == capability;
    584     }
    585 
    586     /**
    587      * Whether the capabilities of this {@code Connection} supports the specified capability.
    588      *
    589      * @param capability The capability to check capabilities for.
    590      * @return Whether the specified capability is supported.
    591      * @hide
    592      */
    593     public boolean can(int capability) {
    594         return can(mConnectionCapabilities, capability);
    595     }
    596 
    597     /**
    598      * Removes the specified capability from the set of capabilities of this {@code Connection}.
    599      *
    600      * @param capability The capability to remove from the set.
    601      * @hide
    602      */
    603     public void removeCapability(int capability) {
    604         mConnectionCapabilities &= ~capability;
    605     }
    606 
    607     /**
    608      * Adds the specified capability to the set of capabilities of this {@code Connection}.
    609      *
    610      * @param capability The capability to add to the set.
    611      * @hide
    612      */
    613     public void addCapability(int capability) {
    614         mConnectionCapabilities |= capability;
    615     }
    616 
    617     /**
    618      * Renders a set of capability bits ({@code CAPABILITY_*}) as a human readable string.
    619      *
    620      * @param capabilities A capability bit field.
    621      * @return A human readable string representation.
    622      */
    623     public static String capabilitiesToString(int capabilities) {
    624         return capabilitiesToStringInternal(capabilities, true /* isLong */);
    625     }
    626 
    627     /**
    628      * Renders a set of capability bits ({@code CAPABILITY_*}) as a *short* human readable
    629      * string.
    630      *
    631      * @param capabilities A capability bit field.
    632      * @return A human readable string representation.
    633      * @hide
    634      */
    635     public static String capabilitiesToStringShort(int capabilities) {
    636         return capabilitiesToStringInternal(capabilities, false /* isLong */);
    637     }
    638 
    639     private static String capabilitiesToStringInternal(int capabilities, boolean isLong) {
    640         StringBuilder builder = new StringBuilder();
    641         builder.append("[");
    642         if (isLong) {
    643             builder.append("Capabilities:");
    644         }
    645 
    646         if (can(capabilities, CAPABILITY_HOLD)) {
    647             builder.append(isLong ? " CAPABILITY_HOLD" : " hld");
    648         }
    649         if (can(capabilities, CAPABILITY_SUPPORT_HOLD)) {
    650             builder.append(isLong ? " CAPABILITY_SUPPORT_HOLD" : " sup_hld");
    651         }
    652         if (can(capabilities, CAPABILITY_MERGE_CONFERENCE)) {
    653             builder.append(isLong ? " CAPABILITY_MERGE_CONFERENCE" : " mrg_cnf");
    654         }
    655         if (can(capabilities, CAPABILITY_SWAP_CONFERENCE)) {
    656             builder.append(isLong ? " CAPABILITY_SWAP_CONFERENCE" : " swp_cnf");
    657         }
    658         if (can(capabilities, CAPABILITY_RESPOND_VIA_TEXT)) {
    659             builder.append(isLong ? " CAPABILITY_RESPOND_VIA_TEXT" : " txt");
    660         }
    661         if (can(capabilities, CAPABILITY_MUTE)) {
    662             builder.append(isLong ? " CAPABILITY_MUTE" : " mut");
    663         }
    664         if (can(capabilities, CAPABILITY_MANAGE_CONFERENCE)) {
    665             builder.append(isLong ? " CAPABILITY_MANAGE_CONFERENCE" : " mng_cnf");
    666         }
    667         if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_RX)) {
    668             builder.append(isLong ? " CAPABILITY_SUPPORTS_VT_LOCAL_RX" : " VTlrx");
    669         }
    670         if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_TX)) {
    671             builder.append(isLong ? " CAPABILITY_SUPPORTS_VT_LOCAL_TX" : " VTltx");
    672         }
    673         if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)) {
    674             builder.append(isLong ? " CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL" : " VTlbi");
    675         }
    676         if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_RX)) {
    677             builder.append(isLong ? " CAPABILITY_SUPPORTS_VT_REMOTE_RX" : " VTrrx");
    678         }
    679         if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_TX)) {
    680             builder.append(isLong ? " CAPABILITY_SUPPORTS_VT_REMOTE_TX" : " VTrtx");
    681         }
    682         if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)) {
    683             builder.append(isLong ? " CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL" : " VTrbi");
    684         }
    685         if (can(capabilities, CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO)) {
    686             builder.append(isLong ? " CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO" : " !v2a");
    687         }
    688         if (can(capabilities, CAPABILITY_SPEED_UP_MT_AUDIO)) {
    689             builder.append(isLong ? " CAPABILITY_SPEED_UP_MT_AUDIO" : " spd_aud");
    690         }
    691         if (can(capabilities, CAPABILITY_CAN_UPGRADE_TO_VIDEO)) {
    692             builder.append(isLong ? " CAPABILITY_CAN_UPGRADE_TO_VIDEO" : " a2v");
    693         }
    694         if (can(capabilities, CAPABILITY_CAN_PAUSE_VIDEO)) {
    695             builder.append(isLong ? " CAPABILITY_CAN_PAUSE_VIDEO" : " paus_VT");
    696         }
    697         if (can(capabilities, CAPABILITY_CONFERENCE_HAS_NO_CHILDREN)) {
    698             builder.append(isLong ? " CAPABILITY_SINGLE_PARTY_CONFERENCE" : " 1p_cnf");
    699         }
    700         if (can(capabilities, CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION)) {
    701             builder.append(isLong ? " CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION" : " rsp_by_con");
    702         }
    703         if (can(capabilities, CAPABILITY_CAN_PULL_CALL)) {
    704             builder.append(isLong ? " CAPABILITY_CAN_PULL_CALL" : " pull");
    705         }
    706 
    707         builder.append("]");
    708         return builder.toString();
    709     }
    710 
    711     /**
    712      * Renders a set of property bits ({@code PROPERTY_*}) as a human readable string.
    713      *
    714      * @param properties A property bit field.
    715      * @return A human readable string representation.
    716      */
    717     public static String propertiesToString(int properties) {
    718         return propertiesToStringInternal(properties, true /* isLong */);
    719     }
    720 
    721     /**
    722      * Renders a set of property bits ({@code PROPERTY_*}) as a *short* human readable string.
    723      *
    724      * @param properties A property bit field.
    725      * @return A human readable string representation.
    726      * @hide
    727      */
    728     public static String propertiesToStringShort(int properties) {
    729         return propertiesToStringInternal(properties, false /* isLong */);
    730     }
    731 
    732     private static String propertiesToStringInternal(int properties, boolean isLong) {
    733         StringBuilder builder = new StringBuilder();
    734         builder.append("[");
    735         if (isLong) {
    736             builder.append("Properties:");
    737         }
    738 
    739         if (can(properties, PROPERTY_SELF_MANAGED)) {
    740             builder.append(isLong ? " PROPERTY_SELF_MANAGED" : " self_mng");
    741         }
    742 
    743         if (can(properties, PROPERTY_EMERGENCY_CALLBACK_MODE)) {
    744             builder.append(isLong ? " PROPERTY_EMERGENCY_CALLBACK_MODE" : " ecbm");
    745         }
    746 
    747         if (can(properties, PROPERTY_HIGH_DEF_AUDIO)) {
    748             builder.append(isLong ? " PROPERTY_HIGH_DEF_AUDIO" : " HD");
    749         }
    750 
    751         if (can(properties, PROPERTY_WIFI)) {
    752             builder.append(isLong ? " PROPERTY_WIFI" : " wifi");
    753         }
    754 
    755         if (can(properties, PROPERTY_GENERIC_CONFERENCE)) {
    756             builder.append(isLong ? " PROPERTY_GENERIC_CONFERENCE" : " gen_conf");
    757         }
    758 
    759         if (can(properties, PROPERTY_IS_EXTERNAL_CALL)) {
    760             builder.append(isLong ? " PROPERTY_IS_EXTERNAL_CALL" : " xtrnl");
    761         }
    762 
    763         if (can(properties, PROPERTY_HAS_CDMA_VOICE_PRIVACY)) {
    764             builder.append(isLong ? " PROPERTY_HAS_CDMA_VOICE_PRIVACY" : " priv");
    765         }
    766 
    767         builder.append("]");
    768         return builder.toString();
    769     }
    770 
    771     /** @hide */
    772     public abstract static class Listener {
    773         public void onStateChanged(Connection c, int state) {}
    774         public void onAddressChanged(Connection c, Uri newAddress, int presentation) {}
    775         public void onCallerDisplayNameChanged(
    776                 Connection c, String callerDisplayName, int presentation) {}
    777         public void onVideoStateChanged(Connection c, int videoState) {}
    778         public void onDisconnected(Connection c, DisconnectCause disconnectCause) {}
    779         public void onPostDialWait(Connection c, String remaining) {}
    780         public void onPostDialChar(Connection c, char nextChar) {}
    781         public void onRingbackRequested(Connection c, boolean ringback) {}
    782         public void onDestroyed(Connection c) {}
    783         public void onConnectionCapabilitiesChanged(Connection c, int capabilities) {}
    784         public void onConnectionPropertiesChanged(Connection c, int properties) {}
    785         public void onSupportedAudioRoutesChanged(Connection c, int supportedAudioRoutes) {}
    786         public void onVideoProviderChanged(
    787                 Connection c, VideoProvider videoProvider) {}
    788         public void onAudioModeIsVoipChanged(Connection c, boolean isVoip) {}
    789         public void onStatusHintsChanged(Connection c, StatusHints statusHints) {}
    790         public void onConferenceablesChanged(
    791                 Connection c, List<Conferenceable> conferenceables) {}
    792         public void onConferenceChanged(Connection c, Conference conference) {}
    793         /** @hide */
    794         public void onConferenceParticipantsChanged(Connection c,
    795                 List<ConferenceParticipant> participants) {}
    796         public void onConferenceStarted() {}
    797         public void onConferenceMergeFailed(Connection c) {}
    798         public void onExtrasChanged(Connection c, Bundle extras) {}
    799         public void onExtrasRemoved(Connection c, List<String> keys) {}
    800         public void onConnectionEvent(Connection c, String event, Bundle extras) {}
    801         /** @hide */
    802         public void onConferenceSupportedChanged(Connection c, boolean isConferenceSupported) {}
    803         public void onAudioRouteChanged(Connection c, int audioRoute) {}
    804         public void onRttInitiationSuccess(Connection c) {}
    805         public void onRttInitiationFailure(Connection c, int reason) {}
    806         public void onRttSessionRemotelyTerminated(Connection c) {}
    807         public void onRemoteRttRequest(Connection c) {}
    808     }
    809 
    810     /**
    811      * Provides methods to read and write RTT data to/from the in-call app.
    812      * @hide
    813      */
    814     public static final class RttTextStream {
    815         private static final int READ_BUFFER_SIZE = 1000;
    816         private final InputStreamReader mPipeFromInCall;
    817         private final OutputStreamWriter mPipeToInCall;
    818         private final ParcelFileDescriptor mFdFromInCall;
    819         private final ParcelFileDescriptor mFdToInCall;
    820         private char[] mReadBuffer = new char[READ_BUFFER_SIZE];
    821 
    822         /**
    823          * @hide
    824          */
    825         public RttTextStream(ParcelFileDescriptor toInCall, ParcelFileDescriptor fromInCall) {
    826             mFdFromInCall = fromInCall;
    827             mFdToInCall = toInCall;
    828             mPipeFromInCall = new InputStreamReader(
    829                     new ParcelFileDescriptor.AutoCloseInputStream(fromInCall));
    830             mPipeToInCall = new OutputStreamWriter(
    831                     new ParcelFileDescriptor.AutoCloseOutputStream(toInCall));
    832         }
    833 
    834         /**
    835          * Writes the string {@param input} into the text stream to the UI for this RTT call. Since
    836          * RTT transmits text in real-time, this method should be called as often as text snippets
    837          * are received from the remote user, even if it is only one character.
    838          *
    839          * This method is not thread-safe -- calling it from multiple threads simultaneously may
    840          * lead to interleaved text.
    841          * @param input The message to send to the in-call app.
    842          */
    843         public void write(String input) throws IOException {
    844             mPipeToInCall.write(input);
    845             mPipeToInCall.flush();
    846         }
    847 
    848 
    849         /**
    850          * Reads a string from the in-call app, blocking if there is no data available. Returns
    851          * {@code null} if the RTT conversation has been terminated and there is no further data
    852          * to read.
    853          *
    854          * This method is not thread-safe -- calling it from multiple threads simultaneously may
    855          * lead to interleaved text.
    856          * @return A string containing text entered by the user, or {@code null} if the
    857          * conversation has been terminated or if there was an error while reading.
    858          */
    859         public String read() {
    860             try {
    861                 int numRead = mPipeFromInCall.read(mReadBuffer, 0, READ_BUFFER_SIZE);
    862                 if (numRead < 0) {
    863                     return null;
    864                 }
    865                 return new String(mReadBuffer, 0, numRead);
    866             } catch (IOException e) {
    867                 Log.w(this, "Exception encountered when reading from InputStreamReader: %s", e);
    868                 return null;
    869             }
    870         }
    871 
    872         /** @hide */
    873         public ParcelFileDescriptor getFdFromInCall() {
    874             return mFdFromInCall;
    875         }
    876 
    877         /** @hide */
    878         public ParcelFileDescriptor getFdToInCall() {
    879             return mFdToInCall;
    880         }
    881     }
    882 
    883     /**
    884      * Provides constants to represent the results of responses to session modify requests sent via
    885      * {@link Call#sendRttRequest()}
    886      */
    887     public static final class RttModifyStatus {
    888         private RttModifyStatus() {}
    889         /**
    890          * Session modify request was successful.
    891          */
    892         public static final int SESSION_MODIFY_REQUEST_SUCCESS = 1;
    893 
    894         /**
    895          * Session modify request failed.
    896          */
    897         public static final int SESSION_MODIFY_REQUEST_FAIL = 2;
    898 
    899         /**
    900          * Session modify request ignored due to invalid parameters.
    901          */
    902         public static final int SESSION_MODIFY_REQUEST_INVALID = 3;
    903 
    904         /**
    905          * Session modify request timed out.
    906          */
    907         public static final int SESSION_MODIFY_REQUEST_TIMED_OUT = 4;
    908 
    909         /**
    910          * Session modify request rejected by remote user.
    911          */
    912         public static final int SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE = 5;
    913     }
    914 
    915     /**
    916      * Provides a means of controlling the video session associated with a {@link Connection}.
    917      * <p>
    918      * Implementations create a custom subclass of {@link VideoProvider} and the
    919      * {@link ConnectionService} creates an instance sets it on the {@link Connection} using
    920      * {@link Connection#setVideoProvider(VideoProvider)}.  Any connection which supports video
    921      * should set the {@link VideoProvider}.
    922      * <p>
    923      * The {@link VideoProvider} serves two primary purposes: it provides a means for Telecom and
    924      * {@link InCallService} implementations to issue requests related to the video session;
    925      * it provides a means for the {@link ConnectionService} to report events and information
    926      * related to the video session to Telecom and the {@link InCallService} implementations.
    927      * <p>
    928      * {@link InCallService} implementations interact with the {@link VideoProvider} via
    929      * {@link android.telecom.InCallService.VideoCall}.
    930      */
    931     public static abstract class VideoProvider {
    932         /**
    933          * Video is not being received (no protocol pause was issued).
    934          * @see #handleCallSessionEvent(int)
    935          */
    936         public static final int SESSION_EVENT_RX_PAUSE = 1;
    937 
    938         /**
    939          * Video reception has resumed after a {@link #SESSION_EVENT_RX_PAUSE}.
    940          * @see #handleCallSessionEvent(int)
    941          */
    942         public static final int SESSION_EVENT_RX_RESUME = 2;
    943 
    944         /**
    945          * Video transmission has begun. This occurs after a negotiated start of video transmission
    946          * when the underlying protocol has actually begun transmitting video to the remote party.
    947          * @see #handleCallSessionEvent(int)
    948          */
    949         public static final int SESSION_EVENT_TX_START = 3;
    950 
    951         /**
    952          * Video transmission has stopped. This occurs after a negotiated stop of video transmission
    953          * when the underlying protocol has actually stopped transmitting video to the remote party.
    954          * @see #handleCallSessionEvent(int)
    955          */
    956         public static final int SESSION_EVENT_TX_STOP = 4;
    957 
    958         /**
    959          * A camera failure has occurred for the selected camera.  The {@link VideoProvider} can use
    960          * this as a cue to inform the user the camera is not available.
    961          * @see #handleCallSessionEvent(int)
    962          */
    963         public static final int SESSION_EVENT_CAMERA_FAILURE = 5;
    964 
    965         /**
    966          * Issued after {@link #SESSION_EVENT_CAMERA_FAILURE} when the camera is once again ready
    967          * for operation.  The {@link VideoProvider} can use this as a cue to inform the user that
    968          * the camera has become available again.
    969          * @see #handleCallSessionEvent(int)
    970          */
    971         public static final int SESSION_EVENT_CAMERA_READY = 6;
    972 
    973         /**
    974          * Session event raised by Telecom when
    975          * {@link android.telecom.InCallService.VideoCall#setCamera(String)} is called and the
    976          * caller does not have the necessary {@link android.Manifest.permission#CAMERA} permission.
    977          * @see #handleCallSessionEvent(int)
    978          */
    979         public static final int SESSION_EVENT_CAMERA_PERMISSION_ERROR = 7;
    980 
    981         /**
    982          * Session modify request was successful.
    983          * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
    984          */
    985         public static final int SESSION_MODIFY_REQUEST_SUCCESS = 1;
    986 
    987         /**
    988          * Session modify request failed.
    989          * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
    990          */
    991         public static final int SESSION_MODIFY_REQUEST_FAIL = 2;
    992 
    993         /**
    994          * Session modify request ignored due to invalid parameters.
    995          * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
    996          */
    997         public static final int SESSION_MODIFY_REQUEST_INVALID = 3;
    998 
    999         /**
   1000          * Session modify request timed out.
   1001          * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
   1002          */
   1003         public static final int SESSION_MODIFY_REQUEST_TIMED_OUT = 4;
   1004 
   1005         /**
   1006          * Session modify request rejected by remote user.
   1007          * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
   1008          */
   1009         public static final int SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE = 5;
   1010 
   1011         private static final int MSG_ADD_VIDEO_CALLBACK = 1;
   1012         private static final int MSG_SET_CAMERA = 2;
   1013         private static final int MSG_SET_PREVIEW_SURFACE = 3;
   1014         private static final int MSG_SET_DISPLAY_SURFACE = 4;
   1015         private static final int MSG_SET_DEVICE_ORIENTATION = 5;
   1016         private static final int MSG_SET_ZOOM = 6;
   1017         private static final int MSG_SEND_SESSION_MODIFY_REQUEST = 7;
   1018         private static final int MSG_SEND_SESSION_MODIFY_RESPONSE = 8;
   1019         private static final int MSG_REQUEST_CAMERA_CAPABILITIES = 9;
   1020         private static final int MSG_REQUEST_CONNECTION_DATA_USAGE = 10;
   1021         private static final int MSG_SET_PAUSE_IMAGE = 11;
   1022         private static final int MSG_REMOVE_VIDEO_CALLBACK = 12;
   1023 
   1024         private static final String SESSION_EVENT_RX_PAUSE_STR = "RX_PAUSE";
   1025         private static final String SESSION_EVENT_RX_RESUME_STR = "RX_RESUME";
   1026         private static final String SESSION_EVENT_TX_START_STR = "TX_START";
   1027         private static final String SESSION_EVENT_TX_STOP_STR = "TX_STOP";
   1028         private static final String SESSION_EVENT_CAMERA_FAILURE_STR = "CAMERA_FAIL";
   1029         private static final String SESSION_EVENT_CAMERA_READY_STR = "CAMERA_READY";
   1030         private static final String SESSION_EVENT_CAMERA_PERMISSION_ERROR_STR =
   1031                 "CAMERA_PERMISSION_ERROR";
   1032         private static final String SESSION_EVENT_UNKNOWN_STR = "UNKNOWN";
   1033 
   1034         private VideoProvider.VideoProviderHandler mMessageHandler;
   1035         private final VideoProvider.VideoProviderBinder mBinder;
   1036 
   1037         /**
   1038          * Stores a list of the video callbacks, keyed by IBinder.
   1039          *
   1040          * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
   1041          * load factor before resizing, 1 means we only expect a single thread to
   1042          * access the map so make only a single shard
   1043          */
   1044         private ConcurrentHashMap<IBinder, IVideoCallback> mVideoCallbacks =
   1045                 new ConcurrentHashMap<IBinder, IVideoCallback>(8, 0.9f, 1);
   1046 
   1047         /**
   1048          * Default handler used to consolidate binder method calls onto a single thread.
   1049          */
   1050         private final class VideoProviderHandler extends Handler {
   1051             public VideoProviderHandler() {
   1052                 super();
   1053             }
   1054 
   1055             public VideoProviderHandler(Looper looper) {
   1056                 super(looper);
   1057             }
   1058 
   1059             @Override
   1060             public void handleMessage(Message msg) {
   1061                 switch (msg.what) {
   1062                     case MSG_ADD_VIDEO_CALLBACK: {
   1063                         IBinder binder = (IBinder) msg.obj;
   1064                         IVideoCallback callback = IVideoCallback.Stub
   1065                                 .asInterface((IBinder) msg.obj);
   1066                         if (callback == null) {
   1067                             Log.w(this, "addVideoProvider - skipped; callback is null.");
   1068                             break;
   1069                         }
   1070 
   1071                         if (mVideoCallbacks.containsKey(binder)) {
   1072                             Log.i(this, "addVideoProvider - skipped; already present.");
   1073                             break;
   1074                         }
   1075                         mVideoCallbacks.put(binder, callback);
   1076                         break;
   1077                     }
   1078                     case MSG_REMOVE_VIDEO_CALLBACK: {
   1079                         IBinder binder = (IBinder) msg.obj;
   1080                         IVideoCallback callback = IVideoCallback.Stub
   1081                                 .asInterface((IBinder) msg.obj);
   1082                         if (!mVideoCallbacks.containsKey(binder)) {
   1083                             Log.i(this, "removeVideoProvider - skipped; not present.");
   1084                             break;
   1085                         }
   1086                         mVideoCallbacks.remove(binder);
   1087                         break;
   1088                     }
   1089                     case MSG_SET_CAMERA:
   1090                     {
   1091                         SomeArgs args = (SomeArgs) msg.obj;
   1092                         try {
   1093                             onSetCamera((String) args.arg1);
   1094                             onSetCamera((String) args.arg1, (String) args.arg2, args.argi1,
   1095                                     args.argi2, args.argi3);
   1096                         } finally {
   1097                             args.recycle();
   1098                         }
   1099                     }
   1100                     break;
   1101                     case MSG_SET_PREVIEW_SURFACE:
   1102                         onSetPreviewSurface((Surface) msg.obj);
   1103                         break;
   1104                     case MSG_SET_DISPLAY_SURFACE:
   1105                         onSetDisplaySurface((Surface) msg.obj);
   1106                         break;
   1107                     case MSG_SET_DEVICE_ORIENTATION:
   1108                         onSetDeviceOrientation(msg.arg1);
   1109                         break;
   1110                     case MSG_SET_ZOOM:
   1111                         onSetZoom((Float) msg.obj);
   1112                         break;
   1113                     case MSG_SEND_SESSION_MODIFY_REQUEST: {
   1114                         SomeArgs args = (SomeArgs) msg.obj;
   1115                         try {
   1116                             onSendSessionModifyRequest((VideoProfile) args.arg1,
   1117                                     (VideoProfile) args.arg2);
   1118                         } finally {
   1119                             args.recycle();
   1120                         }
   1121                         break;
   1122                     }
   1123                     case MSG_SEND_SESSION_MODIFY_RESPONSE:
   1124                         onSendSessionModifyResponse((VideoProfile) msg.obj);
   1125                         break;
   1126                     case MSG_REQUEST_CAMERA_CAPABILITIES:
   1127                         onRequestCameraCapabilities();
   1128                         break;
   1129                     case MSG_REQUEST_CONNECTION_DATA_USAGE:
   1130                         onRequestConnectionDataUsage();
   1131                         break;
   1132                     case MSG_SET_PAUSE_IMAGE:
   1133                         onSetPauseImage((Uri) msg.obj);
   1134                         break;
   1135                     default:
   1136                         break;
   1137                 }
   1138             }
   1139         }
   1140 
   1141         /**
   1142          * IVideoProvider stub implementation.
   1143          */
   1144         private final class VideoProviderBinder extends IVideoProvider.Stub {
   1145             public void addVideoCallback(IBinder videoCallbackBinder) {
   1146                 mMessageHandler.obtainMessage(
   1147                         MSG_ADD_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
   1148             }
   1149 
   1150             public void removeVideoCallback(IBinder videoCallbackBinder) {
   1151                 mMessageHandler.obtainMessage(
   1152                         MSG_REMOVE_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
   1153             }
   1154 
   1155             public void setCamera(String cameraId, String callingPackageName,
   1156                                   int targetSdkVersion) {
   1157 
   1158                 SomeArgs args = SomeArgs.obtain();
   1159                 args.arg1 = cameraId;
   1160                 // Propagate the calling package; originally determined in
   1161                 // android.telecom.InCallService.VideoCall#setCamera(String) from the calling
   1162                 // process.
   1163                 args.arg2 = callingPackageName;
   1164                 // Pass along the uid and pid of the calling app; this gets lost when we put the
   1165                 // message onto the handler.  These are required for Telecom to perform a permission
   1166                 // check to see if the calling app is able to use the camera.
   1167                 args.argi1 = Binder.getCallingUid();
   1168                 args.argi2 = Binder.getCallingPid();
   1169                 // Pass along the target SDK version of the calling InCallService.  This is used to
   1170                 // maintain backwards compatibility of the API for older callers.
   1171                 args.argi3 = targetSdkVersion;
   1172                 mMessageHandler.obtainMessage(MSG_SET_CAMERA, args).sendToTarget();
   1173             }
   1174 
   1175             public void setPreviewSurface(Surface surface) {
   1176                 mMessageHandler.obtainMessage(MSG_SET_PREVIEW_SURFACE, surface).sendToTarget();
   1177             }
   1178 
   1179             public void setDisplaySurface(Surface surface) {
   1180                 mMessageHandler.obtainMessage(MSG_SET_DISPLAY_SURFACE, surface).sendToTarget();
   1181             }
   1182 
   1183             public void setDeviceOrientation(int rotation) {
   1184                 mMessageHandler.obtainMessage(
   1185                         MSG_SET_DEVICE_ORIENTATION, rotation, 0).sendToTarget();
   1186             }
   1187 
   1188             public void setZoom(float value) {
   1189                 mMessageHandler.obtainMessage(MSG_SET_ZOOM, value).sendToTarget();
   1190             }
   1191 
   1192             public void sendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile) {
   1193                 SomeArgs args = SomeArgs.obtain();
   1194                 args.arg1 = fromProfile;
   1195                 args.arg2 = toProfile;
   1196                 mMessageHandler.obtainMessage(MSG_SEND_SESSION_MODIFY_REQUEST, args).sendToTarget();
   1197             }
   1198 
   1199             public void sendSessionModifyResponse(VideoProfile responseProfile) {
   1200                 mMessageHandler.obtainMessage(
   1201                         MSG_SEND_SESSION_MODIFY_RESPONSE, responseProfile).sendToTarget();
   1202             }
   1203 
   1204             public void requestCameraCapabilities() {
   1205                 mMessageHandler.obtainMessage(MSG_REQUEST_CAMERA_CAPABILITIES).sendToTarget();
   1206             }
   1207 
   1208             public void requestCallDataUsage() {
   1209                 mMessageHandler.obtainMessage(MSG_REQUEST_CONNECTION_DATA_USAGE).sendToTarget();
   1210             }
   1211 
   1212             public void setPauseImage(Uri uri) {
   1213                 mMessageHandler.obtainMessage(MSG_SET_PAUSE_IMAGE, uri).sendToTarget();
   1214             }
   1215         }
   1216 
   1217         public VideoProvider() {
   1218             mBinder = new VideoProvider.VideoProviderBinder();
   1219             mMessageHandler = new VideoProvider.VideoProviderHandler(Looper.getMainLooper());
   1220         }
   1221 
   1222         /**
   1223          * Creates an instance of the {@link VideoProvider}, specifying the looper to use.
   1224          *
   1225          * @param looper The looper.
   1226          * @hide
   1227          */
   1228         public VideoProvider(Looper looper) {
   1229             mBinder = new VideoProvider.VideoProviderBinder();
   1230             mMessageHandler = new VideoProvider.VideoProviderHandler(looper);
   1231         }
   1232 
   1233         /**
   1234          * Returns binder object which can be used across IPC methods.
   1235          * @hide
   1236          */
   1237         public final IVideoProvider getInterface() {
   1238             return mBinder;
   1239         }
   1240 
   1241         /**
   1242          * Sets the camera to be used for the outgoing video.
   1243          * <p>
   1244          * The {@link VideoProvider} should respond by communicating the capabilities of the chosen
   1245          * camera via
   1246          * {@link VideoProvider#changeCameraCapabilities(VideoProfile.CameraCapabilities)}.
   1247          * <p>
   1248          * Sent from the {@link InCallService} via
   1249          * {@link InCallService.VideoCall#setCamera(String)}.
   1250          *
   1251          * @param cameraId The id of the camera (use ids as reported by
   1252          * {@link CameraManager#getCameraIdList()}).
   1253          */
   1254         public abstract void onSetCamera(String cameraId);
   1255 
   1256         /**
   1257          * Sets the camera to be used for the outgoing video.
   1258          * <p>
   1259          * The {@link VideoProvider} should respond by communicating the capabilities of the chosen
   1260          * camera via
   1261          * {@link VideoProvider#changeCameraCapabilities(VideoProfile.CameraCapabilities)}.
   1262          * <p>
   1263          * This prototype is used internally to ensure that the calling package name, UID and PID
   1264          * are sent to Telecom so that can perform a camera permission check on the caller.
   1265          * <p>
   1266          * Sent from the {@link InCallService} via
   1267          * {@link InCallService.VideoCall#setCamera(String)}.
   1268          *
   1269          * @param cameraId The id of the camera (use ids as reported by
   1270          * {@link CameraManager#getCameraIdList()}).
   1271          * @param callingPackageName The AppOpps package name of the caller.
   1272          * @param callingUid The UID of the caller.
   1273          * @param callingPid The PID of the caller.
   1274          * @param targetSdkVersion The target SDK version of the caller.
   1275          * @hide
   1276          */
   1277         public void onSetCamera(String cameraId, String callingPackageName, int callingUid,
   1278                 int callingPid, int targetSdkVersion) {}
   1279 
   1280         /**
   1281          * Sets the surface to be used for displaying a preview of what the user's camera is
   1282          * currently capturing.  When video transmission is enabled, this is the video signal which
   1283          * is sent to the remote device.
   1284          * <p>
   1285          * Sent from the {@link InCallService} via
   1286          * {@link InCallService.VideoCall#setPreviewSurface(Surface)}.
   1287          *
   1288          * @param surface The {@link Surface}.
   1289          */
   1290         public abstract void onSetPreviewSurface(Surface surface);
   1291 
   1292         /**
   1293          * Sets the surface to be used for displaying the video received from the remote device.
   1294          * <p>
   1295          * Sent from the {@link InCallService} via
   1296          * {@link InCallService.VideoCall#setDisplaySurface(Surface)}.
   1297          *
   1298          * @param surface The {@link Surface}.
   1299          */
   1300         public abstract void onSetDisplaySurface(Surface surface);
   1301 
   1302         /**
   1303          * Sets the device orientation, in degrees.  Assumes that a standard portrait orientation of
   1304          * the device is 0 degrees.
   1305          * <p>
   1306          * Sent from the {@link InCallService} via
   1307          * {@link InCallService.VideoCall#setDeviceOrientation(int)}.
   1308          *
   1309          * @param rotation The device orientation, in degrees.
   1310          */
   1311         public abstract void onSetDeviceOrientation(int rotation);
   1312 
   1313         /**
   1314          * Sets camera zoom ratio.
   1315          * <p>
   1316          * Sent from the {@link InCallService} via {@link InCallService.VideoCall#setZoom(float)}.
   1317          *
   1318          * @param value The camera zoom ratio.
   1319          */
   1320         public abstract void onSetZoom(float value);
   1321 
   1322         /**
   1323          * Issues a request to modify the properties of the current video session.
   1324          * <p>
   1325          * Example scenarios include: requesting an audio-only call to be upgraded to a
   1326          * bi-directional video call, turning on or off the user's camera, sending a pause signal
   1327          * when the {@link InCallService} is no longer the foreground application.
   1328          * <p>
   1329          * If the {@link VideoProvider} determines a request to be invalid, it should call
   1330          * {@link #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)} to report the
   1331          * invalid request back to the {@link InCallService}.
   1332          * <p>
   1333          * Where a request requires confirmation from the user of the peer device, the
   1334          * {@link VideoProvider} must communicate the request to the peer device and handle the
   1335          * user's response.  {@link #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)}
   1336          * is used to inform the {@link InCallService} of the result of the request.
   1337          * <p>
   1338          * Sent from the {@link InCallService} via
   1339          * {@link InCallService.VideoCall#sendSessionModifyRequest(VideoProfile)}.
   1340          *
   1341          * @param fromProfile The video profile prior to the request.
   1342          * @param toProfile The video profile with the requested changes made.
   1343          */
   1344         public abstract void onSendSessionModifyRequest(VideoProfile fromProfile,
   1345                 VideoProfile toProfile);
   1346 
   1347         /**
   1348          * Provides a response to a request to change the current video session properties.
   1349          * <p>
   1350          * For example, if the peer requests and upgrade from an audio-only call to a bi-directional
   1351          * video call, could decline the request and keep the call as audio-only.
   1352          * In such a scenario, the {@code responseProfile} would have a video state of
   1353          * {@link VideoProfile#STATE_AUDIO_ONLY}.  If the user had decided to accept the request,
   1354          * the video state would be {@link VideoProfile#STATE_BIDIRECTIONAL}.
   1355          * <p>
   1356          * Sent from the {@link InCallService} via
   1357          * {@link InCallService.VideoCall#sendSessionModifyResponse(VideoProfile)} in response to
   1358          * a {@link InCallService.VideoCall.Callback#onSessionModifyRequestReceived(VideoProfile)}
   1359          * callback.
   1360          *
   1361          * @param responseProfile The response video profile.
   1362          */
   1363         public abstract void onSendSessionModifyResponse(VideoProfile responseProfile);
   1364 
   1365         /**
   1366          * Issues a request to the {@link VideoProvider} to retrieve the camera capabilities.
   1367          * <p>
   1368          * The {@link VideoProvider} should respond by communicating the capabilities of the chosen
   1369          * camera via
   1370          * {@link VideoProvider#changeCameraCapabilities(VideoProfile.CameraCapabilities)}.
   1371          * <p>
   1372          * Sent from the {@link InCallService} via
   1373          * {@link InCallService.VideoCall#requestCameraCapabilities()}.
   1374          */
   1375         public abstract void onRequestCameraCapabilities();
   1376 
   1377         /**
   1378          * Issues a request to the {@link VideoProvider} to retrieve the current data usage for the
   1379          * video component of the current {@link Connection}.
   1380          * <p>
   1381          * The {@link VideoProvider} should respond by communicating current data usage, in bytes,
   1382          * via {@link VideoProvider#setCallDataUsage(long)}.
   1383          * <p>
   1384          * Sent from the {@link InCallService} via
   1385          * {@link InCallService.VideoCall#requestCallDataUsage()}.
   1386          */
   1387         public abstract void onRequestConnectionDataUsage();
   1388 
   1389         /**
   1390          * Provides the {@link VideoProvider} with the {@link Uri} of an image to be displayed to
   1391          * the peer device when the video signal is paused.
   1392          * <p>
   1393          * Sent from the {@link InCallService} via
   1394          * {@link InCallService.VideoCall#setPauseImage(Uri)}.
   1395          *
   1396          * @param uri URI of image to display.
   1397          */
   1398         public abstract void onSetPauseImage(Uri uri);
   1399 
   1400         /**
   1401          * Used to inform listening {@link InCallService} implementations when the
   1402          * {@link VideoProvider} receives a session modification request.
   1403          * <p>
   1404          * Received by the {@link InCallService} via
   1405          * {@link InCallService.VideoCall.Callback#onSessionModifyRequestReceived(VideoProfile)},
   1406          *
   1407          * @param videoProfile The requested video profile.
   1408          * @see #onSendSessionModifyRequest(VideoProfile, VideoProfile)
   1409          */
   1410         public void receiveSessionModifyRequest(VideoProfile videoProfile) {
   1411             if (mVideoCallbacks != null) {
   1412                 for (IVideoCallback callback : mVideoCallbacks.values()) {
   1413                     try {
   1414                         callback.receiveSessionModifyRequest(videoProfile);
   1415                     } catch (RemoteException ignored) {
   1416                         Log.w(this, "receiveSessionModifyRequest callback failed", ignored);
   1417                     }
   1418                 }
   1419             }
   1420         }
   1421 
   1422         /**
   1423          * Used to inform listening {@link InCallService} implementations when the
   1424          * {@link VideoProvider} receives a response to a session modification request.
   1425          * <p>
   1426          * Received by the {@link InCallService} via
   1427          * {@link InCallService.VideoCall.Callback#onSessionModifyResponseReceived(int,
   1428          * VideoProfile, VideoProfile)}.
   1429          *
   1430          * @param status Status of the session modify request.  Valid values are
   1431          *               {@link VideoProvider#SESSION_MODIFY_REQUEST_SUCCESS},
   1432          *               {@link VideoProvider#SESSION_MODIFY_REQUEST_FAIL},
   1433          *               {@link VideoProvider#SESSION_MODIFY_REQUEST_INVALID},
   1434          *               {@link VideoProvider#SESSION_MODIFY_REQUEST_TIMED_OUT},
   1435          *               {@link VideoProvider#SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE}
   1436          * @param requestedProfile The original request which was sent to the peer device.
   1437          * @param responseProfile The actual profile changes agreed to by the peer device.
   1438          * @see #onSendSessionModifyRequest(VideoProfile, VideoProfile)
   1439          */
   1440         public void receiveSessionModifyResponse(int status,
   1441                 VideoProfile requestedProfile, VideoProfile responseProfile) {
   1442             if (mVideoCallbacks != null) {
   1443                 for (IVideoCallback callback : mVideoCallbacks.values()) {
   1444                     try {
   1445                         callback.receiveSessionModifyResponse(status, requestedProfile,
   1446                                 responseProfile);
   1447                     } catch (RemoteException ignored) {
   1448                         Log.w(this, "receiveSessionModifyResponse callback failed", ignored);
   1449                     }
   1450                 }
   1451             }
   1452         }
   1453 
   1454         /**
   1455          * Used to inform listening {@link InCallService} implementations when the
   1456          * {@link VideoProvider} reports a call session event.
   1457          * <p>
   1458          * Received by the {@link InCallService} via
   1459          * {@link InCallService.VideoCall.Callback#onCallSessionEvent(int)}.
   1460          *
   1461          * @param event The event.  Valid values are: {@link VideoProvider#SESSION_EVENT_RX_PAUSE},
   1462          *      {@link VideoProvider#SESSION_EVENT_RX_RESUME},
   1463          *      {@link VideoProvider#SESSION_EVENT_TX_START},
   1464          *      {@link VideoProvider#SESSION_EVENT_TX_STOP},
   1465          *      {@link VideoProvider#SESSION_EVENT_CAMERA_FAILURE},
   1466          *      {@link VideoProvider#SESSION_EVENT_CAMERA_READY},
   1467          *      {@link VideoProvider#SESSION_EVENT_CAMERA_FAILURE}.
   1468          */
   1469         public void handleCallSessionEvent(int event) {
   1470             if (mVideoCallbacks != null) {
   1471                 for (IVideoCallback callback : mVideoCallbacks.values()) {
   1472                     try {
   1473                         callback.handleCallSessionEvent(event);
   1474                     } catch (RemoteException ignored) {
   1475                         Log.w(this, "handleCallSessionEvent callback failed", ignored);
   1476                     }
   1477                 }
   1478             }
   1479         }
   1480 
   1481         /**
   1482          * Used to inform listening {@link InCallService} implementations when the dimensions of the
   1483          * peer's video have changed.
   1484          * <p>
   1485          * This could occur if, for example, the peer rotates their device, changing the aspect
   1486          * ratio of the video, or if the user switches between the back and front cameras.
   1487          * <p>
   1488          * Received by the {@link InCallService} via
   1489          * {@link InCallService.VideoCall.Callback#onPeerDimensionsChanged(int, int)}.
   1490          *
   1491          * @param width  The updated peer video width.
   1492          * @param height The updated peer video height.
   1493          */
   1494         public void changePeerDimensions(int width, int height) {
   1495             if (mVideoCallbacks != null) {
   1496                 for (IVideoCallback callback : mVideoCallbacks.values()) {
   1497                     try {
   1498                         callback.changePeerDimensions(width, height);
   1499                     } catch (RemoteException ignored) {
   1500                         Log.w(this, "changePeerDimensions callback failed", ignored);
   1501                     }
   1502                 }
   1503             }
   1504         }
   1505 
   1506         /**
   1507          * Used to inform listening {@link InCallService} implementations when the data usage of the
   1508          * video associated with the current {@link Connection} has changed.
   1509          * <p>
   1510          * This could be in response to a preview request via
   1511          * {@link #onRequestConnectionDataUsage()}, or as a periodic update by the
   1512          * {@link VideoProvider}.  Where periodic updates of data usage are provided, they should be
   1513          * provided at most for every 1 MB of data transferred and no more than once every 10 sec.
   1514          * <p>
   1515          * Received by the {@link InCallService} via
   1516          * {@link InCallService.VideoCall.Callback#onCallDataUsageChanged(long)}.
   1517          *
   1518          * @param dataUsage The updated data usage (in bytes).  Reported as the cumulative bytes
   1519          *                  used since the start of the call.
   1520          */
   1521         public void setCallDataUsage(long dataUsage) {
   1522             if (mVideoCallbacks != null) {
   1523                 for (IVideoCallback callback : mVideoCallbacks.values()) {
   1524                     try {
   1525                         callback.changeCallDataUsage(dataUsage);
   1526                     } catch (RemoteException ignored) {
   1527                         Log.w(this, "setCallDataUsage callback failed", ignored);
   1528                     }
   1529                 }
   1530             }
   1531         }
   1532 
   1533         /**
   1534          * @see #setCallDataUsage(long)
   1535          *
   1536          * @param dataUsage The updated data usage (in byes).
   1537          * @deprecated - Use {@link #setCallDataUsage(long)} instead.
   1538          * @hide
   1539          */
   1540         public void changeCallDataUsage(long dataUsage) {
   1541             setCallDataUsage(dataUsage);
   1542         }
   1543 
   1544         /**
   1545          * Used to inform listening {@link InCallService} implementations when the capabilities of
   1546          * the current camera have changed.
   1547          * <p>
   1548          * The {@link VideoProvider} should call this in response to
   1549          * {@link VideoProvider#onRequestCameraCapabilities()}, or when the current camera is
   1550          * changed via {@link VideoProvider#onSetCamera(String)}.
   1551          * <p>
   1552          * Received by the {@link InCallService} via
   1553          * {@link InCallService.VideoCall.Callback#onCameraCapabilitiesChanged(
   1554          * VideoProfile.CameraCapabilities)}.
   1555          *
   1556          * @param cameraCapabilities The new camera capabilities.
   1557          */
   1558         public void changeCameraCapabilities(VideoProfile.CameraCapabilities cameraCapabilities) {
   1559             if (mVideoCallbacks != null) {
   1560                 for (IVideoCallback callback : mVideoCallbacks.values()) {
   1561                     try {
   1562                         callback.changeCameraCapabilities(cameraCapabilities);
   1563                     } catch (RemoteException ignored) {
   1564                         Log.w(this, "changeCameraCapabilities callback failed", ignored);
   1565                     }
   1566                 }
   1567             }
   1568         }
   1569 
   1570         /**
   1571          * Used to inform listening {@link InCallService} implementations when the video quality
   1572          * of the call has changed.
   1573          * <p>
   1574          * Received by the {@link InCallService} via
   1575          * {@link InCallService.VideoCall.Callback#onVideoQualityChanged(int)}.
   1576          *
   1577          * @param videoQuality The updated video quality.  Valid values:
   1578          *      {@link VideoProfile#QUALITY_HIGH},
   1579          *      {@link VideoProfile#QUALITY_MEDIUM},
   1580          *      {@link VideoProfile#QUALITY_LOW},
   1581          *      {@link VideoProfile#QUALITY_DEFAULT}.
   1582          */
   1583         public void changeVideoQuality(int videoQuality) {
   1584             if (mVideoCallbacks != null) {
   1585                 for (IVideoCallback callback : mVideoCallbacks.values()) {
   1586                     try {
   1587                         callback.changeVideoQuality(videoQuality);
   1588                     } catch (RemoteException ignored) {
   1589                         Log.w(this, "changeVideoQuality callback failed", ignored);
   1590                     }
   1591                 }
   1592             }
   1593         }
   1594 
   1595         /**
   1596          * Returns a string representation of a call session event.
   1597          *
   1598          * @param event A call session event passed to {@link #handleCallSessionEvent(int)}.
   1599          * @return String representation of the call session event.
   1600          * @hide
   1601          */
   1602         public static String sessionEventToString(int event) {
   1603             switch (event) {
   1604                 case SESSION_EVENT_CAMERA_FAILURE:
   1605                     return SESSION_EVENT_CAMERA_FAILURE_STR;
   1606                 case SESSION_EVENT_CAMERA_READY:
   1607                     return SESSION_EVENT_CAMERA_READY_STR;
   1608                 case SESSION_EVENT_RX_PAUSE:
   1609                     return SESSION_EVENT_RX_PAUSE_STR;
   1610                 case SESSION_EVENT_RX_RESUME:
   1611                     return SESSION_EVENT_RX_RESUME_STR;
   1612                 case SESSION_EVENT_TX_START:
   1613                     return SESSION_EVENT_TX_START_STR;
   1614                 case SESSION_EVENT_TX_STOP:
   1615                     return SESSION_EVENT_TX_STOP_STR;
   1616                 case SESSION_EVENT_CAMERA_PERMISSION_ERROR:
   1617                     return SESSION_EVENT_CAMERA_PERMISSION_ERROR_STR;
   1618                 default:
   1619                     return SESSION_EVENT_UNKNOWN_STR + " " + event;
   1620             }
   1621         }
   1622     }
   1623 
   1624     private final Listener mConnectionDeathListener = new Listener() {
   1625         @Override
   1626         public void onDestroyed(Connection c) {
   1627             if (mConferenceables.remove(c)) {
   1628                 fireOnConferenceableConnectionsChanged();
   1629             }
   1630         }
   1631     };
   1632 
   1633     private final Conference.Listener mConferenceDeathListener = new Conference.Listener() {
   1634         @Override
   1635         public void onDestroyed(Conference c) {
   1636             if (mConferenceables.remove(c)) {
   1637                 fireOnConferenceableConnectionsChanged();
   1638             }
   1639         }
   1640     };
   1641 
   1642     /**
   1643      * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
   1644      * load factor before resizing, 1 means we only expect a single thread to
   1645      * access the map so make only a single shard
   1646      */
   1647     private final Set<Listener> mListeners = Collections.newSetFromMap(
   1648             new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
   1649     private final List<Conferenceable> mConferenceables = new ArrayList<>();
   1650     private final List<Conferenceable> mUnmodifiableConferenceables =
   1651             Collections.unmodifiableList(mConferenceables);
   1652 
   1653     // The internal telecom call ID associated with this connection.
   1654     private String mTelecomCallId;
   1655     private int mState = STATE_NEW;
   1656     private CallAudioState mCallAudioState;
   1657     private Uri mAddress;
   1658     private int mAddressPresentation;
   1659     private String mCallerDisplayName;
   1660     private int mCallerDisplayNamePresentation;
   1661     private boolean mRingbackRequested = false;
   1662     private int mConnectionCapabilities;
   1663     private int mConnectionProperties;
   1664     private int mSupportedAudioRoutes = CallAudioState.ROUTE_ALL;
   1665     private VideoProvider mVideoProvider;
   1666     private boolean mAudioModeIsVoip;
   1667     private long mConnectTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED;
   1668     private StatusHints mStatusHints;
   1669     private int mVideoState;
   1670     private DisconnectCause mDisconnectCause;
   1671     private Conference mConference;
   1672     private ConnectionService mConnectionService;
   1673     private Bundle mExtras;
   1674     private final Object mExtrasLock = new Object();
   1675 
   1676     /**
   1677      * Tracks the key set for the extras bundle provided on the last invocation of
   1678      * {@link #setExtras(Bundle)}.  Used so that on subsequent invocations we can remove any extras
   1679      * keys which were set previously but are no longer present in the replacement Bundle.
   1680      */
   1681     private Set<String> mPreviousExtraKeys;
   1682 
   1683     /**
   1684      * Create a new Connection.
   1685      */
   1686     public Connection() {}
   1687 
   1688     /**
   1689      * Returns the Telecom internal call ID associated with this connection.  Should only be used
   1690      * for debugging and tracing purposes.
   1691      *
   1692      * @return The Telecom call ID.
   1693      * @hide
   1694      */
   1695     public final String getTelecomCallId() {
   1696         return mTelecomCallId;
   1697     }
   1698 
   1699     /**
   1700      * @return The address (e.g., phone number) to which this Connection is currently communicating.
   1701      */
   1702     public final Uri getAddress() {
   1703         return mAddress;
   1704     }
   1705 
   1706     /**
   1707      * @return The presentation requirements for the address.
   1708      *         See {@link TelecomManager} for valid values.
   1709      */
   1710     public final int getAddressPresentation() {
   1711         return mAddressPresentation;
   1712     }
   1713 
   1714     /**
   1715      * @return The caller display name (CNAP).
   1716      */
   1717     public final String getCallerDisplayName() {
   1718         return mCallerDisplayName;
   1719     }
   1720 
   1721     /**
   1722      * @return The presentation requirements for the handle.
   1723      *         See {@link TelecomManager} for valid values.
   1724      */
   1725     public final int getCallerDisplayNamePresentation() {
   1726         return mCallerDisplayNamePresentation;
   1727     }
   1728 
   1729     /**
   1730      * @return The state of this Connection.
   1731      */
   1732     public final int getState() {
   1733         return mState;
   1734     }
   1735 
   1736     /**
   1737      * Returns the video state of the connection.
   1738      * Valid values: {@link VideoProfile#STATE_AUDIO_ONLY},
   1739      * {@link VideoProfile#STATE_BIDIRECTIONAL},
   1740      * {@link VideoProfile#STATE_TX_ENABLED},
   1741      * {@link VideoProfile#STATE_RX_ENABLED}.
   1742      *
   1743      * @return The video state of the connection.
   1744      * @hide
   1745      */
   1746     public final int getVideoState() {
   1747         return mVideoState;
   1748     }
   1749 
   1750     /**
   1751      * @return The audio state of the connection, describing how its audio is currently
   1752      *         being routed by the system. This is {@code null} if this Connection
   1753      *         does not directly know about its audio state.
   1754      * @deprecated Use {@link #getCallAudioState()} instead.
   1755      * @hide
   1756      */
   1757     @SystemApi
   1758     @Deprecated
   1759     public final AudioState getAudioState() {
   1760         if (mCallAudioState == null) {
   1761           return null;
   1762         }
   1763         return new AudioState(mCallAudioState);
   1764     }
   1765 
   1766     /**
   1767      * @return The audio state of the connection, describing how its audio is currently
   1768      *         being routed by the system. This is {@code null} if this Connection
   1769      *         does not directly know about its audio state.
   1770      */
   1771     public final CallAudioState getCallAudioState() {
   1772         return mCallAudioState;
   1773     }
   1774 
   1775     /**
   1776      * @return The conference that this connection is a part of.  Null if it is not part of any
   1777      *         conference.
   1778      */
   1779     public final Conference getConference() {
   1780         return mConference;
   1781     }
   1782 
   1783     /**
   1784      * Returns whether this connection is requesting that the system play a ringback tone
   1785      * on its behalf.
   1786      */
   1787     public final boolean isRingbackRequested() {
   1788         return mRingbackRequested;
   1789     }
   1790 
   1791     /**
   1792      * @return True if the connection's audio mode is VOIP.
   1793      */
   1794     public final boolean getAudioModeIsVoip() {
   1795         return mAudioModeIsVoip;
   1796     }
   1797 
   1798     /**
   1799      * Retrieves the connection start time of the {@code Connnection}, if specified.  A value of
   1800      * {@link Conference#CONNECT_TIME_NOT_SPECIFIED} indicates that Telecom should determine the
   1801      * start time of the conference.
   1802      *
   1803      * @return The time at which the {@code Connnection} was connected.
   1804      *
   1805      * @hide
   1806      */
   1807     public final long getConnectTimeMillis() {
   1808         return mConnectTimeMillis;
   1809     }
   1810 
   1811     /**
   1812      * @return The status hints for this connection.
   1813      */
   1814     public final StatusHints getStatusHints() {
   1815         return mStatusHints;
   1816     }
   1817 
   1818     /**
   1819      * Returns the extras associated with this connection.
   1820      * <p>
   1821      * Extras should be updated using {@link #putExtras(Bundle)}.
   1822      * <p>
   1823      * Telecom or an {@link InCallService} can also update the extras via
   1824      * {@link android.telecom.Call#putExtras(Bundle)}, and
   1825      * {@link Call#removeExtras(List)}.
   1826      * <p>
   1827      * The connection is notified of changes to the extras made by Telecom or an
   1828      * {@link InCallService} by {@link #onExtrasChanged(Bundle)}.
   1829      *
   1830      * @return The extras associated with this connection.
   1831      */
   1832     public final Bundle getExtras() {
   1833         Bundle extras = null;
   1834         synchronized (mExtrasLock) {
   1835             if (mExtras != null) {
   1836                 extras = new Bundle(mExtras);
   1837             }
   1838         }
   1839         return extras;
   1840     }
   1841 
   1842     /**
   1843      * Assign a listener to be notified of state changes.
   1844      *
   1845      * @param l A listener.
   1846      * @return This Connection.
   1847      *
   1848      * @hide
   1849      */
   1850     public final Connection addConnectionListener(Listener l) {
   1851         mListeners.add(l);
   1852         return this;
   1853     }
   1854 
   1855     /**
   1856      * Remove a previously assigned listener that was being notified of state changes.
   1857      *
   1858      * @param l A Listener.
   1859      * @return This Connection.
   1860      *
   1861      * @hide
   1862      */
   1863     public final Connection removeConnectionListener(Listener l) {
   1864         if (l != null) {
   1865             mListeners.remove(l);
   1866         }
   1867         return this;
   1868     }
   1869 
   1870     /**
   1871      * @return The {@link DisconnectCause} for this connection.
   1872      */
   1873     public final DisconnectCause getDisconnectCause() {
   1874         return mDisconnectCause;
   1875     }
   1876 
   1877     /**
   1878      * Sets the telecom call ID associated with this Connection.  The Telecom Call ID should be used
   1879      * ONLY for debugging purposes.
   1880      *
   1881      * @param callId The telecom call ID.
   1882      * @hide
   1883      */
   1884     public void setTelecomCallId(String callId) {
   1885         mTelecomCallId = callId;
   1886     }
   1887 
   1888     /**
   1889      * Inform this Connection that the state of its audio output has been changed externally.
   1890      *
   1891      * @param state The new audio state.
   1892      * @hide
   1893      */
   1894     final void setCallAudioState(CallAudioState state) {
   1895         checkImmutable();
   1896         Log.d(this, "setAudioState %s", state);
   1897         mCallAudioState = state;
   1898         onAudioStateChanged(getAudioState());
   1899         onCallAudioStateChanged(state);
   1900     }
   1901 
   1902     /**
   1903      * @param state An integer value of a {@code STATE_*} constant.
   1904      * @return A string representation of the value.
   1905      */
   1906     public static String stateToString(int state) {
   1907         switch (state) {
   1908             case STATE_INITIALIZING:
   1909                 return "INITIALIZING";
   1910             case STATE_NEW:
   1911                 return "NEW";
   1912             case STATE_RINGING:
   1913                 return "RINGING";
   1914             case STATE_DIALING:
   1915                 return "DIALING";
   1916             case STATE_PULLING_CALL:
   1917                 return "PULLING_CALL";
   1918             case STATE_ACTIVE:
   1919                 return "ACTIVE";
   1920             case STATE_HOLDING:
   1921                 return "HOLDING";
   1922             case STATE_DISCONNECTED:
   1923                 return "DISCONNECTED";
   1924             default:
   1925                 Log.wtf(Connection.class, "Unknown state %d", state);
   1926                 return "UNKNOWN";
   1927         }
   1928     }
   1929 
   1930     /**
   1931      * Returns the connection's capabilities, as a bit mask of the {@code CAPABILITY_*} constants.
   1932      */
   1933     public final int getConnectionCapabilities() {
   1934         return mConnectionCapabilities;
   1935     }
   1936 
   1937     /**
   1938      * Returns the connection's properties, as a bit mask of the {@code PROPERTY_*} constants.
   1939      */
   1940     public final int getConnectionProperties() {
   1941         return mConnectionProperties;
   1942     }
   1943 
   1944     /**
   1945      * Returns the connection's supported audio routes.
   1946      *
   1947      * @hide
   1948      */
   1949     public final int getSupportedAudioRoutes() {
   1950         return mSupportedAudioRoutes;
   1951     }
   1952 
   1953     /**
   1954      * Sets the value of the {@link #getAddress()} property.
   1955      *
   1956      * @param address The new address.
   1957      * @param presentation The presentation requirements for the address.
   1958      *        See {@link TelecomManager} for valid values.
   1959      */
   1960     public final void setAddress(Uri address, int presentation) {
   1961         checkImmutable();
   1962         Log.d(this, "setAddress %s", address);
   1963         mAddress = address;
   1964         mAddressPresentation = presentation;
   1965         for (Listener l : mListeners) {
   1966             l.onAddressChanged(this, address, presentation);
   1967         }
   1968     }
   1969 
   1970     /**
   1971      * Sets the caller display name (CNAP).
   1972      *
   1973      * @param callerDisplayName The new display name.
   1974      * @param presentation The presentation requirements for the handle.
   1975      *        See {@link TelecomManager} for valid values.
   1976      */
   1977     public final void setCallerDisplayName(String callerDisplayName, int presentation) {
   1978         checkImmutable();
   1979         Log.d(this, "setCallerDisplayName %s", callerDisplayName);
   1980         mCallerDisplayName = callerDisplayName;
   1981         mCallerDisplayNamePresentation = presentation;
   1982         for (Listener l : mListeners) {
   1983             l.onCallerDisplayNameChanged(this, callerDisplayName, presentation);
   1984         }
   1985     }
   1986 
   1987     /**
   1988      * Set the video state for the connection.
   1989      * Valid values: {@link VideoProfile#STATE_AUDIO_ONLY},
   1990      * {@link VideoProfile#STATE_BIDIRECTIONAL},
   1991      * {@link VideoProfile#STATE_TX_ENABLED},
   1992      * {@link VideoProfile#STATE_RX_ENABLED}.
   1993      *
   1994      * @param videoState The new video state.
   1995      */
   1996     public final void setVideoState(int videoState) {
   1997         checkImmutable();
   1998         Log.d(this, "setVideoState %d", videoState);
   1999         mVideoState = videoState;
   2000         for (Listener l : mListeners) {
   2001             l.onVideoStateChanged(this, mVideoState);
   2002         }
   2003     }
   2004 
   2005     /**
   2006      * Sets state to active (e.g., an ongoing connection where two or more parties can actively
   2007      * communicate).
   2008      */
   2009     public final void setActive() {
   2010         checkImmutable();
   2011         setRingbackRequested(false);
   2012         setState(STATE_ACTIVE);
   2013     }
   2014 
   2015     /**
   2016      * Sets state to ringing (e.g., an inbound ringing connection).
   2017      */
   2018     public final void setRinging() {
   2019         checkImmutable();
   2020         setState(STATE_RINGING);
   2021     }
   2022 
   2023     /**
   2024      * Sets state to initializing (this Connection is not yet ready to be used).
   2025      */
   2026     public final void setInitializing() {
   2027         checkImmutable();
   2028         setState(STATE_INITIALIZING);
   2029     }
   2030 
   2031     /**
   2032      * Sets state to initialized (the Connection has been set up and is now ready to be used).
   2033      */
   2034     public final void setInitialized() {
   2035         checkImmutable();
   2036         setState(STATE_NEW);
   2037     }
   2038 
   2039     /**
   2040      * Sets state to dialing (e.g., dialing an outbound connection).
   2041      */
   2042     public final void setDialing() {
   2043         checkImmutable();
   2044         setState(STATE_DIALING);
   2045     }
   2046 
   2047     /**
   2048      * Sets state to pulling (e.g. the connection is being pulled to the local device from another
   2049      * device).  Only applicable for {@link Connection}s with
   2050      * {@link Connection#PROPERTY_IS_EXTERNAL_CALL} and {@link Connection#CAPABILITY_CAN_PULL_CALL}.
   2051      */
   2052     public final void setPulling() {
   2053         checkImmutable();
   2054         setState(STATE_PULLING_CALL);
   2055     }
   2056 
   2057     /**
   2058      * Sets state to be on hold.
   2059      */
   2060     public final void setOnHold() {
   2061         checkImmutable();
   2062         setState(STATE_HOLDING);
   2063     }
   2064 
   2065     /**
   2066      * Sets the video connection provider.
   2067      * @param videoProvider The video provider.
   2068      */
   2069     public final void setVideoProvider(VideoProvider videoProvider) {
   2070         checkImmutable();
   2071         mVideoProvider = videoProvider;
   2072         for (Listener l : mListeners) {
   2073             l.onVideoProviderChanged(this, videoProvider);
   2074         }
   2075     }
   2076 
   2077     public final VideoProvider getVideoProvider() {
   2078         return mVideoProvider;
   2079     }
   2080 
   2081     /**
   2082      * Sets state to disconnected.
   2083      *
   2084      * @param disconnectCause The reason for the disconnection, as specified by
   2085      *         {@link DisconnectCause}.
   2086      */
   2087     public final void setDisconnected(DisconnectCause disconnectCause) {
   2088         checkImmutable();
   2089         mDisconnectCause = disconnectCause;
   2090         setState(STATE_DISCONNECTED);
   2091         Log.d(this, "Disconnected with cause %s", disconnectCause);
   2092         for (Listener l : mListeners) {
   2093             l.onDisconnected(this, disconnectCause);
   2094         }
   2095     }
   2096 
   2097     /**
   2098      * Informs listeners that this {@code Connection} is in a post-dial wait state. This is done
   2099      * when (a) the {@code Connection} is issuing a DTMF sequence; (b) it has encountered a "wait"
   2100      * character; and (c) it wishes to inform the In-Call app that it is waiting for the end-user
   2101      * to send an {@link #onPostDialContinue(boolean)} signal.
   2102      *
   2103      * @param remaining The DTMF character sequence remaining to be emitted once the
   2104      *         {@link #onPostDialContinue(boolean)} is received, including any "wait" characters
   2105      *         that remaining sequence may contain.
   2106      */
   2107     public final void setPostDialWait(String remaining) {
   2108         checkImmutable();
   2109         for (Listener l : mListeners) {
   2110             l.onPostDialWait(this, remaining);
   2111         }
   2112     }
   2113 
   2114     /**
   2115      * Informs listeners that this {@code Connection} has processed a character in the post-dial
   2116      * started state. This is done when (a) the {@code Connection} is issuing a DTMF sequence;
   2117      * and (b) it wishes to signal Telecom to play the corresponding DTMF tone locally.
   2118      *
   2119      * @param nextChar The DTMF character that was just processed by the {@code Connection}.
   2120      */
   2121     public final void setNextPostDialChar(char nextChar) {
   2122         checkImmutable();
   2123         for (Listener l : mListeners) {
   2124             l.onPostDialChar(this, nextChar);
   2125         }
   2126     }
   2127 
   2128     /**
   2129      * Requests that the framework play a ringback tone. This is to be invoked by implementations
   2130      * that do not play a ringback tone themselves in the connection's audio stream.
   2131      *
   2132      * @param ringback Whether the ringback tone is to be played.
   2133      */
   2134     public final void setRingbackRequested(boolean ringback) {
   2135         checkImmutable();
   2136         if (mRingbackRequested != ringback) {
   2137             mRingbackRequested = ringback;
   2138             for (Listener l : mListeners) {
   2139                 l.onRingbackRequested(this, ringback);
   2140             }
   2141         }
   2142     }
   2143 
   2144     /**
   2145      * Sets the connection's capabilities as a bit mask of the {@code CAPABILITY_*} constants.
   2146      *
   2147      * @param connectionCapabilities The new connection capabilities.
   2148      */
   2149     public final void setConnectionCapabilities(int connectionCapabilities) {
   2150         checkImmutable();
   2151         if (mConnectionCapabilities != connectionCapabilities) {
   2152             mConnectionCapabilities = connectionCapabilities;
   2153             for (Listener l : mListeners) {
   2154                 l.onConnectionCapabilitiesChanged(this, mConnectionCapabilities);
   2155             }
   2156         }
   2157     }
   2158 
   2159     /**
   2160      * Sets the connection's properties as a bit mask of the {@code PROPERTY_*} constants.
   2161      *
   2162      * @param connectionProperties The new connection properties.
   2163      */
   2164     public final void setConnectionProperties(int connectionProperties) {
   2165         checkImmutable();
   2166         if (mConnectionProperties != connectionProperties) {
   2167             mConnectionProperties = connectionProperties;
   2168             for (Listener l : mListeners) {
   2169                 l.onConnectionPropertiesChanged(this, mConnectionProperties);
   2170             }
   2171         }
   2172     }
   2173 
   2174     /**
   2175      * Sets the supported audio routes.
   2176      *
   2177      * @param supportedAudioRoutes the supported audio routes as a bitmask.
   2178      *                             See {@link CallAudioState}
   2179      * @hide
   2180      */
   2181     public final void setSupportedAudioRoutes(int supportedAudioRoutes) {
   2182         if ((supportedAudioRoutes
   2183                 & (CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER)) == 0) {
   2184             throw new IllegalArgumentException(
   2185                     "supported audio routes must include either speaker or earpiece");
   2186         }
   2187 
   2188         if (mSupportedAudioRoutes != supportedAudioRoutes) {
   2189             mSupportedAudioRoutes = supportedAudioRoutes;
   2190             for (Listener l : mListeners) {
   2191                 l.onSupportedAudioRoutesChanged(this, mSupportedAudioRoutes);
   2192             }
   2193         }
   2194     }
   2195 
   2196     /**
   2197      * Tears down the Connection object.
   2198      */
   2199     public final void destroy() {
   2200         for (Listener l : mListeners) {
   2201             l.onDestroyed(this);
   2202         }
   2203     }
   2204 
   2205     /**
   2206      * Requests that the framework use VOIP audio mode for this connection.
   2207      *
   2208      * @param isVoip True if the audio mode is VOIP.
   2209      */
   2210     public final void setAudioModeIsVoip(boolean isVoip) {
   2211         checkImmutable();
   2212         mAudioModeIsVoip = isVoip;
   2213         for (Listener l : mListeners) {
   2214             l.onAudioModeIsVoipChanged(this, isVoip);
   2215         }
   2216     }
   2217 
   2218     /**
   2219      * Sets the time at which a call became active on this Connection. This is set only
   2220      * when a conference call becomes active on this connection.
   2221      *
   2222      * @param connectionTimeMillis The connection time, in milliseconds.
   2223      *
   2224      * @hide
   2225      */
   2226     public final void setConnectTimeMillis(long connectTimeMillis) {
   2227         mConnectTimeMillis = connectTimeMillis;
   2228     }
   2229 
   2230     /**
   2231      * Sets the label and icon status to display in the in-call UI.
   2232      *
   2233      * @param statusHints The status label and icon to set.
   2234      */
   2235     public final void setStatusHints(StatusHints statusHints) {
   2236         checkImmutable();
   2237         mStatusHints = statusHints;
   2238         for (Listener l : mListeners) {
   2239             l.onStatusHintsChanged(this, statusHints);
   2240         }
   2241     }
   2242 
   2243     /**
   2244      * Sets the connections with which this connection can be conferenced.
   2245      *
   2246      * @param conferenceableConnections The set of connections this connection can conference with.
   2247      */
   2248     public final void setConferenceableConnections(List<Connection> conferenceableConnections) {
   2249         checkImmutable();
   2250         clearConferenceableList();
   2251         for (Connection c : conferenceableConnections) {
   2252             // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a
   2253             // small amount of items here.
   2254             if (!mConferenceables.contains(c)) {
   2255                 c.addConnectionListener(mConnectionDeathListener);
   2256                 mConferenceables.add(c);
   2257             }
   2258         }
   2259         fireOnConferenceableConnectionsChanged();
   2260     }
   2261 
   2262     /**
   2263      * Similar to {@link #setConferenceableConnections(java.util.List)}, sets a list of connections
   2264      * or conferences with which this connection can be conferenced.
   2265      *
   2266      * @param conferenceables The conferenceables.
   2267      */
   2268     public final void setConferenceables(List<Conferenceable> conferenceables) {
   2269         clearConferenceableList();
   2270         for (Conferenceable c : conferenceables) {
   2271             // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a
   2272             // small amount of items here.
   2273             if (!mConferenceables.contains(c)) {
   2274                 if (c instanceof Connection) {
   2275                     Connection connection = (Connection) c;
   2276                     connection.addConnectionListener(mConnectionDeathListener);
   2277                 } else if (c instanceof Conference) {
   2278                     Conference conference = (Conference) c;
   2279                     conference.addListener(mConferenceDeathListener);
   2280                 }
   2281                 mConferenceables.add(c);
   2282             }
   2283         }
   2284         fireOnConferenceableConnectionsChanged();
   2285     }
   2286 
   2287     /**
   2288      * Returns the connections or conferences with which this connection can be conferenced.
   2289      */
   2290     public final List<Conferenceable> getConferenceables() {
   2291         return mUnmodifiableConferenceables;
   2292     }
   2293 
   2294     /**
   2295      * @hide
   2296      */
   2297     public final void setConnectionService(ConnectionService connectionService) {
   2298         checkImmutable();
   2299         if (mConnectionService != null) {
   2300             Log.e(this, new Exception(), "Trying to set ConnectionService on a connection " +
   2301                     "which is already associated with another ConnectionService.");
   2302         } else {
   2303             mConnectionService = connectionService;
   2304         }
   2305     }
   2306 
   2307     /**
   2308      * @hide
   2309      */
   2310     public final void unsetConnectionService(ConnectionService connectionService) {
   2311         if (mConnectionService != connectionService) {
   2312             Log.e(this, new Exception(), "Trying to remove ConnectionService from a Connection " +
   2313                     "that does not belong to the ConnectionService.");
   2314         } else {
   2315             mConnectionService = null;
   2316         }
   2317     }
   2318 
   2319     /**
   2320      * @hide
   2321      */
   2322     public final ConnectionService getConnectionService() {
   2323         return mConnectionService;
   2324     }
   2325 
   2326     /**
   2327      * Sets the conference that this connection is a part of. This will fail if the connection is
   2328      * already part of a conference. {@link #resetConference} to un-set the conference first.
   2329      *
   2330      * @param conference The conference.
   2331      * @return {@code true} if the conference was successfully set.
   2332      * @hide
   2333      */
   2334     public final boolean setConference(Conference conference) {
   2335         checkImmutable();
   2336         // We check to see if it is already part of another conference.
   2337         if (mConference == null) {
   2338             mConference = conference;
   2339             if (mConnectionService != null && mConnectionService.containsConference(conference)) {
   2340                 fireConferenceChanged();
   2341             }
   2342             return true;
   2343         }
   2344         return false;
   2345     }
   2346 
   2347     /**
   2348      * Resets the conference that this connection is a part of.
   2349      * @hide
   2350      */
   2351     public final void resetConference() {
   2352         if (mConference != null) {
   2353             Log.d(this, "Conference reset");
   2354             mConference = null;
   2355             fireConferenceChanged();
   2356         }
   2357     }
   2358 
   2359     /**
   2360      * Set some extras that can be associated with this {@code Connection}.
   2361      * <p>
   2362      * New or existing keys are replaced in the {@code Connection} extras.  Keys which are no longer
   2363      * in the new extras, but were present the last time {@code setExtras} was called are removed.
   2364      * <p>
   2365      * Alternatively you may use the {@link #putExtras(Bundle)}, and
   2366      * {@link #removeExtras(String...)} methods to modify the extras.
   2367      * <p>
   2368      * No assumptions should be made as to how an In-Call UI or service will handle these extras.
   2369      * Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
   2370      *
   2371      * @param extras The extras associated with this {@code Connection}.
   2372      */
   2373     public final void setExtras(@Nullable Bundle extras) {
   2374         checkImmutable();
   2375 
   2376         // Add/replace any new or changed extras values.
   2377         putExtras(extras);
   2378 
   2379         // If we have used "setExtras" in the past, compare the key set from the last invocation to
   2380         // the current one and remove any keys that went away.
   2381         if (mPreviousExtraKeys != null) {
   2382             List<String> toRemove = new ArrayList<String>();
   2383             for (String oldKey : mPreviousExtraKeys) {
   2384                 if (extras == null || !extras.containsKey(oldKey)) {
   2385                     toRemove.add(oldKey);
   2386                 }
   2387             }
   2388             if (!toRemove.isEmpty()) {
   2389                 removeExtras(toRemove);
   2390             }
   2391         }
   2392 
   2393         // Track the keys the last time set called setExtras.  This way, the next time setExtras is
   2394         // called we can see if the caller has removed any extras values.
   2395         if (mPreviousExtraKeys == null) {
   2396             mPreviousExtraKeys = new ArraySet<String>();
   2397         }
   2398         mPreviousExtraKeys.clear();
   2399         if (extras != null) {
   2400             mPreviousExtraKeys.addAll(extras.keySet());
   2401         }
   2402     }
   2403 
   2404     /**
   2405      * Adds some extras to this {@code Connection}.  Existing keys are replaced and new ones are
   2406      * added.
   2407      * <p>
   2408      * No assumptions should be made as to how an In-Call UI or service will handle these extras.
   2409      * Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
   2410      *
   2411      * @param extras The extras to add.
   2412      */
   2413     public final void putExtras(@NonNull Bundle extras) {
   2414         checkImmutable();
   2415         if (extras == null) {
   2416             return;
   2417         }
   2418         // Creating a duplicate bundle so we don't have to synchronize on mExtrasLock while calling
   2419         // the listeners.
   2420         Bundle listenerExtras;
   2421         synchronized (mExtrasLock) {
   2422             if (mExtras == null) {
   2423                 mExtras = new Bundle();
   2424             }
   2425             mExtras.putAll(extras);
   2426             listenerExtras = new Bundle(mExtras);
   2427         }
   2428         for (Listener l : mListeners) {
   2429             // Create a new clone of the extras for each listener so that they don't clobber
   2430             // each other
   2431             l.onExtrasChanged(this, new Bundle(listenerExtras));
   2432         }
   2433     }
   2434 
   2435     /**
   2436      * Adds a boolean extra to this {@code Connection}.
   2437      *
   2438      * @param key The extra key.
   2439      * @param value The value.
   2440      * @hide
   2441      */
   2442     public final void putExtra(String key, boolean value) {
   2443         Bundle newExtras = new Bundle();
   2444         newExtras.putBoolean(key, value);
   2445         putExtras(newExtras);
   2446     }
   2447 
   2448     /**
   2449      * Adds an integer extra to this {@code Connection}.
   2450      *
   2451      * @param key The extra key.
   2452      * @param value The value.
   2453      * @hide
   2454      */
   2455     public final void putExtra(String key, int value) {
   2456         Bundle newExtras = new Bundle();
   2457         newExtras.putInt(key, value);
   2458         putExtras(newExtras);
   2459     }
   2460 
   2461     /**
   2462      * Adds a string extra to this {@code Connection}.
   2463      *
   2464      * @param key The extra key.
   2465      * @param value The value.
   2466      * @hide
   2467      */
   2468     public final void putExtra(String key, String value) {
   2469         Bundle newExtras = new Bundle();
   2470         newExtras.putString(key, value);
   2471         putExtras(newExtras);
   2472     }
   2473 
   2474     /**
   2475      * Removes extras from this {@code Connection}.
   2476      *
   2477      * @param keys The keys of the extras to remove.
   2478      */
   2479     public final void removeExtras(List<String> keys) {
   2480         synchronized (mExtrasLock) {
   2481             if (mExtras != null) {
   2482                 for (String key : keys) {
   2483                     mExtras.remove(key);
   2484                 }
   2485             }
   2486         }
   2487         List<String> unmodifiableKeys = Collections.unmodifiableList(keys);
   2488         for (Listener l : mListeners) {
   2489             l.onExtrasRemoved(this, unmodifiableKeys);
   2490         }
   2491     }
   2492 
   2493     /**
   2494      * Removes extras from this {@code Connection}.
   2495      *
   2496      * @param keys The keys of the extras to remove.
   2497      */
   2498     public final void removeExtras(String ... keys) {
   2499         removeExtras(Arrays.asList(keys));
   2500     }
   2501 
   2502     /**
   2503      * Sets the audio route (speaker, bluetooth, etc...).  When this request is honored, there will
   2504      * be change to the {@link #getCallAudioState()}.
   2505      * <p>
   2506      * Used by self-managed {@link ConnectionService}s which wish to change the audio route for a
   2507      * self-managed {@link Connection} (see {@link PhoneAccount#CAPABILITY_SELF_MANAGED}.)
   2508      * <p>
   2509      * See also {@link InCallService#setAudioRoute(int)}.
   2510      *
   2511      * @param route The audio route to use (one of {@link CallAudioState#ROUTE_BLUETOOTH},
   2512      *              {@link CallAudioState#ROUTE_EARPIECE}, {@link CallAudioState#ROUTE_SPEAKER}, or
   2513      *              {@link CallAudioState#ROUTE_WIRED_HEADSET}).
   2514      */
   2515     public final void setAudioRoute(int route) {
   2516         for (Listener l : mListeners) {
   2517             l.onAudioRouteChanged(this, route);
   2518         }
   2519     }
   2520 
   2521     /**
   2522      * Informs listeners that a previously requested RTT session via
   2523      * {@link ConnectionRequest#isRequestingRtt()} or
   2524      * {@link #onStartRtt(ParcelFileDescriptor, ParcelFileDescriptor)} has succeeded.
   2525      * @hide
   2526      */
   2527     public final void sendRttInitiationSuccess() {
   2528         mListeners.forEach((l) -> l.onRttInitiationSuccess(Connection.this));
   2529     }
   2530 
   2531     /**
   2532      * Informs listeners that a previously requested RTT session via
   2533      * {@link ConnectionRequest#isRequestingRtt()} or
   2534      * {@link #onStartRtt(ParcelFileDescriptor, ParcelFileDescriptor)}
   2535      * has failed.
   2536      * @param reason One of the reason codes defined in {@link RttModifyStatus}, with the
   2537      *               exception of {@link RttModifyStatus#SESSION_MODIFY_REQUEST_SUCCESS}.
   2538      * @hide
   2539      */
   2540     public final void sendRttInitiationFailure(int reason) {
   2541         mListeners.forEach((l) -> l.onRttInitiationFailure(Connection.this, reason));
   2542     }
   2543 
   2544     /**
   2545      * Informs listeners that a currently active RTT session has been terminated by the remote
   2546      * side of the coll.
   2547      * @hide
   2548      */
   2549     public final void sendRttSessionRemotelyTerminated() {
   2550         mListeners.forEach((l) -> l.onRttSessionRemotelyTerminated(Connection.this));
   2551     }
   2552 
   2553     /**
   2554      * Informs listeners that the remote side of the call has requested an upgrade to include an
   2555      * RTT session in the call.
   2556      * @hide
   2557      */
   2558     public final void sendRemoteRttRequest() {
   2559         mListeners.forEach((l) -> l.onRemoteRttRequest(Connection.this));
   2560     }
   2561 
   2562     /**
   2563      * Notifies this Connection that the {@link #getAudioState()} property has a new value.
   2564      *
   2565      * @param state The new connection audio state.
   2566      * @deprecated Use {@link #onCallAudioStateChanged(CallAudioState)} instead.
   2567      * @hide
   2568      */
   2569     @SystemApi
   2570     @Deprecated
   2571     public void onAudioStateChanged(AudioState state) {}
   2572 
   2573     /**
   2574      * Notifies this Connection that the {@link #getCallAudioState()} property has a new value.
   2575      *
   2576      * @param state The new connection audio state.
   2577      */
   2578     public void onCallAudioStateChanged(CallAudioState state) {}
   2579 
   2580     /**
   2581      * Notifies this Connection of an internal state change. This method is called after the
   2582      * state is changed.
   2583      *
   2584      * @param state The new state, one of the {@code STATE_*} constants.
   2585      */
   2586     public void onStateChanged(int state) {}
   2587 
   2588     /**
   2589      * Notifies this Connection of a request to play a DTMF tone.
   2590      *
   2591      * @param c A DTMF character.
   2592      */
   2593     public void onPlayDtmfTone(char c) {}
   2594 
   2595     /**
   2596      * Notifies this Connection of a request to stop any currently playing DTMF tones.
   2597      */
   2598     public void onStopDtmfTone() {}
   2599 
   2600     /**
   2601      * Notifies this Connection of a request to disconnect.
   2602      */
   2603     public void onDisconnect() {}
   2604 
   2605     /**
   2606      * Notifies this Connection of a request to disconnect a participant of the conference managed
   2607      * by the connection.
   2608      *
   2609      * @param endpoint the {@link Uri} of the participant to disconnect.
   2610      * @hide
   2611      */
   2612     public void onDisconnectConferenceParticipant(Uri endpoint) {}
   2613 
   2614     /**
   2615      * Notifies this Connection of a request to separate from its parent conference.
   2616      */
   2617     public void onSeparate() {}
   2618 
   2619     /**
   2620      * Notifies this Connection of a request to abort.
   2621      */
   2622     public void onAbort() {}
   2623 
   2624     /**
   2625      * Notifies this Connection of a request to hold.
   2626      */
   2627     public void onHold() {}
   2628 
   2629     /**
   2630      * Notifies this Connection of a request to exit a hold state.
   2631      */
   2632     public void onUnhold() {}
   2633 
   2634     /**
   2635      * Notifies this Connection, which is in {@link #STATE_RINGING}, of
   2636      * a request to accept.
   2637      *
   2638      * @param videoState The video state in which to answer the connection.
   2639      */
   2640     public void onAnswer(int videoState) {}
   2641 
   2642     /**
   2643      * Notifies this Connection, which is in {@link #STATE_RINGING}, of
   2644      * a request to accept.
   2645      */
   2646     public void onAnswer() {
   2647         onAnswer(VideoProfile.STATE_AUDIO_ONLY);
   2648     }
   2649 
   2650     /**
   2651      * Notifies this Connection, which is in {@link #STATE_RINGING}, of
   2652      * a request to reject.
   2653      */
   2654     public void onReject() {}
   2655 
   2656     /**
   2657      * Notifies this Connection, which is in {@link #STATE_RINGING}, of
   2658      * a request to reject with a message.
   2659      */
   2660     public void onReject(String replyMessage) {}
   2661 
   2662     /**
   2663      * Notifies the Connection of a request to silence the ringer.
   2664      *
   2665      * @hide
   2666      */
   2667     public void onSilence() {}
   2668 
   2669     /**
   2670      * Notifies this Connection whether the user wishes to proceed with the post-dial DTMF codes.
   2671      */
   2672     public void onPostDialContinue(boolean proceed) {}
   2673 
   2674     /**
   2675      * Notifies this Connection of a request to pull an external call to the local device.
   2676      * <p>
   2677      * The {@link InCallService} issues a request to pull an external call to the local device via
   2678      * {@link Call#pullExternalCall()}.
   2679      * <p>
   2680      * For a Connection to be pulled, both the {@link Connection#CAPABILITY_CAN_PULL_CALL}
   2681      * capability and {@link Connection#PROPERTY_IS_EXTERNAL_CALL} property bits must be set.
   2682      * <p>
   2683      * For more information on external calls, see {@link Connection#PROPERTY_IS_EXTERNAL_CALL}.
   2684      */
   2685     public void onPullExternalCall() {}
   2686 
   2687     /**
   2688      * Notifies this Connection of a {@link Call} event initiated from an {@link InCallService}.
   2689      * <p>
   2690      * The {@link InCallService} issues a Call event via {@link Call#sendCallEvent(String, Bundle)}.
   2691      * <p>
   2692      * Where possible, the Connection should make an attempt to handle {@link Call} events which
   2693      * are part of the {@code android.telecom.*} namespace.  The Connection should ignore any events
   2694      * it does not wish to handle.  Unexpected events should be handled gracefully, as it is
   2695      * possible that a {@link InCallService} has defined its own Call events which a Connection is
   2696      * not aware of.
   2697      * <p>
   2698      * See also {@link Call#sendCallEvent(String, Bundle)}.
   2699      *
   2700      * @param event The call event.
   2701      * @param extras Extras associated with the call event.
   2702      */
   2703     public void onCallEvent(String event, Bundle extras) {}
   2704 
   2705     /**
   2706      * Notifies this {@link Connection} of a change to the extras made outside the
   2707      * {@link ConnectionService}.
   2708      * <p>
   2709      * These extras changes can originate from Telecom itself, or from an {@link InCallService} via
   2710      * the {@link android.telecom.Call#putExtras(Bundle)} and
   2711      * {@link Call#removeExtras(List)}.
   2712      *
   2713      * @param extras The new extras bundle.
   2714      */
   2715     public void onExtrasChanged(Bundle extras) {}
   2716 
   2717     /**
   2718      * Notifies this {@link Connection} that its {@link ConnectionService} is responsible for
   2719      * displaying its incoming call user interface for the {@link Connection}.
   2720      * <p>
   2721      * Will only be called for incoming calls added via a self-managed {@link ConnectionService}
   2722      * (see {@link PhoneAccount#CAPABILITY_SELF_MANAGED}), where the {@link ConnectionService}
   2723      * should show its own incoming call user interface.
   2724      * <p>
   2725      * Where there are ongoing calls in other self-managed {@link ConnectionService}s, or in a
   2726      * regular {@link ConnectionService}, the Telecom framework will display its own incoming call
   2727      * user interface to allow the user to choose whether to answer the new incoming call and
   2728      * disconnect other ongoing calls, or to reject the new incoming call.
   2729      * <p>
   2730      * You should trigger the display of the incoming call user interface for your application by
   2731      * showing a {@link Notification} with a full-screen {@link Intent} specified.
   2732      * For example:
   2733      * <pre><code>
   2734      *     // Create an intent which triggers your fullscreen incoming call user interface.
   2735      *     Intent intent = new Intent(Intent.ACTION_MAIN, null);
   2736      *     intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
   2737      *     intent.setClass(context, YourIncomingCallActivity.class);
   2738      *     PendingIntent pendingIntent = PendingIntent.getActivity(context, 1, intent, 0);
   2739      *
   2740      *     // Build the notification as an ongoing high priority item; this ensures it will show as
   2741      *     // a heads up notification which slides down over top of the current content.
   2742      *     final Notification.Builder builder = new Notification.Builder(context);
   2743      *     builder.setOngoing(true);
   2744      *     builder.setPriority(Notification.PRIORITY_HIGH);
   2745      *
   2746      *     // Set notification content intent to take user to fullscreen UI if user taps on the
   2747      *     // notification body.
   2748      *     builder.setContentIntent(pendingIntent);
   2749      *     // Set full screen intent to trigger display of the fullscreen UI when the notification
   2750      *     // manager deems it appropriate.
   2751      *     builder.setFullScreenIntent(pendingIntent, true);
   2752      *
   2753      *     // Setup notification content.
   2754      *     builder.setSmallIcon( yourIconResourceId );
   2755      *     builder.setContentTitle("Your notification title");
   2756      *     builder.setContentText("Your notification content.");
   2757      *
   2758      *     // Use builder.addAction(..) to add buttons to answer or reject the call.
   2759      *
   2760      *     NotificationManager notificationManager = mContext.getSystemService(
   2761      *         NotificationManager.class);
   2762      *     notificationManager.notify(YOUR_TAG, YOUR_ID, builder.build());
   2763      * </code></pre>
   2764      */
   2765     public void onShowIncomingCallUi() {}
   2766 
   2767     /**
   2768      * Notifies this {@link Connection} that the user has requested an RTT session.
   2769      * The connection service should call {@link #sendRttInitiationSuccess} or
   2770      * {@link #sendRttInitiationFailure} to inform Telecom of the success or failure of the
   2771      * request, respectively.
   2772      * @param rttTextStream The object that should be used to send text to or receive text from
   2773      *                      the in-call app.
   2774      * @hide
   2775      */
   2776     public void onStartRtt(@NonNull RttTextStream rttTextStream) {}
   2777 
   2778     /**
   2779      * Notifies this {@link Connection} that it should terminate any existing RTT communication
   2780      * channel. No response to Telecom is needed for this method.
   2781      * @hide
   2782      */
   2783     public void onStopRtt() {}
   2784 
   2785     /**
   2786      * Notifies this connection of a response to a previous remotely-initiated RTT upgrade
   2787      * request sent via {@link #sendRemoteRttRequest}. Acceptance of the request is
   2788      * indicated by the supplied {@link RttTextStream} being non-null, and rejection is
   2789      * indicated by {@code rttTextStream} being {@code null}
   2790      * @hide
   2791      * @param rttTextStream The object that should be used to send text to or receive text from
   2792      *                      the in-call app.
   2793      */
   2794     public void handleRttUpgradeResponse(@Nullable RttTextStream rttTextStream) {}
   2795 
   2796     static String toLogSafePhoneNumber(String number) {
   2797         // For unknown number, log empty string.
   2798         if (number == null) {
   2799             return "";
   2800         }
   2801 
   2802         if (PII_DEBUG) {
   2803             // When PII_DEBUG is true we emit PII.
   2804             return number;
   2805         }
   2806 
   2807         // Do exactly same thing as Uri#toSafeString() does, which will enable us to compare
   2808         // sanitized phone numbers.
   2809         StringBuilder builder = new StringBuilder();
   2810         for (int i = 0; i < number.length(); i++) {
   2811             char c = number.charAt(i);
   2812             if (c == '-' || c == '@' || c == '.') {
   2813                 builder.append(c);
   2814             } else {
   2815                 builder.append('x');
   2816             }
   2817         }
   2818         return builder.toString();
   2819     }
   2820 
   2821     private void setState(int state) {
   2822         checkImmutable();
   2823         if (mState == STATE_DISCONNECTED && mState != state) {
   2824             Log.d(this, "Connection already DISCONNECTED; cannot transition out of this state.");
   2825             return;
   2826         }
   2827         if (mState != state) {
   2828             Log.d(this, "setState: %s", stateToString(state));
   2829             mState = state;
   2830             onStateChanged(state);
   2831             for (Listener l : mListeners) {
   2832                 l.onStateChanged(this, state);
   2833             }
   2834         }
   2835     }
   2836 
   2837     private static class FailureSignalingConnection extends Connection {
   2838         private boolean mImmutable = false;
   2839         public FailureSignalingConnection(DisconnectCause disconnectCause) {
   2840             setDisconnected(disconnectCause);
   2841             mImmutable = true;
   2842         }
   2843 
   2844         public void checkImmutable() {
   2845             if (mImmutable) {
   2846                 throw new UnsupportedOperationException("Connection is immutable");
   2847             }
   2848         }
   2849     }
   2850 
   2851     /**
   2852      * Return a {@code Connection} which represents a failed connection attempt. The returned
   2853      * {@code Connection} will have a {@link android.telecom.DisconnectCause} and as specified,
   2854      * and a {@link #getState()} of {@link #STATE_DISCONNECTED}.
   2855      * <p>
   2856      * The returned {@code Connection} can be assumed to {@link #destroy()} itself when appropriate,
   2857      * so users of this method need not maintain a reference to its return value to destroy it.
   2858      *
   2859      * @param disconnectCause The disconnect cause, ({@see android.telecomm.DisconnectCause}).
   2860      * @return A {@code Connection} which indicates failure.
   2861      */
   2862     public static Connection createFailedConnection(DisconnectCause disconnectCause) {
   2863         return new FailureSignalingConnection(disconnectCause);
   2864     }
   2865 
   2866     /**
   2867      * Override to throw an {@link UnsupportedOperationException} if this {@code Connection} is
   2868      * not intended to be mutated, e.g., if it is a marker for failure. Only for framework use;
   2869      * this should never be un-@hide-den.
   2870      *
   2871      * @hide
   2872      */
   2873     public void checkImmutable() {}
   2874 
   2875     /**
   2876      * Return a {@code Connection} which represents a canceled connection attempt. The returned
   2877      * {@code Connection} will have state {@link #STATE_DISCONNECTED}, and cannot be moved out of
   2878      * that state. This connection should not be used for anything, and no other
   2879      * {@code Connection}s should be attempted.
   2880      * <p>
   2881      * so users of this method need not maintain a reference to its return value to destroy it.
   2882      *
   2883      * @return A {@code Connection} which indicates that the underlying connection should
   2884      * be canceled.
   2885      */
   2886     public static Connection createCanceledConnection() {
   2887         return new FailureSignalingConnection(new DisconnectCause(DisconnectCause.CANCELED));
   2888     }
   2889 
   2890     private final void fireOnConferenceableConnectionsChanged() {
   2891         for (Listener l : mListeners) {
   2892             l.onConferenceablesChanged(this, getConferenceables());
   2893         }
   2894     }
   2895 
   2896     private final void fireConferenceChanged() {
   2897         for (Listener l : mListeners) {
   2898             l.onConferenceChanged(this, mConference);
   2899         }
   2900     }
   2901 
   2902     private final void clearConferenceableList() {
   2903         for (Conferenceable c : mConferenceables) {
   2904             if (c instanceof Connection) {
   2905                 Connection connection = (Connection) c;
   2906                 connection.removeConnectionListener(mConnectionDeathListener);
   2907             } else if (c instanceof Conference) {
   2908                 Conference conference = (Conference) c;
   2909                 conference.removeListener(mConferenceDeathListener);
   2910             }
   2911         }
   2912         mConferenceables.clear();
   2913     }
   2914 
   2915     /**
   2916      * Handles a change to extras received from Telecom.
   2917      *
   2918      * @param extras The new extras.
   2919      * @hide
   2920      */
   2921     final void handleExtrasChanged(Bundle extras) {
   2922         Bundle b = null;
   2923         synchronized (mExtrasLock) {
   2924             mExtras = extras;
   2925             if (mExtras != null) {
   2926                 b = new Bundle(mExtras);
   2927             }
   2928         }
   2929         onExtrasChanged(b);
   2930     }
   2931 
   2932     /**
   2933      * Notifies listeners that the merge request failed.
   2934      *
   2935      * @hide
   2936      */
   2937     protected final void notifyConferenceMergeFailed() {
   2938         for (Listener l : mListeners) {
   2939             l.onConferenceMergeFailed(this);
   2940         }
   2941     }
   2942 
   2943     /**
   2944      * Notifies listeners of a change to conference participant(s).
   2945      *
   2946      * @param conferenceParticipants The participants.
   2947      * @hide
   2948      */
   2949     protected final void updateConferenceParticipants(
   2950             List<ConferenceParticipant> conferenceParticipants) {
   2951         for (Listener l : mListeners) {
   2952             l.onConferenceParticipantsChanged(this, conferenceParticipants);
   2953         }
   2954     }
   2955 
   2956     /**
   2957      * Notifies listeners that a conference call has been started.
   2958      * @hide
   2959      */
   2960     protected void notifyConferenceStarted() {
   2961         for (Listener l : mListeners) {
   2962             l.onConferenceStarted();
   2963         }
   2964     }
   2965 
   2966     /**
   2967      * Notifies listeners when a change has occurred to the Connection which impacts its ability to
   2968      * be a part of a conference call.
   2969      * @param isConferenceSupported {@code true} if the connection supports being part of a
   2970      *      conference call, {@code false} otherwise.
   2971      * @hide
   2972      */
   2973     protected void notifyConferenceSupportedChanged(boolean isConferenceSupported) {
   2974         for (Listener l : mListeners) {
   2975             l.onConferenceSupportedChanged(this, isConferenceSupported);
   2976         }
   2977     }
   2978 
   2979     /**
   2980      * Sends an event associated with this {@code Connection} with associated event extras to the
   2981      * {@link InCallService}.
   2982      * <p>
   2983      * Connection events are used to communicate point in time information from a
   2984      * {@link ConnectionService} to a {@link InCallService} implementations.  An example of a
   2985      * custom connection event includes notifying the UI when a WIFI call has been handed over to
   2986      * LTE, which the InCall UI might use to inform the user that billing charges may apply.  The
   2987      * Android Telephony framework will send the {@link #EVENT_CALL_MERGE_FAILED} connection event
   2988      * when a call to {@link Call#mergeConference()} has failed to complete successfully.  A
   2989      * connection event could also be used to trigger UI in the {@link InCallService} which prompts
   2990      * the user to make a choice (e.g. whether they want to incur roaming costs for making a call),
   2991      * which is communicated back via {@link Call#sendCallEvent(String, Bundle)}.
   2992      * <p>
   2993      * Events are exposed to {@link InCallService} implementations via
   2994      * {@link Call.Callback#onConnectionEvent(Call, String, Bundle)}.
   2995      * <p>
   2996      * No assumptions should be made as to how an In-Call UI or service will handle these events.
   2997      * The {@link ConnectionService} must assume that the In-Call UI could even chose to ignore
   2998      * some events altogether.
   2999      * <p>
   3000      * Events should be fully qualified (e.g. {@code com.example.event.MY_EVENT}) to avoid
   3001      * conflicts between {@link ConnectionService} implementations.  Further, custom
   3002      * {@link ConnectionService} implementations shall not re-purpose events in the
   3003      * {@code android.*} namespace, nor shall they define new event types in this namespace.  When
   3004      * defining a custom event type, ensure the contents of the extras {@link Bundle} is clearly
   3005      * defined.  Extra keys for this bundle should be named similar to the event type (e.g.
   3006      * {@code com.example.extra.MY_EXTRA}).
   3007      * <p>
   3008      *  When defining events and the associated extras, it is important to keep their behavior
   3009      * consistent when the associated {@link ConnectionService} is updated.  Support for deprecated
   3010      * events/extras should me maintained to ensure backwards compatibility with older
   3011      * {@link InCallService} implementations which were built to support the older behavior.
   3012      *
   3013      * @param event The connection event.
   3014      * @param extras Optional bundle containing extra information associated with the event.
   3015      */
   3016     public void sendConnectionEvent(String event, Bundle extras) {
   3017         for (Listener l : mListeners) {
   3018             l.onConnectionEvent(this, event, extras);
   3019         }
   3020     }
   3021 }
   3022