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.NonNull;
     24 import android.annotation.Nullable;
     25 import android.annotation.SystemApi;
     26 import android.hardware.camera2.CameraManager;
     27 import android.net.Uri;
     28 import android.os.Bundle;
     29 import android.os.Handler;
     30 import android.os.IBinder;
     31 import android.os.Looper;
     32 import android.os.Message;
     33 import android.os.RemoteException;
     34 import android.util.ArraySet;
     35 import android.view.Surface;
     36 
     37 import java.util.ArrayList;
     38 import java.util.Collections;
     39 import java.util.List;
     40 import java.util.Set;
     41 import java.util.concurrent.ConcurrentHashMap;
     42 
     43 /**
     44  * Represents a phone call or connection to a remote endpoint that carries voice and/or video
     45  * traffic.
     46  * <p>
     47  * Implementations create a custom subclass of {@code Connection} and return it to the framework
     48  * as the return value of
     49  * {@link ConnectionService#onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)}
     50  * or
     51  * {@link ConnectionService#onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
     52  * Implementations are then responsible for updating the state of the {@code Connection}, and
     53  * must call {@link #destroy()} to signal to the framework that the {@code Connection} is no
     54  * longer used and associated resources may be recovered.
     55  */
     56 public abstract class Connection extends Conferenceable {
     57 
     58     /**
     59      * The connection is initializing. This is generally the first state for a {@code Connection}
     60      * returned by a {@link ConnectionService}.
     61      */
     62     public static final int STATE_INITIALIZING = 0;
     63 
     64     /**
     65      * The connection is new and not connected.
     66      */
     67     public static final int STATE_NEW = 1;
     68 
     69     /**
     70      * An incoming connection is in the ringing state. During this state, the user's ringer or
     71      * vibration feature will be activated.
     72      */
     73     public static final int STATE_RINGING = 2;
     74 
     75     /**
     76      * An outgoing connection is in the dialing state. In this state the other party has not yet
     77      * answered the call and the user traditionally hears a ringback tone.
     78      */
     79     public static final int STATE_DIALING = 3;
     80 
     81     /**
     82      * A connection is active. Both parties are connected to the call and can actively communicate.
     83      */
     84     public static final int STATE_ACTIVE = 4;
     85 
     86     /**
     87      * A connection is on hold.
     88      */
     89     public static final int STATE_HOLDING = 5;
     90 
     91     /**
     92      * A connection has been disconnected. This is the final state once the user has been
     93      * disconnected from a call either locally, remotely or by an error in the service.
     94      */
     95     public static final int STATE_DISCONNECTED = 6;
     96 
     97     /**
     98      * The state of an external connection which is in the process of being pulled from a remote
     99      * device to the local device.
    100      * <p>
    101      * A connection can only be in this state if the {@link #PROPERTY_IS_EXTERNAL_CALL} property and
    102      * {@link #CAPABILITY_CAN_PULL_CALL} capability bits are set on the connection.
    103      * @hide
    104      */
    105     public static final int STATE_PULLING_CALL = 7;
    106 
    107     /**
    108      * Connection can currently be put on hold or unheld. This is distinct from
    109      * {@link #CAPABILITY_SUPPORT_HOLD} in that although a connection may support 'hold' most times,
    110      * it does not at the moment support the function. This can be true while the call is in the
    111      * state {@link #STATE_DIALING}, for example. During this condition, an in-call UI may
    112      * display a disabled 'hold' button.
    113      */
    114     public static final int CAPABILITY_HOLD = 0x00000001;
    115 
    116     /** Connection supports the hold feature. */
    117     public static final int CAPABILITY_SUPPORT_HOLD = 0x00000002;
    118 
    119     /**
    120      * Connections within a conference can be merged. A {@link ConnectionService} has the option to
    121      * add a {@link Conference} before the child {@link Connection}s are merged. This is how
    122      * CDMA-based {@link Connection}s are implemented. For these unmerged {@link Conference}s, this
    123      * capability allows a merge button to be shown while the conference is in the foreground
    124      * of the in-call UI.
    125      * <p>
    126      * This is only intended for use by a {@link Conference}.
    127      */
    128     public static final int CAPABILITY_MERGE_CONFERENCE = 0x00000004;
    129 
    130     /**
    131      * Connections within a conference can be swapped between foreground and background.
    132      * See {@link #CAPABILITY_MERGE_CONFERENCE} for additional information.
    133      * <p>
    134      * This is only intended for use by a {@link Conference}.
    135      */
    136     public static final int CAPABILITY_SWAP_CONFERENCE = 0x00000008;
    137 
    138     /**
    139      * @hide
    140      */
    141     public static final int CAPABILITY_UNUSED = 0x00000010;
    142 
    143     /** Connection supports responding via text option. */
    144     public static final int CAPABILITY_RESPOND_VIA_TEXT = 0x00000020;
    145 
    146     /** Connection can be muted. */
    147     public static final int CAPABILITY_MUTE = 0x00000040;
    148 
    149     /**
    150      * Connection supports conference management. This capability only applies to
    151      * {@link Conference}s which can have {@link Connection}s as children.
    152      */
    153     public static final int CAPABILITY_MANAGE_CONFERENCE = 0x00000080;
    154 
    155     /**
    156      * Local device supports receiving video.
    157      */
    158     public static final int CAPABILITY_SUPPORTS_VT_LOCAL_RX = 0x00000100;
    159 
    160     /**
    161      * Local device supports transmitting video.
    162      */
    163     public static final int CAPABILITY_SUPPORTS_VT_LOCAL_TX = 0x00000200;
    164 
    165     /**
    166      * Local device supports bidirectional video calling.
    167      */
    168     public static final int CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL =
    169             CAPABILITY_SUPPORTS_VT_LOCAL_RX | CAPABILITY_SUPPORTS_VT_LOCAL_TX;
    170 
    171     /**
    172      * Remote device supports receiving video.
    173      */
    174     public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 0x00000400;
    175 
    176     /**
    177      * Remote device supports transmitting video.
    178      */
    179     public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 0x00000800;
    180 
    181     /**
    182      * Remote device supports bidirectional video calling.
    183      */
    184     public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL =
    185             CAPABILITY_SUPPORTS_VT_REMOTE_RX | CAPABILITY_SUPPORTS_VT_REMOTE_TX;
    186 
    187     /**
    188      * Connection is able to be separated from its parent {@code Conference}, if any.
    189      */
    190     public static final int CAPABILITY_SEPARATE_FROM_CONFERENCE = 0x00001000;
    191 
    192     /**
    193      * Connection is able to be individually disconnected when in a {@code Conference}.
    194      */
    195     public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 0x00002000;
    196 
    197     /**
    198      * Un-used.
    199      * @hide
    200      */
    201     public static final int CAPABILITY_UNUSED_2 = 0x00004000;
    202 
    203     /**
    204      * Un-used.
    205      * @hide
    206      */
    207     public static final int CAPABILITY_UNUSED_3 = 0x00008000;
    208 
    209     /**
    210      * Un-used.
    211      * @hide
    212      */
    213     public static final int CAPABILITY_UNUSED_4 = 0x00010000;
    214 
    215     /**
    216      * Un-used.
    217      * @hide
    218      */
    219     public static final int CAPABILITY_UNUSED_5 = 0x00020000;
    220 
    221     /**
    222      * Speed up audio setup for MT call.
    223      * @hide
    224      */
    225     public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 0x00040000;
    226 
    227     /**
    228      * Call can be upgraded to a video call.
    229      */
    230     public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 0x00080000;
    231 
    232     /**
    233      * For video calls, indicates whether the outgoing video for the call can be paused using
    234      * the {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState.
    235      */
    236     public static final int CAPABILITY_CAN_PAUSE_VIDEO = 0x00100000;
    237 
    238     /**
    239      * For a conference, indicates the conference will not have child connections.
    240      * <p>
    241      * An example of a conference with child connections is a GSM conference call, where the radio
    242      * retains connections to the individual participants of the conference.  Another example is an
    243      * IMS conference call where conference event package functionality is supported; in this case
    244      * the conference server ensures the radio is aware of the participants in the conference, which
    245      * are represented by child connections.
    246      * <p>
    247      * An example of a conference with no child connections is an IMS conference call with no
    248      * conference event package support.  Such a conference is represented by the radio as a single
    249      * connection to the IMS conference server.
    250      * <p>
    251      * Indicating whether a conference has children or not is important to help user interfaces
    252      * visually represent a conference.  A conference with no children, for example, will have the
    253      * conference connection shown in the list of calls on a Bluetooth device, where if the
    254      * conference has children, only the children will be shown in the list of calls on a Bluetooth
    255      * device.
    256      * @hide
    257      */
    258     public static final int CAPABILITY_CONFERENCE_HAS_NO_CHILDREN = 0x00200000;
    259 
    260     /**
    261      * Indicates that the connection itself wants to handle any sort of reply response, rather than
    262      * relying on SMS.
    263      */
    264     public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 0x00400000;
    265 
    266     /**
    267      * When set, prevents a video call from being downgraded to an audio-only call.
    268      * <p>
    269      * Should be set when the VideoState has the {@link VideoProfile#STATE_TX_ENABLED} or
    270      * {@link VideoProfile#STATE_RX_ENABLED} bits set to indicate that the connection cannot be
    271      * downgraded from a video call back to a VideoState of
    272      * {@link VideoProfile#STATE_AUDIO_ONLY}.
    273      * <p>
    274      * Intuitively, a call which can be downgraded to audio should also have local and remote
    275      * video
    276      * capabilities (see {@link #CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL} and
    277      * {@link #CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL}).
    278      */
    279     public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 0x00800000;
    280 
    281     /**
    282      * When set for an external connection, indicates that this {@code Connection} can be pulled
    283      * from a remote device to the current device.
    284      * <p>
    285      * Should only be set on a {@code Connection} where {@link #PROPERTY_IS_EXTERNAL_CALL}
    286      * is set.
    287      * @hide
    288      */
    289     public static final int CAPABILITY_CAN_PULL_CALL = 0x01000000;
    290 
    291     //**********************************************************************************************
    292     // Next CAPABILITY value: 0x02000000
    293     //**********************************************************************************************
    294 
    295     /**
    296      * Indicates that the current device callback number should be shown.
    297      *
    298      * @hide
    299      */
    300     public static final int PROPERTY_SHOW_CALLBACK_NUMBER = 1<<0;
    301 
    302     /**
    303      * Whether the call is a generic conference, where we do not know the precise state of
    304      * participants in the conference (eg. on CDMA).
    305      *
    306      * @hide
    307      */
    308     public static final int PROPERTY_GENERIC_CONFERENCE = 1<<1;
    309 
    310     /**
    311      * Connection is using high definition audio.
    312      * @hide
    313      */
    314     public static final int PROPERTY_HIGH_DEF_AUDIO = 1<<2;
    315 
    316     /**
    317      * Connection is using WIFI.
    318      * @hide
    319      */
    320     public static final int PROPERTY_WIFI = 1<<3;
    321 
    322     /**
    323      * When set, indicates that the {@code Connection} does not actually exist locally for the
    324      * {@link ConnectionService}.
    325      * <p>
    326      * Consider, for example, a scenario where a user has two devices with the same phone number.
    327      * When a user places a call on one devices, the telephony stack can represent that call on the
    328      * other device by adding is to the {@link ConnectionService} with the
    329      * {@link #PROPERTY_IS_EXTERNAL_CALL} capability set.
    330      * <p>
    331      * An {@link ConnectionService} should not assume that all {@link InCallService}s will handle
    332      * external connections.  Only those {@link InCallService}s which have the
    333      * {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true} in its
    334      * manifest will see external connections.
    335      * @hide
    336      */
    337     public static final int PROPERTY_IS_EXTERNAL_CALL = 1<<4;
    338 
    339 
    340     //**********************************************************************************************
    341     // Next PROPERTY value: 1<<5
    342     //**********************************************************************************************
    343 
    344     /**
    345      * Connection extra key used to store the last forwarded number associated with the current
    346      * connection.  Used to communicate to the user interface that the connection was forwarded via
    347      * the specified number.
    348      */
    349     public static final String EXTRA_LAST_FORWARDED_NUMBER =
    350             "android.telecom.extra.LAST_FORWARDED_NUMBER";
    351 
    352     /**
    353      * Connection extra key used to store a child number associated with the current connection.
    354      * Used to communicate to the user interface that the connection was received via
    355      * a child address (i.e. phone number) associated with the {@link PhoneAccount}'s primary
    356      * address.
    357      */
    358     public static final String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
    359 
    360     /**
    361      * Connection extra key used to store the subject for an incoming call.  The user interface can
    362      * query this extra and display its contents for incoming calls.  Will only be used if the
    363      * {@link PhoneAccount} supports the capability {@link PhoneAccount#CAPABILITY_CALL_SUBJECT}.
    364      */
    365     public static final String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
    366 
    367     /**
    368      * Connection event used to inform Telecom that it should play the on hold tone.  This is used
    369      * to play a tone when the peer puts the current call on hold.  Sent to Telecom via
    370      * {@link #sendConnectionEvent(String)}.
    371      * @hide
    372      */
    373     public static final String EVENT_ON_HOLD_TONE_START =
    374             "android.telecom.event.ON_HOLD_TONE_START";
    375 
    376     /**
    377      * Connection event used to inform Telecom that it should stop the on hold tone.  This is used
    378      * to stop a tone when the peer puts the current call on hold.  Sent to Telecom via
    379      * {@link #sendConnectionEvent(String)}.
    380      * @hide
    381      */
    382     public static final String EVENT_ON_HOLD_TONE_END =
    383             "android.telecom.event.ON_HOLD_TONE_END";
    384 
    385     /**
    386      * Connection event used to inform {@link InCallService}s when pulling of an external call has
    387      * failed.  The user interface should inform the user of the error.
    388      * <p>
    389      * Expected to be used by the {@link ConnectionService} when the {@link Call#pullExternalCall()}
    390      * API is called on a {@link Call} with the properties
    391      * {@link Call.Details#PROPERTY_IS_EXTERNAL_CALL} and
    392      * {@link Call.Details#CAPABILITY_CAN_PULL_CALL}, but the {@link ConnectionService} could not
    393      * pull the external call due to an error condition.
    394      * @hide
    395      */
    396     public static final String EVENT_CALL_PULL_FAILED = "android.telecom.event.CALL_PULL_FAILED";
    397 
    398     // Flag controlling whether PII is emitted into the logs
    399     private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
    400 
    401     /**
    402      * Whether the given capabilities support the specified capability.
    403      *
    404      * @param capabilities A capability bit field.
    405      * @param capability The capability to check capabilities for.
    406      * @return Whether the specified capability is supported.
    407      * @hide
    408      */
    409     public static boolean can(int capabilities, int capability) {
    410         return (capabilities & capability) == capability;
    411     }
    412 
    413     /**
    414      * Whether the capabilities of this {@code Connection} supports the specified capability.
    415      *
    416      * @param capability The capability to check capabilities for.
    417      * @return Whether the specified capability is supported.
    418      * @hide
    419      */
    420     public boolean can(int capability) {
    421         return can(mConnectionCapabilities, capability);
    422     }
    423 
    424     /**
    425      * Removes the specified capability from the set of capabilities of this {@code Connection}.
    426      *
    427      * @param capability The capability to remove from the set.
    428      * @hide
    429      */
    430     public void removeCapability(int capability) {
    431         mConnectionCapabilities &= ~capability;
    432     }
    433 
    434     /**
    435      * Adds the specified capability to the set of capabilities of this {@code Connection}.
    436      *
    437      * @param capability The capability to add to the set.
    438      * @hide
    439      */
    440     public void addCapability(int capability) {
    441         mConnectionCapabilities |= capability;
    442     }
    443 
    444 
    445     public static String capabilitiesToString(int capabilities) {
    446         StringBuilder builder = new StringBuilder();
    447         builder.append("[Capabilities:");
    448         if (can(capabilities, CAPABILITY_HOLD)) {
    449             builder.append(" CAPABILITY_HOLD");
    450         }
    451         if (can(capabilities, CAPABILITY_SUPPORT_HOLD)) {
    452             builder.append(" CAPABILITY_SUPPORT_HOLD");
    453         }
    454         if (can(capabilities, CAPABILITY_MERGE_CONFERENCE)) {
    455             builder.append(" CAPABILITY_MERGE_CONFERENCE");
    456         }
    457         if (can(capabilities, CAPABILITY_SWAP_CONFERENCE)) {
    458             builder.append(" CAPABILITY_SWAP_CONFERENCE");
    459         }
    460         if (can(capabilities, CAPABILITY_RESPOND_VIA_TEXT)) {
    461             builder.append(" CAPABILITY_RESPOND_VIA_TEXT");
    462         }
    463         if (can(capabilities, CAPABILITY_MUTE)) {
    464             builder.append(" CAPABILITY_MUTE");
    465         }
    466         if (can(capabilities, CAPABILITY_MANAGE_CONFERENCE)) {
    467             builder.append(" CAPABILITY_MANAGE_CONFERENCE");
    468         }
    469         if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_RX)) {
    470             builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_RX");
    471         }
    472         if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_TX)) {
    473             builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_TX");
    474         }
    475         if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)) {
    476             builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL");
    477         }
    478         if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_RX)) {
    479             builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_RX");
    480         }
    481         if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_TX)) {
    482             builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_TX");
    483         }
    484         if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)) {
    485             builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL");
    486         }
    487         if (can(capabilities, CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO)) {
    488             builder.append(" CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO");
    489         }
    490         if (can(capabilities, CAPABILITY_SPEED_UP_MT_AUDIO)) {
    491             builder.append(" CAPABILITY_SPEED_UP_MT_AUDIO");
    492         }
    493         if (can(capabilities, CAPABILITY_CAN_UPGRADE_TO_VIDEO)) {
    494             builder.append(" CAPABILITY_CAN_UPGRADE_TO_VIDEO");
    495         }
    496         if (can(capabilities, CAPABILITY_CAN_PAUSE_VIDEO)) {
    497             builder.append(" CAPABILITY_CAN_PAUSE_VIDEO");
    498         }
    499         if (can(capabilities, CAPABILITY_CONFERENCE_HAS_NO_CHILDREN)) {
    500             builder.append(" CAPABILITY_SINGLE_PARTY_CONFERENCE");
    501         }
    502         if (can(capabilities, CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION)) {
    503             builder.append(" CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION");
    504         }
    505         if (can(capabilities, CAPABILITY_CAN_PULL_CALL)) {
    506             builder.append(" CAPABILITY_CAN_PULL_CALL");
    507         }
    508 
    509         builder.append("]");
    510         return builder.toString();
    511     }
    512 
    513     /**
    514      * Builds a string representation of a properties bit-mask.
    515      *
    516      * @param properties The properties bit-mask.
    517      * @return String representation.
    518      * @hide
    519      */
    520     public static String propertiesToString(int properties) {
    521         StringBuilder builder = new StringBuilder();
    522         builder.append("[Properties:");
    523 
    524         if (can(properties, PROPERTY_SHOW_CALLBACK_NUMBER)) {
    525             builder.append(" PROPERTY_SHOW_CALLBACK_NUMBER");
    526         }
    527 
    528         if (can(properties, PROPERTY_HIGH_DEF_AUDIO)) {
    529             builder.append(" PROPERTY_HIGH_DEF_AUDIO");
    530         }
    531 
    532         if (can(properties, PROPERTY_WIFI)) {
    533             builder.append(" PROPERTY_WIFI");
    534         }
    535 
    536         if (can(properties, PROPERTY_GENERIC_CONFERENCE)) {
    537             builder.append(" PROPERTY_GENERIC_CONFERENCE");
    538         }
    539 
    540         if (can(properties, PROPERTY_IS_EXTERNAL_CALL)) {
    541             builder.append(" PROPERTY_IS_EXTERNAL_CALL");
    542         }
    543 
    544         builder.append("]");
    545         return builder.toString();
    546     }
    547 
    548     /** @hide */
    549     public abstract static class Listener {
    550         public void onStateChanged(Connection c, int state) {}
    551         public void onAddressChanged(Connection c, Uri newAddress, int presentation) {}
    552         public void onCallerDisplayNameChanged(
    553                 Connection c, String callerDisplayName, int presentation) {}
    554         public void onVideoStateChanged(Connection c, int videoState) {}
    555         public void onDisconnected(Connection c, DisconnectCause disconnectCause) {}
    556         public void onPostDialWait(Connection c, String remaining) {}
    557         public void onPostDialChar(Connection c, char nextChar) {}
    558         public void onRingbackRequested(Connection c, boolean ringback) {}
    559         public void onDestroyed(Connection c) {}
    560         public void onConnectionCapabilitiesChanged(Connection c, int capabilities) {}
    561         public void onConnectionPropertiesChanged(Connection c, int properties) {}
    562         public void onVideoProviderChanged(
    563                 Connection c, VideoProvider videoProvider) {}
    564         public void onAudioModeIsVoipChanged(Connection c, boolean isVoip) {}
    565         public void onStatusHintsChanged(Connection c, StatusHints statusHints) {}
    566         public void onConferenceablesChanged(
    567                 Connection c, List<Conferenceable> conferenceables) {}
    568         public void onConferenceChanged(Connection c, Conference conference) {}
    569         /** @hide */
    570         public void onConferenceParticipantsChanged(Connection c,
    571                 List<ConferenceParticipant> participants) {}
    572         public void onConferenceStarted() {}
    573         public void onConferenceMergeFailed(Connection c) {}
    574         public void onExtrasChanged(Connection c, Bundle extras) {}
    575         public void onExtrasRemoved(Connection c, List<String> keys) {}
    576         public void onConnectionEvent(Connection c, String event, Bundle extras) {}
    577     }
    578 
    579     /**
    580      * Provides a means of controlling the video session associated with a {@link Connection}.
    581      * <p>
    582      * Implementations create a custom subclass of {@link VideoProvider} and the
    583      * {@link ConnectionService} creates an instance sets it on the {@link Connection} using
    584      * {@link Connection#setVideoProvider(VideoProvider)}.  Any connection which supports video
    585      * should set the {@link VideoProvider}.
    586      * <p>
    587      * The {@link VideoProvider} serves two primary purposes: it provides a means for Telecom and
    588      * {@link InCallService} implementations to issue requests related to the video session;
    589      * it provides a means for the {@link ConnectionService} to report events and information
    590      * related to the video session to Telecom and the {@link InCallService} implementations.
    591      * <p>
    592      * {@link InCallService} implementations interact with the {@link VideoProvider} via
    593      * {@link android.telecom.InCallService.VideoCall}.
    594      */
    595     public static abstract class VideoProvider {
    596 
    597         /**
    598          * Video is not being received (no protocol pause was issued).
    599          * @see #handleCallSessionEvent(int)
    600          */
    601         public static final int SESSION_EVENT_RX_PAUSE = 1;
    602 
    603         /**
    604          * Video reception has resumed after a {@link #SESSION_EVENT_RX_PAUSE}.
    605          * @see #handleCallSessionEvent(int)
    606          */
    607         public static final int SESSION_EVENT_RX_RESUME = 2;
    608 
    609         /**
    610          * Video transmission has begun. This occurs after a negotiated start of video transmission
    611          * when the underlying protocol has actually begun transmitting video to the remote party.
    612          * @see #handleCallSessionEvent(int)
    613          */
    614         public static final int SESSION_EVENT_TX_START = 3;
    615 
    616         /**
    617          * Video transmission has stopped. This occurs after a negotiated stop of video transmission
    618          * when the underlying protocol has actually stopped transmitting video to the remote party.
    619          * @see #handleCallSessionEvent(int)
    620          */
    621         public static final int SESSION_EVENT_TX_STOP = 4;
    622 
    623         /**
    624          * A camera failure has occurred for the selected camera.  The {@link InCallService} can use
    625          * this as a cue to inform the user the camera is not available.
    626          * @see #handleCallSessionEvent(int)
    627          */
    628         public static final int SESSION_EVENT_CAMERA_FAILURE = 5;
    629 
    630         /**
    631          * Issued after {@link #SESSION_EVENT_CAMERA_FAILURE} when the camera is once again ready
    632          * for operation.  The {@link InCallService} can use this as a cue to inform the user that
    633          * the camera has become available again.
    634          * @see #handleCallSessionEvent(int)
    635          */
    636         public static final int SESSION_EVENT_CAMERA_READY = 6;
    637 
    638         /**
    639          * Session modify request was successful.
    640          * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
    641          */
    642         public static final int SESSION_MODIFY_REQUEST_SUCCESS = 1;
    643 
    644         /**
    645          * Session modify request failed.
    646          * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
    647          */
    648         public static final int SESSION_MODIFY_REQUEST_FAIL = 2;
    649 
    650         /**
    651          * Session modify request ignored due to invalid parameters.
    652          * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
    653          */
    654         public static final int SESSION_MODIFY_REQUEST_INVALID = 3;
    655 
    656         /**
    657          * Session modify request timed out.
    658          * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
    659          */
    660         public static final int SESSION_MODIFY_REQUEST_TIMED_OUT = 4;
    661 
    662         /**
    663          * Session modify request rejected by remote user.
    664          * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
    665          */
    666         public static final int SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE = 5;
    667 
    668         private static final int MSG_ADD_VIDEO_CALLBACK = 1;
    669         private static final int MSG_SET_CAMERA = 2;
    670         private static final int MSG_SET_PREVIEW_SURFACE = 3;
    671         private static final int MSG_SET_DISPLAY_SURFACE = 4;
    672         private static final int MSG_SET_DEVICE_ORIENTATION = 5;
    673         private static final int MSG_SET_ZOOM = 6;
    674         private static final int MSG_SEND_SESSION_MODIFY_REQUEST = 7;
    675         private static final int MSG_SEND_SESSION_MODIFY_RESPONSE = 8;
    676         private static final int MSG_REQUEST_CAMERA_CAPABILITIES = 9;
    677         private static final int MSG_REQUEST_CONNECTION_DATA_USAGE = 10;
    678         private static final int MSG_SET_PAUSE_IMAGE = 11;
    679         private static final int MSG_REMOVE_VIDEO_CALLBACK = 12;
    680 
    681         private VideoProvider.VideoProviderHandler mMessageHandler;
    682         private final VideoProvider.VideoProviderBinder mBinder;
    683 
    684         /**
    685          * Stores a list of the video callbacks, keyed by IBinder.
    686          *
    687          * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
    688          * load factor before resizing, 1 means we only expect a single thread to
    689          * access the map so make only a single shard
    690          */
    691         private ConcurrentHashMap<IBinder, IVideoCallback> mVideoCallbacks =
    692                 new ConcurrentHashMap<IBinder, IVideoCallback>(8, 0.9f, 1);
    693 
    694         /**
    695          * Default handler used to consolidate binder method calls onto a single thread.
    696          */
    697         private final class VideoProviderHandler extends Handler {
    698             public VideoProviderHandler() {
    699                 super();
    700             }
    701 
    702             public VideoProviderHandler(Looper looper) {
    703                 super(looper);
    704             }
    705 
    706             @Override
    707             public void handleMessage(Message msg) {
    708                 switch (msg.what) {
    709                     case MSG_ADD_VIDEO_CALLBACK: {
    710                         IBinder binder = (IBinder) msg.obj;
    711                         IVideoCallback callback = IVideoCallback.Stub
    712                                 .asInterface((IBinder) msg.obj);
    713                         if (callback == null) {
    714                             Log.w(this, "addVideoProvider - skipped; callback is null.");
    715                             break;
    716                         }
    717 
    718                         if (mVideoCallbacks.containsKey(binder)) {
    719                             Log.i(this, "addVideoProvider - skipped; already present.");
    720                             break;
    721                         }
    722                         mVideoCallbacks.put(binder, callback);
    723                         break;
    724                     }
    725                     case MSG_REMOVE_VIDEO_CALLBACK: {
    726                         IBinder binder = (IBinder) msg.obj;
    727                         IVideoCallback callback = IVideoCallback.Stub
    728                                 .asInterface((IBinder) msg.obj);
    729                         if (!mVideoCallbacks.containsKey(binder)) {
    730                             Log.i(this, "removeVideoProvider - skipped; not present.");
    731                             break;
    732                         }
    733                         mVideoCallbacks.remove(binder);
    734                         break;
    735                     }
    736                     case MSG_SET_CAMERA:
    737                         onSetCamera((String) msg.obj);
    738                         break;
    739                     case MSG_SET_PREVIEW_SURFACE:
    740                         onSetPreviewSurface((Surface) msg.obj);
    741                         break;
    742                     case MSG_SET_DISPLAY_SURFACE:
    743                         onSetDisplaySurface((Surface) msg.obj);
    744                         break;
    745                     case MSG_SET_DEVICE_ORIENTATION:
    746                         onSetDeviceOrientation(msg.arg1);
    747                         break;
    748                     case MSG_SET_ZOOM:
    749                         onSetZoom((Float) msg.obj);
    750                         break;
    751                     case MSG_SEND_SESSION_MODIFY_REQUEST: {
    752                         SomeArgs args = (SomeArgs) msg.obj;
    753                         try {
    754                             onSendSessionModifyRequest((VideoProfile) args.arg1,
    755                                     (VideoProfile) args.arg2);
    756                         } finally {
    757                             args.recycle();
    758                         }
    759                         break;
    760                     }
    761                     case MSG_SEND_SESSION_MODIFY_RESPONSE:
    762                         onSendSessionModifyResponse((VideoProfile) msg.obj);
    763                         break;
    764                     case MSG_REQUEST_CAMERA_CAPABILITIES:
    765                         onRequestCameraCapabilities();
    766                         break;
    767                     case MSG_REQUEST_CONNECTION_DATA_USAGE:
    768                         onRequestConnectionDataUsage();
    769                         break;
    770                     case MSG_SET_PAUSE_IMAGE:
    771                         onSetPauseImage((Uri) msg.obj);
    772                         break;
    773                     default:
    774                         break;
    775                 }
    776             }
    777         }
    778 
    779         /**
    780          * IVideoProvider stub implementation.
    781          */
    782         private final class VideoProviderBinder extends IVideoProvider.Stub {
    783             public void addVideoCallback(IBinder videoCallbackBinder) {
    784                 mMessageHandler.obtainMessage(
    785                         MSG_ADD_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
    786             }
    787 
    788             public void removeVideoCallback(IBinder videoCallbackBinder) {
    789                 mMessageHandler.obtainMessage(
    790                         MSG_REMOVE_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
    791             }
    792 
    793             public void setCamera(String cameraId) {
    794                 mMessageHandler.obtainMessage(MSG_SET_CAMERA, cameraId).sendToTarget();
    795             }
    796 
    797             public void setPreviewSurface(Surface surface) {
    798                 mMessageHandler.obtainMessage(MSG_SET_PREVIEW_SURFACE, surface).sendToTarget();
    799             }
    800 
    801             public void setDisplaySurface(Surface surface) {
    802                 mMessageHandler.obtainMessage(MSG_SET_DISPLAY_SURFACE, surface).sendToTarget();
    803             }
    804 
    805             public void setDeviceOrientation(int rotation) {
    806                 mMessageHandler.obtainMessage(
    807                         MSG_SET_DEVICE_ORIENTATION, rotation, 0).sendToTarget();
    808             }
    809 
    810             public void setZoom(float value) {
    811                 mMessageHandler.obtainMessage(MSG_SET_ZOOM, value).sendToTarget();
    812             }
    813 
    814             public void sendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile) {
    815                 SomeArgs args = SomeArgs.obtain();
    816                 args.arg1 = fromProfile;
    817                 args.arg2 = toProfile;
    818                 mMessageHandler.obtainMessage(MSG_SEND_SESSION_MODIFY_REQUEST, args).sendToTarget();
    819             }
    820 
    821             public void sendSessionModifyResponse(VideoProfile responseProfile) {
    822                 mMessageHandler.obtainMessage(
    823                         MSG_SEND_SESSION_MODIFY_RESPONSE, responseProfile).sendToTarget();
    824             }
    825 
    826             public void requestCameraCapabilities() {
    827                 mMessageHandler.obtainMessage(MSG_REQUEST_CAMERA_CAPABILITIES).sendToTarget();
    828             }
    829 
    830             public void requestCallDataUsage() {
    831                 mMessageHandler.obtainMessage(MSG_REQUEST_CONNECTION_DATA_USAGE).sendToTarget();
    832             }
    833 
    834             public void setPauseImage(Uri uri) {
    835                 mMessageHandler.obtainMessage(MSG_SET_PAUSE_IMAGE, uri).sendToTarget();
    836             }
    837         }
    838 
    839         public VideoProvider() {
    840             mBinder = new VideoProvider.VideoProviderBinder();
    841             mMessageHandler = new VideoProvider.VideoProviderHandler(Looper.getMainLooper());
    842         }
    843 
    844         /**
    845          * Creates an instance of the {@link VideoProvider}, specifying the looper to use.
    846          *
    847          * @param looper The looper.
    848          * @hide
    849          */
    850         public VideoProvider(Looper looper) {
    851             mBinder = new VideoProvider.VideoProviderBinder();
    852             mMessageHandler = new VideoProvider.VideoProviderHandler(looper);
    853         }
    854 
    855         /**
    856          * Returns binder object which can be used across IPC methods.
    857          * @hide
    858          */
    859         public final IVideoProvider getInterface() {
    860             return mBinder;
    861         }
    862 
    863         /**
    864          * Sets the camera to be used for the outgoing video.
    865          * <p>
    866          * The {@link VideoProvider} should respond by communicating the capabilities of the chosen
    867          * camera via
    868          * {@link VideoProvider#changeCameraCapabilities(VideoProfile.CameraCapabilities)}.
    869          * <p>
    870          * Sent from the {@link InCallService} via
    871          * {@link InCallService.VideoCall#setCamera(String)}.
    872          *
    873          * @param cameraId The id of the camera (use ids as reported by
    874          * {@link CameraManager#getCameraIdList()}).
    875          */
    876         public abstract void onSetCamera(String cameraId);
    877 
    878         /**
    879          * Sets the surface to be used for displaying a preview of what the user's camera is
    880          * currently capturing.  When video transmission is enabled, this is the video signal which
    881          * is sent to the remote device.
    882          * <p>
    883          * Sent from the {@link InCallService} via
    884          * {@link InCallService.VideoCall#setPreviewSurface(Surface)}.
    885          *
    886          * @param surface The {@link Surface}.
    887          */
    888         public abstract void onSetPreviewSurface(Surface surface);
    889 
    890         /**
    891          * Sets the surface to be used for displaying the video received from the remote device.
    892          * <p>
    893          * Sent from the {@link InCallService} via
    894          * {@link InCallService.VideoCall#setDisplaySurface(Surface)}.
    895          *
    896          * @param surface The {@link Surface}.
    897          */
    898         public abstract void onSetDisplaySurface(Surface surface);
    899 
    900         /**
    901          * Sets the device orientation, in degrees.  Assumes that a standard portrait orientation of
    902          * the device is 0 degrees.
    903          * <p>
    904          * Sent from the {@link InCallService} via
    905          * {@link InCallService.VideoCall#setDeviceOrientation(int)}.
    906          *
    907          * @param rotation The device orientation, in degrees.
    908          */
    909         public abstract void onSetDeviceOrientation(int rotation);
    910 
    911         /**
    912          * Sets camera zoom ratio.
    913          * <p>
    914          * Sent from the {@link InCallService} via {@link InCallService.VideoCall#setZoom(float)}.
    915          *
    916          * @param value The camera zoom ratio.
    917          */
    918         public abstract void onSetZoom(float value);
    919 
    920         /**
    921          * Issues a request to modify the properties of the current video session.
    922          * <p>
    923          * Example scenarios include: requesting an audio-only call to be upgraded to a
    924          * bi-directional video call, turning on or off the user's camera, sending a pause signal
    925          * when the {@link InCallService} is no longer the foreground application.
    926          * <p>
    927          * If the {@link VideoProvider} determines a request to be invalid, it should call
    928          * {@link #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)} to report the
    929          * invalid request back to the {@link InCallService}.
    930          * <p>
    931          * Where a request requires confirmation from the user of the peer device, the
    932          * {@link VideoProvider} must communicate the request to the peer device and handle the
    933          * user's response.  {@link #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)}
    934          * is used to inform the {@link InCallService} of the result of the request.
    935          * <p>
    936          * Sent from the {@link InCallService} via
    937          * {@link InCallService.VideoCall#sendSessionModifyRequest(VideoProfile)}.
    938          *
    939          * @param fromProfile The video profile prior to the request.
    940          * @param toProfile The video profile with the requested changes made.
    941          */
    942         public abstract void onSendSessionModifyRequest(VideoProfile fromProfile,
    943                 VideoProfile toProfile);
    944 
    945         /**
    946          * Provides a response to a request to change the current video session properties.
    947          * <p>
    948          * For example, if the peer requests and upgrade from an audio-only call to a bi-directional
    949          * video call, could decline the request and keep the call as audio-only.
    950          * In such a scenario, the {@code responseProfile} would have a video state of
    951          * {@link VideoProfile#STATE_AUDIO_ONLY}.  If the user had decided to accept the request,
    952          * the video state would be {@link VideoProfile#STATE_BIDIRECTIONAL}.
    953          * <p>
    954          * Sent from the {@link InCallService} via
    955          * {@link InCallService.VideoCall#sendSessionModifyResponse(VideoProfile)} in response to
    956          * a {@link InCallService.VideoCall.Callback#onSessionModifyRequestReceived(VideoProfile)}
    957          * callback.
    958          *
    959          * @param responseProfile The response video profile.
    960          */
    961         public abstract void onSendSessionModifyResponse(VideoProfile responseProfile);
    962 
    963         /**
    964          * Issues a request to the {@link VideoProvider} to retrieve the camera capabilities.
    965          * <p>
    966          * The {@link VideoProvider} should respond by communicating the capabilities of the chosen
    967          * camera via
    968          * {@link VideoProvider#changeCameraCapabilities(VideoProfile.CameraCapabilities)}.
    969          * <p>
    970          * Sent from the {@link InCallService} via
    971          * {@link InCallService.VideoCall#requestCameraCapabilities()}.
    972          */
    973         public abstract void onRequestCameraCapabilities();
    974 
    975         /**
    976          * Issues a request to the {@link VideoProvider} to retrieve the current data usage for the
    977          * video component of the current {@link Connection}.
    978          * <p>
    979          * The {@link VideoProvider} should respond by communicating current data usage, in bytes,
    980          * via {@link VideoProvider#setCallDataUsage(long)}.
    981          * <p>
    982          * Sent from the {@link InCallService} via
    983          * {@link InCallService.VideoCall#requestCallDataUsage()}.
    984          */
    985         public abstract void onRequestConnectionDataUsage();
    986 
    987         /**
    988          * Provides the {@link VideoProvider} with the {@link Uri} of an image to be displayed to
    989          * the peer device when the video signal is paused.
    990          * <p>
    991          * Sent from the {@link InCallService} via
    992          * {@link InCallService.VideoCall#setPauseImage(Uri)}.
    993          *
    994          * @param uri URI of image to display.
    995          */
    996         public abstract void onSetPauseImage(Uri uri);
    997 
    998         /**
    999          * Used to inform listening {@link InCallService} implementations when the
   1000          * {@link VideoProvider} receives a session modification request.
   1001          * <p>
   1002          * Received by the {@link InCallService} via
   1003          * {@link InCallService.VideoCall.Callback#onSessionModifyRequestReceived(VideoProfile)},
   1004          *
   1005          * @param videoProfile The requested video profile.
   1006          * @see #onSendSessionModifyRequest(VideoProfile, VideoProfile)
   1007          */
   1008         public void receiveSessionModifyRequest(VideoProfile videoProfile) {
   1009             if (mVideoCallbacks != null) {
   1010                 for (IVideoCallback callback : mVideoCallbacks.values()) {
   1011                     try {
   1012                         callback.receiveSessionModifyRequest(videoProfile);
   1013                     } catch (RemoteException ignored) {
   1014                         Log.w(this, "receiveSessionModifyRequest callback failed", ignored);
   1015                     }
   1016                 }
   1017             }
   1018         }
   1019 
   1020         /**
   1021          * Used to inform listening {@link InCallService} implementations when the
   1022          * {@link VideoProvider} receives a response to a session modification request.
   1023          * <p>
   1024          * Received by the {@link InCallService} via
   1025          * {@link InCallService.VideoCall.Callback#onSessionModifyResponseReceived(int,
   1026          * VideoProfile, VideoProfile)}.
   1027          *
   1028          * @param status Status of the session modify request.  Valid values are
   1029          *               {@link VideoProvider#SESSION_MODIFY_REQUEST_SUCCESS},
   1030          *               {@link VideoProvider#SESSION_MODIFY_REQUEST_FAIL},
   1031          *               {@link VideoProvider#SESSION_MODIFY_REQUEST_INVALID},
   1032          *               {@link VideoProvider#SESSION_MODIFY_REQUEST_TIMED_OUT},
   1033          *               {@link VideoProvider#SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE}
   1034          * @param requestedProfile The original request which was sent to the peer device.
   1035          * @param responseProfile The actual profile changes agreed to by the peer device.
   1036          * @see #onSendSessionModifyRequest(VideoProfile, VideoProfile)
   1037          */
   1038         public void receiveSessionModifyResponse(int status,
   1039                 VideoProfile requestedProfile, VideoProfile responseProfile) {
   1040             if (mVideoCallbacks != null) {
   1041                 for (IVideoCallback callback : mVideoCallbacks.values()) {
   1042                     try {
   1043                         callback.receiveSessionModifyResponse(status, requestedProfile,
   1044                                 responseProfile);
   1045                     } catch (RemoteException ignored) {
   1046                         Log.w(this, "receiveSessionModifyResponse callback failed", ignored);
   1047                     }
   1048                 }
   1049             }
   1050         }
   1051 
   1052         /**
   1053          * Used to inform listening {@link InCallService} implementations when the
   1054          * {@link VideoProvider} reports a call session event.
   1055          * <p>
   1056          * Received by the {@link InCallService} via
   1057          * {@link InCallService.VideoCall.Callback#onCallSessionEvent(int)}.
   1058          *
   1059          * @param event The event.  Valid values are: {@link VideoProvider#SESSION_EVENT_RX_PAUSE},
   1060          *      {@link VideoProvider#SESSION_EVENT_RX_RESUME},
   1061          *      {@link VideoProvider#SESSION_EVENT_TX_START},
   1062          *      {@link VideoProvider#SESSION_EVENT_TX_STOP},
   1063          *      {@link VideoProvider#SESSION_EVENT_CAMERA_FAILURE},
   1064          *      {@link VideoProvider#SESSION_EVENT_CAMERA_READY}.
   1065          */
   1066         public void handleCallSessionEvent(int event) {
   1067             if (mVideoCallbacks != null) {
   1068                 for (IVideoCallback callback : mVideoCallbacks.values()) {
   1069                     try {
   1070                         callback.handleCallSessionEvent(event);
   1071                     } catch (RemoteException ignored) {
   1072                         Log.w(this, "handleCallSessionEvent callback failed", ignored);
   1073                     }
   1074                 }
   1075             }
   1076         }
   1077 
   1078         /**
   1079          * Used to inform listening {@link InCallService} implementations when the dimensions of the
   1080          * peer's video have changed.
   1081          * <p>
   1082          * This could occur if, for example, the peer rotates their device, changing the aspect
   1083          * ratio of the video, or if the user switches between the back and front cameras.
   1084          * <p>
   1085          * Received by the {@link InCallService} via
   1086          * {@link InCallService.VideoCall.Callback#onPeerDimensionsChanged(int, int)}.
   1087          *
   1088          * @param width  The updated peer video width.
   1089          * @param height The updated peer video height.
   1090          */
   1091         public void changePeerDimensions(int width, int height) {
   1092             if (mVideoCallbacks != null) {
   1093                 for (IVideoCallback callback : mVideoCallbacks.values()) {
   1094                     try {
   1095                         callback.changePeerDimensions(width, height);
   1096                     } catch (RemoteException ignored) {
   1097                         Log.w(this, "changePeerDimensions callback failed", ignored);
   1098                     }
   1099                 }
   1100             }
   1101         }
   1102 
   1103         /**
   1104          * Used to inform listening {@link InCallService} implementations when the data usage of the
   1105          * video associated with the current {@link Connection} has changed.
   1106          * <p>
   1107          * This could be in response to a preview request via
   1108          * {@link #onRequestConnectionDataUsage()}, or as a periodic update by the
   1109          * {@link VideoProvider}.  Where periodic updates of data usage are provided, they should be
   1110          * provided at most for every 1 MB of data transferred and no more than once every 10 sec.
   1111          * <p>
   1112          * Received by the {@link InCallService} via
   1113          * {@link InCallService.VideoCall.Callback#onCallDataUsageChanged(long)}.
   1114          *
   1115          * @param dataUsage The updated data usage (in bytes).  Reported as the cumulative bytes
   1116          *                  used since the start of the call.
   1117          */
   1118         public void setCallDataUsage(long dataUsage) {
   1119             if (mVideoCallbacks != null) {
   1120                 for (IVideoCallback callback : mVideoCallbacks.values()) {
   1121                     try {
   1122                         callback.changeCallDataUsage(dataUsage);
   1123                     } catch (RemoteException ignored) {
   1124                         Log.w(this, "setCallDataUsage callback failed", ignored);
   1125                     }
   1126                 }
   1127             }
   1128         }
   1129 
   1130         /**
   1131          * @see #setCallDataUsage(long)
   1132          *
   1133          * @param dataUsage The updated data usage (in byes).
   1134          * @deprecated - Use {@link #setCallDataUsage(long)} instead.
   1135          * @hide
   1136          */
   1137         public void changeCallDataUsage(long dataUsage) {
   1138             setCallDataUsage(dataUsage);
   1139         }
   1140 
   1141         /**
   1142          * Used to inform listening {@link InCallService} implementations when the capabilities of
   1143          * the current camera have changed.
   1144          * <p>
   1145          * The {@link VideoProvider} should call this in response to
   1146          * {@link VideoProvider#onRequestCameraCapabilities()}, or when the current camera is
   1147          * changed via {@link VideoProvider#onSetCamera(String)}.
   1148          * <p>
   1149          * Received by the {@link InCallService} via
   1150          * {@link InCallService.VideoCall.Callback#onCameraCapabilitiesChanged(
   1151          * VideoProfile.CameraCapabilities)}.
   1152          *
   1153          * @param cameraCapabilities The new camera capabilities.
   1154          */
   1155         public void changeCameraCapabilities(VideoProfile.CameraCapabilities cameraCapabilities) {
   1156             if (mVideoCallbacks != null) {
   1157                 for (IVideoCallback callback : mVideoCallbacks.values()) {
   1158                     try {
   1159                         callback.changeCameraCapabilities(cameraCapabilities);
   1160                     } catch (RemoteException ignored) {
   1161                         Log.w(this, "changeCameraCapabilities callback failed", ignored);
   1162                     }
   1163                 }
   1164             }
   1165         }
   1166 
   1167         /**
   1168          * Used to inform listening {@link InCallService} implementations when the video quality
   1169          * of the call has changed.
   1170          * <p>
   1171          * Received by the {@link InCallService} via
   1172          * {@link InCallService.VideoCall.Callback#onVideoQualityChanged(int)}.
   1173          *
   1174          * @param videoQuality The updated video quality.  Valid values:
   1175          *      {@link VideoProfile#QUALITY_HIGH},
   1176          *      {@link VideoProfile#QUALITY_MEDIUM},
   1177          *      {@link VideoProfile#QUALITY_LOW},
   1178          *      {@link VideoProfile#QUALITY_DEFAULT}.
   1179          */
   1180         public void changeVideoQuality(int videoQuality) {
   1181             if (mVideoCallbacks != null) {
   1182                 for (IVideoCallback callback : mVideoCallbacks.values()) {
   1183                     try {
   1184                         callback.changeVideoQuality(videoQuality);
   1185                     } catch (RemoteException ignored) {
   1186                         Log.w(this, "changeVideoQuality callback failed", ignored);
   1187                     }
   1188                 }
   1189             }
   1190         }
   1191     }
   1192 
   1193     private final Listener mConnectionDeathListener = new Listener() {
   1194         @Override
   1195         public void onDestroyed(Connection c) {
   1196             if (mConferenceables.remove(c)) {
   1197                 fireOnConferenceableConnectionsChanged();
   1198             }
   1199         }
   1200     };
   1201 
   1202     private final Conference.Listener mConferenceDeathListener = new Conference.Listener() {
   1203         @Override
   1204         public void onDestroyed(Conference c) {
   1205             if (mConferenceables.remove(c)) {
   1206                 fireOnConferenceableConnectionsChanged();
   1207             }
   1208         }
   1209     };
   1210 
   1211     /**
   1212      * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
   1213      * load factor before resizing, 1 means we only expect a single thread to
   1214      * access the map so make only a single shard
   1215      */
   1216     private final Set<Listener> mListeners = Collections.newSetFromMap(
   1217             new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
   1218     private final List<Conferenceable> mConferenceables = new ArrayList<>();
   1219     private final List<Conferenceable> mUnmodifiableConferenceables =
   1220             Collections.unmodifiableList(mConferenceables);
   1221 
   1222     // The internal telecom call ID associated with this connection.
   1223     private String mTelecomCallId;
   1224     private int mState = STATE_NEW;
   1225     private CallAudioState mCallAudioState;
   1226     private Uri mAddress;
   1227     private int mAddressPresentation;
   1228     private String mCallerDisplayName;
   1229     private int mCallerDisplayNamePresentation;
   1230     private boolean mRingbackRequested = false;
   1231     private int mConnectionCapabilities;
   1232     private int mConnectionProperties;
   1233     private VideoProvider mVideoProvider;
   1234     private boolean mAudioModeIsVoip;
   1235     private long mConnectTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED;
   1236     private StatusHints mStatusHints;
   1237     private int mVideoState;
   1238     private DisconnectCause mDisconnectCause;
   1239     private Conference mConference;
   1240     private ConnectionService mConnectionService;
   1241     private Bundle mExtras;
   1242     private final Object mExtrasLock = new Object();
   1243 
   1244     /**
   1245      * Tracks the key set for the extras bundle provided on the last invocation of
   1246      * {@link #setExtras(Bundle)}.  Used so that on subsequent invocations we can remove any extras
   1247      * keys which were set previously but are no longer present in the replacement Bundle.
   1248      */
   1249     private Set<String> mPreviousExtraKeys;
   1250 
   1251     /**
   1252      * Create a new Connection.
   1253      */
   1254     public Connection() {}
   1255 
   1256     /**
   1257      * Returns the Telecom internal call ID associated with this connection.  Should only be used
   1258      * for debugging and tracing purposes.
   1259      *
   1260      * @return The Telecom call ID.
   1261      * @hide
   1262      */
   1263     public final String getTelecomCallId() {
   1264         return mTelecomCallId;
   1265     }
   1266 
   1267     /**
   1268      * @return The address (e.g., phone number) to which this Connection is currently communicating.
   1269      */
   1270     public final Uri getAddress() {
   1271         return mAddress;
   1272     }
   1273 
   1274     /**
   1275      * @return The presentation requirements for the address.
   1276      *         See {@link TelecomManager} for valid values.
   1277      */
   1278     public final int getAddressPresentation() {
   1279         return mAddressPresentation;
   1280     }
   1281 
   1282     /**
   1283      * @return The caller display name (CNAP).
   1284      */
   1285     public final String getCallerDisplayName() {
   1286         return mCallerDisplayName;
   1287     }
   1288 
   1289     /**
   1290      * @return The presentation requirements for the handle.
   1291      *         See {@link TelecomManager} for valid values.
   1292      */
   1293     public final int getCallerDisplayNamePresentation() {
   1294         return mCallerDisplayNamePresentation;
   1295     }
   1296 
   1297     /**
   1298      * @return The state of this Connection.
   1299      */
   1300     public final int getState() {
   1301         return mState;
   1302     }
   1303 
   1304     /**
   1305      * Returns the video state of the connection.
   1306      * Valid values: {@link VideoProfile#STATE_AUDIO_ONLY},
   1307      * {@link VideoProfile#STATE_BIDIRECTIONAL},
   1308      * {@link VideoProfile#STATE_TX_ENABLED},
   1309      * {@link VideoProfile#STATE_RX_ENABLED}.
   1310      *
   1311      * @return The video state of the connection.
   1312      * @hide
   1313      */
   1314     public final int getVideoState() {
   1315         return mVideoState;
   1316     }
   1317 
   1318     /**
   1319      * @return The audio state of the connection, describing how its audio is currently
   1320      *         being routed by the system. This is {@code null} if this Connection
   1321      *         does not directly know about its audio state.
   1322      * @deprecated Use {@link #getCallAudioState()} instead.
   1323      * @hide
   1324      */
   1325     @SystemApi
   1326     @Deprecated
   1327     public final AudioState getAudioState() {
   1328         if (mCallAudioState == null) {
   1329           return null;
   1330         }
   1331         return new AudioState(mCallAudioState);
   1332     }
   1333 
   1334     /**
   1335      * @return The audio state of the connection, describing how its audio is currently
   1336      *         being routed by the system. This is {@code null} if this Connection
   1337      *         does not directly know about its audio state.
   1338      */
   1339     public final CallAudioState getCallAudioState() {
   1340         return mCallAudioState;
   1341     }
   1342 
   1343     /**
   1344      * @return The conference that this connection is a part of.  Null if it is not part of any
   1345      *         conference.
   1346      */
   1347     public final Conference getConference() {
   1348         return mConference;
   1349     }
   1350 
   1351     /**
   1352      * Returns whether this connection is requesting that the system play a ringback tone
   1353      * on its behalf.
   1354      */
   1355     public final boolean isRingbackRequested() {
   1356         return mRingbackRequested;
   1357     }
   1358 
   1359     /**
   1360      * @return True if the connection's audio mode is VOIP.
   1361      */
   1362     public final boolean getAudioModeIsVoip() {
   1363         return mAudioModeIsVoip;
   1364     }
   1365 
   1366     /**
   1367      * Retrieves the connection start time of the {@code Connnection}, if specified.  A value of
   1368      * {@link Conference#CONNECT_TIME_NOT_SPECIFIED} indicates that Telecom should determine the
   1369      * start time of the conference.
   1370      *
   1371      * @return The time at which the {@code Connnection} was connected.
   1372      *
   1373      * @hide
   1374      */
   1375     public final long getConnectTimeMillis() {
   1376         return mConnectTimeMillis;
   1377     }
   1378 
   1379     /**
   1380      * @return The status hints for this connection.
   1381      */
   1382     public final StatusHints getStatusHints() {
   1383         return mStatusHints;
   1384     }
   1385 
   1386     /**
   1387      * Returns the extras associated with this connection.
   1388      *
   1389      * @return The extras associated with this connection.
   1390      */
   1391     public final Bundle getExtras() {
   1392         Bundle extras = null;
   1393         synchronized (mExtrasLock) {
   1394             if (mExtras != null) {
   1395                 extras = new Bundle(mExtras);
   1396             }
   1397         }
   1398         return extras;
   1399     }
   1400 
   1401     /**
   1402      * Assign a listener to be notified of state changes.
   1403      *
   1404      * @param l A listener.
   1405      * @return This Connection.
   1406      *
   1407      * @hide
   1408      */
   1409     public final Connection addConnectionListener(Listener l) {
   1410         mListeners.add(l);
   1411         return this;
   1412     }
   1413 
   1414     /**
   1415      * Remove a previously assigned listener that was being notified of state changes.
   1416      *
   1417      * @param l A Listener.
   1418      * @return This Connection.
   1419      *
   1420      * @hide
   1421      */
   1422     public final Connection removeConnectionListener(Listener l) {
   1423         if (l != null) {
   1424             mListeners.remove(l);
   1425         }
   1426         return this;
   1427     }
   1428 
   1429     /**
   1430      * @return The {@link DisconnectCause} for this connection.
   1431      */
   1432     public final DisconnectCause getDisconnectCause() {
   1433         return mDisconnectCause;
   1434     }
   1435 
   1436     /**
   1437      * Sets the telecom call ID associated with this Connection.  The Telecom Call ID should be used
   1438      * ONLY for debugging purposes.
   1439      *
   1440      * @param callId The telecom call ID.
   1441      * @hide
   1442      */
   1443     public void setTelecomCallId(String callId) {
   1444         mTelecomCallId = callId;
   1445     }
   1446 
   1447     /**
   1448      * Inform this Connection that the state of its audio output has been changed externally.
   1449      *
   1450      * @param state The new audio state.
   1451      * @hide
   1452      */
   1453     final void setCallAudioState(CallAudioState state) {
   1454         checkImmutable();
   1455         Log.d(this, "setAudioState %s", state);
   1456         mCallAudioState = state;
   1457         onAudioStateChanged(getAudioState());
   1458         onCallAudioStateChanged(state);
   1459     }
   1460 
   1461     /**
   1462      * @param state An integer value of a {@code STATE_*} constant.
   1463      * @return A string representation of the value.
   1464      */
   1465     public static String stateToString(int state) {
   1466         switch (state) {
   1467             case STATE_INITIALIZING:
   1468                 return "INITIALIZING";
   1469             case STATE_NEW:
   1470                 return "NEW";
   1471             case STATE_RINGING:
   1472                 return "RINGING";
   1473             case STATE_DIALING:
   1474                 return "DIALING";
   1475             case STATE_ACTIVE:
   1476                 return "ACTIVE";
   1477             case STATE_HOLDING:
   1478                 return "HOLDING";
   1479             case STATE_DISCONNECTED:
   1480                 return "DISCONNECTED";
   1481             default:
   1482                 Log.wtf(Connection.class, "Unknown state %d", state);
   1483                 return "UNKNOWN";
   1484         }
   1485     }
   1486 
   1487     /**
   1488      * Returns the connection's capabilities, as a bit mask of the {@code CAPABILITY_*} constants.
   1489      */
   1490     public final int getConnectionCapabilities() {
   1491         return mConnectionCapabilities;
   1492     }
   1493 
   1494     /**
   1495      * Returns the connection's properties, as a bit mask of the {@code PROPERTY_*} constants.
   1496      * @hide
   1497      */
   1498     public final int getConnectionProperties() {
   1499         return mConnectionProperties;
   1500     }
   1501 
   1502     /**
   1503      * Sets the value of the {@link #getAddress()} property.
   1504      *
   1505      * @param address The new address.
   1506      * @param presentation The presentation requirements for the address.
   1507      *        See {@link TelecomManager} for valid values.
   1508      */
   1509     public final void setAddress(Uri address, int presentation) {
   1510         checkImmutable();
   1511         Log.d(this, "setAddress %s", address);
   1512         mAddress = address;
   1513         mAddressPresentation = presentation;
   1514         for (Listener l : mListeners) {
   1515             l.onAddressChanged(this, address, presentation);
   1516         }
   1517     }
   1518 
   1519     /**
   1520      * Sets the caller display name (CNAP).
   1521      *
   1522      * @param callerDisplayName The new display name.
   1523      * @param presentation The presentation requirements for the handle.
   1524      *        See {@link TelecomManager} for valid values.
   1525      */
   1526     public final void setCallerDisplayName(String callerDisplayName, int presentation) {
   1527         checkImmutable();
   1528         Log.d(this, "setCallerDisplayName %s", callerDisplayName);
   1529         mCallerDisplayName = callerDisplayName;
   1530         mCallerDisplayNamePresentation = presentation;
   1531         for (Listener l : mListeners) {
   1532             l.onCallerDisplayNameChanged(this, callerDisplayName, presentation);
   1533         }
   1534     }
   1535 
   1536     /**
   1537      * Set the video state for the connection.
   1538      * Valid values: {@link VideoProfile#STATE_AUDIO_ONLY},
   1539      * {@link VideoProfile#STATE_BIDIRECTIONAL},
   1540      * {@link VideoProfile#STATE_TX_ENABLED},
   1541      * {@link VideoProfile#STATE_RX_ENABLED}.
   1542      *
   1543      * @param videoState The new video state.
   1544      */
   1545     public final void setVideoState(int videoState) {
   1546         checkImmutable();
   1547         Log.d(this, "setVideoState %d", videoState);
   1548         mVideoState = videoState;
   1549         for (Listener l : mListeners) {
   1550             l.onVideoStateChanged(this, mVideoState);
   1551         }
   1552     }
   1553 
   1554     /**
   1555      * Sets state to active (e.g., an ongoing connection where two or more parties can actively
   1556      * communicate).
   1557      */
   1558     public final void setActive() {
   1559         checkImmutable();
   1560         setRingbackRequested(false);
   1561         setState(STATE_ACTIVE);
   1562     }
   1563 
   1564     /**
   1565      * Sets state to ringing (e.g., an inbound ringing connection).
   1566      */
   1567     public final void setRinging() {
   1568         checkImmutable();
   1569         setState(STATE_RINGING);
   1570     }
   1571 
   1572     /**
   1573      * Sets state to initializing (this Connection is not yet ready to be used).
   1574      */
   1575     public final void setInitializing() {
   1576         checkImmutable();
   1577         setState(STATE_INITIALIZING);
   1578     }
   1579 
   1580     /**
   1581      * Sets state to initialized (the Connection has been set up and is now ready to be used).
   1582      */
   1583     public final void setInitialized() {
   1584         checkImmutable();
   1585         setState(STATE_NEW);
   1586     }
   1587 
   1588     /**
   1589      * Sets state to dialing (e.g., dialing an outbound connection).
   1590      */
   1591     public final void setDialing() {
   1592         checkImmutable();
   1593         setState(STATE_DIALING);
   1594     }
   1595 
   1596     /**
   1597      * Sets state to be on hold.
   1598      */
   1599     public final void setOnHold() {
   1600         checkImmutable();
   1601         setState(STATE_HOLDING);
   1602     }
   1603 
   1604     /**
   1605      * Sets the video connection provider.
   1606      * @param videoProvider The video provider.
   1607      */
   1608     public final void setVideoProvider(VideoProvider videoProvider) {
   1609         checkImmutable();
   1610         mVideoProvider = videoProvider;
   1611         for (Listener l : mListeners) {
   1612             l.onVideoProviderChanged(this, videoProvider);
   1613         }
   1614     }
   1615 
   1616     public final VideoProvider getVideoProvider() {
   1617         return mVideoProvider;
   1618     }
   1619 
   1620     /**
   1621      * Sets state to disconnected.
   1622      *
   1623      * @param disconnectCause The reason for the disconnection, as specified by
   1624      *         {@link DisconnectCause}.
   1625      */
   1626     public final void setDisconnected(DisconnectCause disconnectCause) {
   1627         checkImmutable();
   1628         mDisconnectCause = disconnectCause;
   1629         setState(STATE_DISCONNECTED);
   1630         Log.d(this, "Disconnected with cause %s", disconnectCause);
   1631         for (Listener l : mListeners) {
   1632             l.onDisconnected(this, disconnectCause);
   1633         }
   1634     }
   1635 
   1636     /**
   1637      * Informs listeners that this {@code Connection} is in a post-dial wait state. This is done
   1638      * when (a) the {@code Connection} is issuing a DTMF sequence; (b) it has encountered a "wait"
   1639      * character; and (c) it wishes to inform the In-Call app that it is waiting for the end-user
   1640      * to send an {@link #onPostDialContinue(boolean)} signal.
   1641      *
   1642      * @param remaining The DTMF character sequence remaining to be emitted once the
   1643      *         {@link #onPostDialContinue(boolean)} is received, including any "wait" characters
   1644      *         that remaining sequence may contain.
   1645      */
   1646     public final void setPostDialWait(String remaining) {
   1647         checkImmutable();
   1648         for (Listener l : mListeners) {
   1649             l.onPostDialWait(this, remaining);
   1650         }
   1651     }
   1652 
   1653     /**
   1654      * Informs listeners that this {@code Connection} has processed a character in the post-dial
   1655      * started state. This is done when (a) the {@code Connection} is issuing a DTMF sequence;
   1656      * and (b) it wishes to signal Telecom to play the corresponding DTMF tone locally.
   1657      *
   1658      * @param nextChar The DTMF character that was just processed by the {@code Connection}.
   1659      */
   1660     public final void setNextPostDialChar(char nextChar) {
   1661         checkImmutable();
   1662         for (Listener l : mListeners) {
   1663             l.onPostDialChar(this, nextChar);
   1664         }
   1665     }
   1666 
   1667     /**
   1668      * Requests that the framework play a ringback tone. This is to be invoked by implementations
   1669      * that do not play a ringback tone themselves in the connection's audio stream.
   1670      *
   1671      * @param ringback Whether the ringback tone is to be played.
   1672      */
   1673     public final void setRingbackRequested(boolean ringback) {
   1674         checkImmutable();
   1675         if (mRingbackRequested != ringback) {
   1676             mRingbackRequested = ringback;
   1677             for (Listener l : mListeners) {
   1678                 l.onRingbackRequested(this, ringback);
   1679             }
   1680         }
   1681     }
   1682 
   1683     /**
   1684      * Sets the connection's capabilities as a bit mask of the {@code CAPABILITY_*} constants.
   1685      *
   1686      * @param connectionCapabilities The new connection capabilities.
   1687      */
   1688     public final void setConnectionCapabilities(int connectionCapabilities) {
   1689         checkImmutable();
   1690         if (mConnectionCapabilities != connectionCapabilities) {
   1691             mConnectionCapabilities = connectionCapabilities;
   1692             for (Listener l : mListeners) {
   1693                 l.onConnectionCapabilitiesChanged(this, mConnectionCapabilities);
   1694             }
   1695         }
   1696     }
   1697 
   1698     /**
   1699      * Sets the connection's properties as a bit mask of the {@code PROPERTY_*} constants.
   1700      *
   1701      * @param connectionProperties The new connection properties.
   1702      * @hide
   1703      */
   1704     public final void setConnectionProperties(int connectionProperties) {
   1705         checkImmutable();
   1706         if (mConnectionProperties != connectionProperties) {
   1707             mConnectionProperties = connectionProperties;
   1708             for (Listener l : mListeners) {
   1709                 l.onConnectionPropertiesChanged(this, mConnectionProperties);
   1710             }
   1711         }
   1712     }
   1713 
   1714     /**
   1715      * Tears down the Connection object.
   1716      */
   1717     public final void destroy() {
   1718         for (Listener l : mListeners) {
   1719             l.onDestroyed(this);
   1720         }
   1721     }
   1722 
   1723     /**
   1724      * Requests that the framework use VOIP audio mode for this connection.
   1725      *
   1726      * @param isVoip True if the audio mode is VOIP.
   1727      */
   1728     public final void setAudioModeIsVoip(boolean isVoip) {
   1729         checkImmutable();
   1730         mAudioModeIsVoip = isVoip;
   1731         for (Listener l : mListeners) {
   1732             l.onAudioModeIsVoipChanged(this, isVoip);
   1733         }
   1734     }
   1735 
   1736     /**
   1737      * Sets the time at which a call became active on this Connection. This is set only
   1738      * when a conference call becomes active on this connection.
   1739      *
   1740      * @param connectionTimeMillis The connection time, in milliseconds.
   1741      *
   1742      * @hide
   1743      */
   1744     public final void setConnectTimeMillis(long connectTimeMillis) {
   1745         mConnectTimeMillis = connectTimeMillis;
   1746     }
   1747 
   1748     /**
   1749      * Sets the label and icon status to display in the in-call UI.
   1750      *
   1751      * @param statusHints The status label and icon to set.
   1752      */
   1753     public final void setStatusHints(StatusHints statusHints) {
   1754         checkImmutable();
   1755         mStatusHints = statusHints;
   1756         for (Listener l : mListeners) {
   1757             l.onStatusHintsChanged(this, statusHints);
   1758         }
   1759     }
   1760 
   1761     /**
   1762      * Sets the connections with which this connection can be conferenced.
   1763      *
   1764      * @param conferenceableConnections The set of connections this connection can conference with.
   1765      */
   1766     public final void setConferenceableConnections(List<Connection> conferenceableConnections) {
   1767         checkImmutable();
   1768         clearConferenceableList();
   1769         for (Connection c : conferenceableConnections) {
   1770             // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a
   1771             // small amount of items here.
   1772             if (!mConferenceables.contains(c)) {
   1773                 c.addConnectionListener(mConnectionDeathListener);
   1774                 mConferenceables.add(c);
   1775             }
   1776         }
   1777         fireOnConferenceableConnectionsChanged();
   1778     }
   1779 
   1780     /**
   1781      * Similar to {@link #setConferenceableConnections(java.util.List)}, sets a list of connections
   1782      * or conferences with which this connection can be conferenced.
   1783      *
   1784      * @param conferenceables The conferenceables.
   1785      */
   1786     public final void setConferenceables(List<Conferenceable> conferenceables) {
   1787         clearConferenceableList();
   1788         for (Conferenceable c : conferenceables) {
   1789             // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a
   1790             // small amount of items here.
   1791             if (!mConferenceables.contains(c)) {
   1792                 if (c instanceof Connection) {
   1793                     Connection connection = (Connection) c;
   1794                     connection.addConnectionListener(mConnectionDeathListener);
   1795                 } else if (c instanceof Conference) {
   1796                     Conference conference = (Conference) c;
   1797                     conference.addListener(mConferenceDeathListener);
   1798                 }
   1799                 mConferenceables.add(c);
   1800             }
   1801         }
   1802         fireOnConferenceableConnectionsChanged();
   1803     }
   1804 
   1805     /**
   1806      * Returns the connections or conferences with which this connection can be conferenced.
   1807      */
   1808     public final List<Conferenceable> getConferenceables() {
   1809         return mUnmodifiableConferenceables;
   1810     }
   1811 
   1812     /**
   1813      * @hide
   1814      */
   1815     public final void setConnectionService(ConnectionService connectionService) {
   1816         checkImmutable();
   1817         if (mConnectionService != null) {
   1818             Log.e(this, new Exception(), "Trying to set ConnectionService on a connection " +
   1819                     "which is already associated with another ConnectionService.");
   1820         } else {
   1821             mConnectionService = connectionService;
   1822         }
   1823     }
   1824 
   1825     /**
   1826      * @hide
   1827      */
   1828     public final void unsetConnectionService(ConnectionService connectionService) {
   1829         if (mConnectionService != connectionService) {
   1830             Log.e(this, new Exception(), "Trying to remove ConnectionService from a Connection " +
   1831                     "that does not belong to the ConnectionService.");
   1832         } else {
   1833             mConnectionService = null;
   1834         }
   1835     }
   1836 
   1837     /**
   1838      * @hide
   1839      */
   1840     public final ConnectionService getConnectionService() {
   1841         return mConnectionService;
   1842     }
   1843 
   1844     /**
   1845      * Sets the conference that this connection is a part of. This will fail if the connection is
   1846      * already part of a conference. {@link #resetConference} to un-set the conference first.
   1847      *
   1848      * @param conference The conference.
   1849      * @return {@code true} if the conference was successfully set.
   1850      * @hide
   1851      */
   1852     public final boolean setConference(Conference conference) {
   1853         checkImmutable();
   1854         // We check to see if it is already part of another conference.
   1855         if (mConference == null) {
   1856             mConference = conference;
   1857             if (mConnectionService != null && mConnectionService.containsConference(conference)) {
   1858                 fireConferenceChanged();
   1859             }
   1860             return true;
   1861         }
   1862         return false;
   1863     }
   1864 
   1865     /**
   1866      * Resets the conference that this connection is a part of.
   1867      * @hide
   1868      */
   1869     public final void resetConference() {
   1870         if (mConference != null) {
   1871             Log.d(this, "Conference reset");
   1872             mConference = null;
   1873             fireConferenceChanged();
   1874         }
   1875     }
   1876 
   1877     /**
   1878      * Set some extras that can be associated with this {@code Connection}.
   1879      * <p>
   1880      * New or existing keys are replaced in the {@code Connection} extras.  Keys which are no longer
   1881      * in the new extras, but were present the last time {@code setExtras} was called are removed.
   1882      * <p>
   1883      * No assumptions should be made as to how an In-Call UI or service will handle these extras.
   1884      * Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
   1885      *
   1886      * @param extras The extras associated with this {@code Connection}.
   1887      */
   1888     public final void setExtras(@Nullable Bundle extras) {
   1889         checkImmutable();
   1890 
   1891         // Add/replace any new or changed extras values.
   1892         putExtras(extras);
   1893 
   1894         // If we have used "setExtras" in the past, compare the key set from the last invocation to
   1895         // the current one and remove any keys that went away.
   1896         if (mPreviousExtraKeys != null) {
   1897             List<String> toRemove = new ArrayList<String>();
   1898             for (String oldKey : mPreviousExtraKeys) {
   1899                 if (extras == null || !extras.containsKey(oldKey)) {
   1900                     toRemove.add(oldKey);
   1901                 }
   1902             }
   1903             if (!toRemove.isEmpty()) {
   1904                 removeExtras(toRemove);
   1905             }
   1906         }
   1907 
   1908         // Track the keys the last time set called setExtras.  This way, the next time setExtras is
   1909         // called we can see if the caller has removed any extras values.
   1910         if (mPreviousExtraKeys == null) {
   1911             mPreviousExtraKeys = new ArraySet<String>();
   1912         }
   1913         mPreviousExtraKeys.clear();
   1914         if (extras != null) {
   1915             mPreviousExtraKeys.addAll(extras.keySet());
   1916         }
   1917     }
   1918 
   1919     /**
   1920      * Adds some extras to this {@code Connection}.  Existing keys are replaced and new ones are
   1921      * added.
   1922      * <p>
   1923      * No assumptions should be made as to how an In-Call UI or service will handle these extras.
   1924      * Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
   1925      *
   1926      * @param extras The extras to add.
   1927      * @hide
   1928      */
   1929     public final void putExtras(@NonNull Bundle extras) {
   1930         checkImmutable();
   1931         if (extras == null) {
   1932             return;
   1933         }
   1934         // Creating a duplicate bundle so we don't have to synchronize on mExtrasLock while calling
   1935         // the listeners.
   1936         Bundle listenerExtras;
   1937         synchronized (mExtrasLock) {
   1938             if (mExtras == null) {
   1939                 mExtras = new Bundle();
   1940             }
   1941             mExtras.putAll(extras);
   1942             listenerExtras = new Bundle(mExtras);
   1943         }
   1944         for (Listener l : mListeners) {
   1945             // Create a new clone of the extras for each listener so that they don't clobber
   1946             // each other
   1947             l.onExtrasChanged(this, new Bundle(listenerExtras));
   1948         }
   1949     }
   1950 
   1951     /**
   1952      * Adds a boolean extra to this {@code Connection}.
   1953      *
   1954      * @param key The extra key.
   1955      * @param value The value.
   1956      * @hide
   1957      */
   1958     public final void putExtra(String key, boolean value) {
   1959         Bundle newExtras = new Bundle();
   1960         newExtras.putBoolean(key, value);
   1961         putExtras(newExtras);
   1962     }
   1963 
   1964     /**
   1965      * Adds an integer extra to this {@code Connection}.
   1966      *
   1967      * @param key The extra key.
   1968      * @param value The value.
   1969      * @hide
   1970      */
   1971     public final void putExtra(String key, int value) {
   1972         Bundle newExtras = new Bundle();
   1973         newExtras.putInt(key, value);
   1974         putExtras(newExtras);
   1975     }
   1976 
   1977     /**
   1978      * Adds a string extra to this {@code Connection}.
   1979      *
   1980      * @param key The extra key.
   1981      * @param value The value.
   1982      * @hide
   1983      */
   1984     public final void putExtra(String key, String value) {
   1985         Bundle newExtras = new Bundle();
   1986         newExtras.putString(key, value);
   1987         putExtras(newExtras);
   1988     }
   1989 
   1990     /**
   1991      * Removes an extra from this {@code Connection}.
   1992      *
   1993      * @param keys The key of the extra key to remove.
   1994      * @hide
   1995      */
   1996     public final void removeExtras(List<String> keys) {
   1997         synchronized (mExtrasLock) {
   1998             if (mExtras != null) {
   1999                 for (String key : keys) {
   2000                     mExtras.remove(key);
   2001                 }
   2002             }
   2003         }
   2004         List<String> unmodifiableKeys = Collections.unmodifiableList(keys);
   2005         for (Listener l : mListeners) {
   2006             l.onExtrasRemoved(this, unmodifiableKeys);
   2007         }
   2008     }
   2009 
   2010     /**
   2011      * Notifies this Connection that the {@link #getAudioState()} property has a new value.
   2012      *
   2013      * @param state The new connection audio state.
   2014      * @deprecated Use {@link #onCallAudioStateChanged(CallAudioState)} instead.
   2015      * @hide
   2016      */
   2017     @SystemApi
   2018     @Deprecated
   2019     public void onAudioStateChanged(AudioState state) {}
   2020 
   2021     /**
   2022      * Notifies this Connection that the {@link #getCallAudioState()} property has a new value.
   2023      *
   2024      * @param state The new connection audio state.
   2025      */
   2026     public void onCallAudioStateChanged(CallAudioState state) {}
   2027 
   2028     /**
   2029      * Notifies this Connection of an internal state change. This method is called after the
   2030      * state is changed.
   2031      *
   2032      * @param state The new state, one of the {@code STATE_*} constants.
   2033      */
   2034     public void onStateChanged(int state) {}
   2035 
   2036     /**
   2037      * Notifies this Connection of a request to play a DTMF tone.
   2038      *
   2039      * @param c A DTMF character.
   2040      */
   2041     public void onPlayDtmfTone(char c) {}
   2042 
   2043     /**
   2044      * Notifies this Connection of a request to stop any currently playing DTMF tones.
   2045      */
   2046     public void onStopDtmfTone() {}
   2047 
   2048     /**
   2049      * Notifies this Connection of a request to disconnect.
   2050      */
   2051     public void onDisconnect() {}
   2052 
   2053     /**
   2054      * Notifies this Connection of a request to disconnect a participant of the conference managed
   2055      * by the connection.
   2056      *
   2057      * @param endpoint the {@link Uri} of the participant to disconnect.
   2058      * @hide
   2059      */
   2060     public void onDisconnectConferenceParticipant(Uri endpoint) {}
   2061 
   2062     /**
   2063      * Notifies this Connection of a request to separate from its parent conference.
   2064      */
   2065     public void onSeparate() {}
   2066 
   2067     /**
   2068      * Notifies this Connection of a request to abort.
   2069      */
   2070     public void onAbort() {}
   2071 
   2072     /**
   2073      * Notifies this Connection of a request to hold.
   2074      */
   2075     public void onHold() {}
   2076 
   2077     /**
   2078      * Notifies this Connection of a request to exit a hold state.
   2079      */
   2080     public void onUnhold() {}
   2081 
   2082     /**
   2083      * Notifies this Connection, which is in {@link #STATE_RINGING}, of
   2084      * a request to accept.
   2085      *
   2086      * @param videoState The video state in which to answer the connection.
   2087      */
   2088     public void onAnswer(int videoState) {}
   2089 
   2090     /**
   2091      * Notifies this Connection, which is in {@link #STATE_RINGING}, of
   2092      * a request to accept.
   2093      */
   2094     public void onAnswer() {
   2095         onAnswer(VideoProfile.STATE_AUDIO_ONLY);
   2096     }
   2097 
   2098     /**
   2099      * Notifies this Connection, which is in {@link #STATE_RINGING}, of
   2100      * a request to reject.
   2101      */
   2102     public void onReject() {}
   2103 
   2104     /**
   2105      * Notifies this Connection, which is in {@link #STATE_RINGING}, of
   2106      * a request to reject with a message.
   2107      */
   2108     public void onReject(String replyMessage) {}
   2109 
   2110     /**
   2111      * Notifies the Connection of a request to silence the ringer.
   2112      *
   2113      * @hide
   2114      */
   2115     public void onSilence() {}
   2116 
   2117     /**
   2118      * Notifies this Connection whether the user wishes to proceed with the post-dial DTMF codes.
   2119      */
   2120     public void onPostDialContinue(boolean proceed) {}
   2121 
   2122     /**
   2123      * Notifies this Connection of a request to pull an external call to the local device.
   2124      * <p>
   2125      * The {@link InCallService} issues a request to pull an external call to the local device via
   2126      * {@link Call#pullExternalCall()}.
   2127      * <p>
   2128      * For a Connection to be pulled, both the {@link Connection#CAPABILITY_CAN_PULL_CALL}
   2129      * capability and {@link Connection#PROPERTY_IS_EXTERNAL_CALL} property bits must be set.
   2130      * <p>
   2131      * For more information on external calls, see {@link Connection#PROPERTY_IS_EXTERNAL_CALL}.
   2132      * @hide
   2133      */
   2134     public void onPullExternalCall() {}
   2135 
   2136     /**
   2137      * Notifies this Connection of a {@link Call} event initiated from an {@link InCallService}.
   2138      * <p>
   2139      * The {@link InCallService} issues a Call event via {@link Call#sendCallEvent(String, Bundle)}.
   2140      * <p>
   2141      * See also {@link Call#sendCallEvent(String, Bundle)}.
   2142      *
   2143      * @param event The call event.
   2144      * @param extras Extras associated with the call event.
   2145      * @hide
   2146      */
   2147     public void onCallEvent(String event, Bundle extras) {}
   2148 
   2149     /**
   2150      * Notifies this {@link Connection} of a change to the extras made outside the
   2151      * {@link ConnectionService}.
   2152      * <p>
   2153      * These extras changes can originate from Telecom itself, or from an {@link InCallService} via
   2154      * the {@link android.telecom.Call#putExtras(Bundle)} and
   2155      * {@link Call#removeExtras(List)}.
   2156      *
   2157      * @param extras The new extras bundle.
   2158      * @hide
   2159      */
   2160     public void onExtrasChanged(Bundle extras) {}
   2161 
   2162     static String toLogSafePhoneNumber(String number) {
   2163         // For unknown number, log empty string.
   2164         if (number == null) {
   2165             return "";
   2166         }
   2167 
   2168         if (PII_DEBUG) {
   2169             // When PII_DEBUG is true we emit PII.
   2170             return number;
   2171         }
   2172 
   2173         // Do exactly same thing as Uri#toSafeString() does, which will enable us to compare
   2174         // sanitized phone numbers.
   2175         StringBuilder builder = new StringBuilder();
   2176         for (int i = 0; i < number.length(); i++) {
   2177             char c = number.charAt(i);
   2178             if (c == '-' || c == '@' || c == '.') {
   2179                 builder.append(c);
   2180             } else {
   2181                 builder.append('x');
   2182             }
   2183         }
   2184         return builder.toString();
   2185     }
   2186 
   2187     private void setState(int state) {
   2188         checkImmutable();
   2189         if (mState == STATE_DISCONNECTED && mState != state) {
   2190             Log.d(this, "Connection already DISCONNECTED; cannot transition out of this state.");
   2191             return;
   2192         }
   2193         if (mState != state) {
   2194             Log.d(this, "setState: %s", stateToString(state));
   2195             mState = state;
   2196             onStateChanged(state);
   2197             for (Listener l : mListeners) {
   2198                 l.onStateChanged(this, state);
   2199             }
   2200         }
   2201     }
   2202 
   2203     private static class FailureSignalingConnection extends Connection {
   2204         private boolean mImmutable = false;
   2205         public FailureSignalingConnection(DisconnectCause disconnectCause) {
   2206             setDisconnected(disconnectCause);
   2207             mImmutable = true;
   2208         }
   2209 
   2210         public void checkImmutable() {
   2211             if (mImmutable) {
   2212                 throw new UnsupportedOperationException("Connection is immutable");
   2213             }
   2214         }
   2215     }
   2216 
   2217     /**
   2218      * Return a {@code Connection} which represents a failed connection attempt. The returned
   2219      * {@code Connection} will have a {@link android.telecom.DisconnectCause} and as specified,
   2220      * and a {@link #getState()} of {@link #STATE_DISCONNECTED}.
   2221      * <p>
   2222      * The returned {@code Connection} can be assumed to {@link #destroy()} itself when appropriate,
   2223      * so users of this method need not maintain a reference to its return value to destroy it.
   2224      *
   2225      * @param disconnectCause The disconnect cause, ({@see android.telecomm.DisconnectCause}).
   2226      * @return A {@code Connection} which indicates failure.
   2227      */
   2228     public static Connection createFailedConnection(DisconnectCause disconnectCause) {
   2229         return new FailureSignalingConnection(disconnectCause);
   2230     }
   2231 
   2232     /**
   2233      * Override to throw an {@link UnsupportedOperationException} if this {@code Connection} is
   2234      * not intended to be mutated, e.g., if it is a marker for failure. Only for framework use;
   2235      * this should never be un-@hide-den.
   2236      *
   2237      * @hide
   2238      */
   2239     public void checkImmutable() {}
   2240 
   2241     /**
   2242      * Return a {@code Connection} which represents a canceled connection attempt. The returned
   2243      * {@code Connection} will have state {@link #STATE_DISCONNECTED}, and cannot be moved out of
   2244      * that state. This connection should not be used for anything, and no other
   2245      * {@code Connection}s should be attempted.
   2246      * <p>
   2247      * so users of this method need not maintain a reference to its return value to destroy it.
   2248      *
   2249      * @return A {@code Connection} which indicates that the underlying connection should
   2250      * be canceled.
   2251      */
   2252     public static Connection createCanceledConnection() {
   2253         return new FailureSignalingConnection(new DisconnectCause(DisconnectCause.CANCELED));
   2254     }
   2255 
   2256     private final void fireOnConferenceableConnectionsChanged() {
   2257         for (Listener l : mListeners) {
   2258             l.onConferenceablesChanged(this, getConferenceables());
   2259         }
   2260     }
   2261 
   2262     private final void fireConferenceChanged() {
   2263         for (Listener l : mListeners) {
   2264             l.onConferenceChanged(this, mConference);
   2265         }
   2266     }
   2267 
   2268     private final void clearConferenceableList() {
   2269         for (Conferenceable c : mConferenceables) {
   2270             if (c instanceof Connection) {
   2271                 Connection connection = (Connection) c;
   2272                 connection.removeConnectionListener(mConnectionDeathListener);
   2273             } else if (c instanceof Conference) {
   2274                 Conference conference = (Conference) c;
   2275                 conference.removeListener(mConferenceDeathListener);
   2276             }
   2277         }
   2278         mConferenceables.clear();
   2279     }
   2280 
   2281     /**
   2282      * Handles a change to extras received from Telecom.
   2283      *
   2284      * @param extras The new extras.
   2285      * @hide
   2286      */
   2287     final void handleExtrasChanged(Bundle extras) {
   2288         Bundle b = null;
   2289         synchronized (mExtrasLock) {
   2290             mExtras = extras;
   2291             if (mExtras != null) {
   2292                 b = new Bundle(mExtras);
   2293             }
   2294         }
   2295         onExtrasChanged(b);
   2296     }
   2297 
   2298     /**
   2299      * Notifies listeners that the merge request failed.
   2300      *
   2301      * @hide
   2302      */
   2303     protected final void notifyConferenceMergeFailed() {
   2304         for (Listener l : mListeners) {
   2305             l.onConferenceMergeFailed(this);
   2306         }
   2307     }
   2308 
   2309     /**
   2310      * Notifies listeners of a change to conference participant(s).
   2311      *
   2312      * @param conferenceParticipants The participants.
   2313      * @hide
   2314      */
   2315     protected final void updateConferenceParticipants(
   2316             List<ConferenceParticipant> conferenceParticipants) {
   2317         for (Listener l : mListeners) {
   2318             l.onConferenceParticipantsChanged(this, conferenceParticipants);
   2319         }
   2320     }
   2321 
   2322     /**
   2323      * Notifies listeners that a conference call has been started.
   2324      * @hide
   2325      */
   2326     protected void notifyConferenceStarted() {
   2327         for (Listener l : mListeners) {
   2328             l.onConferenceStarted();
   2329         }
   2330     }
   2331 
   2332     /**
   2333      * Sends an event associated with this {@code Connection}, with associated event extras.
   2334      *
   2335      * Events are exposed to {@link InCallService} implementations via the
   2336      * {@link Call.Callback#onConnectionEvent(Call, String, Bundle)} API.
   2337      *
   2338      * No assumptions should be made as to how an In-Call UI or service will handle these events.
   2339      * Events should be fully qualified (e.g., com.example.event.MY_EVENT) to avoid conflicts.
   2340      *
   2341      * @param event The connection event.
   2342      * @param extras Bundle containing extra information associated with the event.
   2343      * @hide
   2344      */
   2345     public void sendConnectionEvent(String event, Bundle extras) {
   2346         for (Listener l : mListeners) {
   2347             l.onConnectionEvent(this, event, extras);
   2348         }
   2349     }
   2350 }
   2351