Home | History | Annotate | Download | only in telephony
      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 com.android.services.telephony;
     18 
     19 import android.content.Context;
     20 import android.graphics.drawable.Icon;
     21 import android.net.Uri;
     22 import android.os.AsyncResult;
     23 import android.os.Bundle;
     24 import android.os.Handler;
     25 import android.os.Looper;
     26 import android.os.Message;
     27 import android.os.PersistableBundle;
     28 import android.telecom.CallAudioState;
     29 import android.telecom.ConferenceParticipant;
     30 import android.telecom.Connection;
     31 import android.telecom.PhoneAccount;
     32 import android.telecom.PhoneAccountHandle;
     33 import android.telecom.StatusHints;
     34 import android.telecom.TelecomManager;
     35 import android.telecom.VideoProfile;
     36 import android.telephony.CarrierConfigManager;
     37 import android.telephony.DisconnectCause;
     38 import android.telephony.PhoneNumberUtils;
     39 import android.telephony.TelephonyManager;
     40 import android.telephony.ims.ImsCallProfile;
     41 import android.text.TextUtils;
     42 import android.util.Pair;
     43 
     44 import com.android.ims.ImsCall;
     45 import com.android.internal.telephony.Call;
     46 import com.android.internal.telephony.CallFailCause;
     47 import com.android.internal.telephony.CallStateException;
     48 import com.android.internal.telephony.Connection.Capability;
     49 import com.android.internal.telephony.Connection.PostDialListener;
     50 import com.android.internal.telephony.Phone;
     51 import com.android.internal.telephony.PhoneConstants;
     52 import com.android.internal.telephony.gsm.SuppServiceNotification;
     53 import com.android.internal.telephony.imsphone.ImsPhone;
     54 import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
     55 import com.android.internal.telephony.imsphone.ImsPhoneConnection;
     56 import com.android.phone.ImsUtil;
     57 import com.android.phone.PhoneGlobals;
     58 import com.android.phone.PhoneUtils;
     59 import com.android.phone.R;
     60 
     61 import java.util.ArrayList;
     62 import java.util.Arrays;
     63 import java.util.Collections;
     64 import java.util.HashMap;
     65 import java.util.List;
     66 import java.util.Map;
     67 import java.util.Objects;
     68 import java.util.Set;
     69 import java.util.concurrent.ConcurrentHashMap;
     70 
     71 /**
     72  * Base class for CDMA and GSM connections.
     73  */
     74 abstract class TelephonyConnection extends Connection implements Holdable {
     75     private static final int MSG_PRECISE_CALL_STATE_CHANGED = 1;
     76     private static final int MSG_RINGBACK_TONE = 2;
     77     private static final int MSG_HANDOVER_STATE_CHANGED = 3;
     78     private static final int MSG_DISCONNECT = 4;
     79     private static final int MSG_MULTIPARTY_STATE_CHANGED = 5;
     80     private static final int MSG_CONFERENCE_MERGE_FAILED = 6;
     81     private static final int MSG_SUPP_SERVICE_NOTIFY = 7;
     82 
     83     /**
     84      * Mappings from {@link com.android.internal.telephony.Connection} extras keys to their
     85      * equivalents defined in {@link android.telecom.Connection}.
     86      */
     87     private static final Map<String, String> sExtrasMap = createExtrasMap();
     88 
     89     private static final int MSG_SET_VIDEO_STATE = 8;
     90     private static final int MSG_SET_VIDEO_PROVIDER = 9;
     91     private static final int MSG_SET_AUDIO_QUALITY = 10;
     92     private static final int MSG_SET_CONFERENCE_PARTICIPANTS = 11;
     93     private static final int MSG_CONNECTION_EXTRAS_CHANGED = 12;
     94     private static final int MSG_SET_ORIGNAL_CONNECTION_CAPABILITIES = 13;
     95     private static final int MSG_ON_HOLD_TONE = 14;
     96     private static final int MSG_CDMA_VOICE_PRIVACY_ON = 15;
     97     private static final int MSG_CDMA_VOICE_PRIVACY_OFF = 16;
     98     private static final int MSG_HANGUP = 17;
     99 
    100     private final Handler mHandler = new Handler(Looper.getMainLooper()) {
    101         @Override
    102         public void handleMessage(Message msg) {
    103             switch (msg.what) {
    104                 case MSG_PRECISE_CALL_STATE_CHANGED:
    105                     Log.v(TelephonyConnection.this, "MSG_PRECISE_CALL_STATE_CHANGED");
    106                     updateState();
    107                     break;
    108                 case MSG_HANDOVER_STATE_CHANGED:
    109                     Log.v(TelephonyConnection.this, "MSG_HANDOVER_STATE_CHANGED");
    110                     AsyncResult ar = (AsyncResult) msg.obj;
    111                     com.android.internal.telephony.Connection connection =
    112                          (com.android.internal.telephony.Connection) ar.result;
    113                     if (mOriginalConnection != null) {
    114                         if (connection != null &&
    115                             ((connection.getAddress() != null &&
    116                             mOriginalConnection.getAddress() != null &&
    117                             mOriginalConnection.getAddress().contains(connection.getAddress())) ||
    118                             connection.getState() == mOriginalConnection.getStateBeforeHandover())) {
    119                             Log.d(TelephonyConnection.this,
    120                                     "SettingOriginalConnection " + mOriginalConnection.toString()
    121                                             + " with " + connection.toString());
    122                             setOriginalConnection(connection);
    123                             mWasImsConnection = false;
    124                         }
    125                     } else {
    126                         Log.w(TelephonyConnection.this,
    127                                 "MSG_HANDOVER_STATE_CHANGED: mOriginalConnection==null - invalid state (not cleaned up)");
    128                     }
    129                     break;
    130                 case MSG_RINGBACK_TONE:
    131                     Log.v(TelephonyConnection.this, "MSG_RINGBACK_TONE");
    132                     // TODO: This code assumes that there is only one connection in the foreground
    133                     // call, in other words, it punts on network-mediated conference calling.
    134                     if (getOriginalConnection() != getForegroundConnection()) {
    135                         Log.v(TelephonyConnection.this, "handleMessage, original connection is " +
    136                                 "not foreground connection, skipping");
    137                         return;
    138                     }
    139                     setRingbackRequested((Boolean) ((AsyncResult) msg.obj).result);
    140                     break;
    141                 case MSG_DISCONNECT:
    142                     updateState();
    143                     break;
    144                 case MSG_MULTIPARTY_STATE_CHANGED:
    145                     boolean isMultiParty = (Boolean) msg.obj;
    146                     Log.i(this, "Update multiparty state to %s", isMultiParty ? "Y" : "N");
    147                     mIsMultiParty = isMultiParty;
    148                     if (isMultiParty) {
    149                         notifyConferenceStarted();
    150                     }
    151                     break;
    152                 case MSG_CONFERENCE_MERGE_FAILED:
    153                     notifyConferenceMergeFailed();
    154                     break;
    155                 case MSG_SUPP_SERVICE_NOTIFY:
    156                     Phone phone = getPhone();
    157                     Log.v(TelephonyConnection.this, "MSG_SUPP_SERVICE_NOTIFY on phoneId : "
    158                             + (phone != null ? Integer.toString(phone.getPhoneId())
    159                             : "null"));
    160                     SuppServiceNotification mSsNotification = null;
    161                     if (msg.obj != null && ((AsyncResult) msg.obj).result != null) {
    162                         mSsNotification =
    163                                 (SuppServiceNotification)((AsyncResult) msg.obj).result;
    164                         if (mOriginalConnection != null) {
    165                             handleSuppServiceNotification(mSsNotification);
    166                         }
    167                     }
    168                     break;
    169 
    170                 case MSG_SET_VIDEO_STATE:
    171                     int videoState = (int) msg.obj;
    172                     setVideoState(videoState);
    173 
    174                     // A change to the video state of the call can influence whether or not it
    175                     // can be part of a conference, whether another call can be added, and
    176                     // whether the call should have the HD audio property set.
    177                     refreshConferenceSupported();
    178                     refreshDisableAddCall();
    179                     updateConnectionProperties();
    180                     break;
    181 
    182                 case MSG_SET_VIDEO_PROVIDER:
    183                     VideoProvider videoProvider = (VideoProvider) msg.obj;
    184                     setVideoProvider(videoProvider);
    185                     break;
    186 
    187                 case MSG_SET_AUDIO_QUALITY:
    188                     int audioQuality = (int) msg.obj;
    189                     setAudioQuality(audioQuality);
    190                     break;
    191 
    192                 case MSG_SET_CONFERENCE_PARTICIPANTS:
    193                     List<ConferenceParticipant> participants = (List<ConferenceParticipant>) msg.obj;
    194                     updateConferenceParticipants(participants);
    195                     break;
    196 
    197                 case MSG_CONNECTION_EXTRAS_CHANGED:
    198                     final Bundle extras = (Bundle) msg.obj;
    199                     updateExtras(extras);
    200                     break;
    201 
    202                 case MSG_SET_ORIGNAL_CONNECTION_CAPABILITIES:
    203                     setOriginalConnectionCapabilities(msg.arg1);
    204                     break;
    205 
    206                 case MSG_ON_HOLD_TONE:
    207                     AsyncResult asyncResult = (AsyncResult) msg.obj;
    208                     Pair<com.android.internal.telephony.Connection, Boolean> heldInfo =
    209                             (Pair<com.android.internal.telephony.Connection, Boolean>)
    210                                     asyncResult.result;
    211 
    212                     // Determines if the hold tone is starting or stopping.
    213                     boolean playTone = ((Boolean) (heldInfo.second)).booleanValue();
    214 
    215                     // Determine which connection the hold tone is stopping or starting for
    216                     com.android.internal.telephony.Connection heldConnection = heldInfo.first;
    217 
    218                     // Only start or stop the hold tone if this is the connection which is starting
    219                     // or stopping the hold tone.
    220                     if (heldConnection == mOriginalConnection) {
    221                         // If starting the hold tone, send a connection event to Telecom which will
    222                         // cause it to play the on hold tone.
    223                         if (playTone) {
    224                             sendConnectionEvent(EVENT_ON_HOLD_TONE_START, null);
    225                         } else {
    226                             sendConnectionEvent(EVENT_ON_HOLD_TONE_END, null);
    227                         }
    228                     }
    229                     break;
    230 
    231                 case MSG_CDMA_VOICE_PRIVACY_ON:
    232                     Log.d(this, "MSG_CDMA_VOICE_PRIVACY_ON received");
    233                     setCdmaVoicePrivacy(true);
    234                     break;
    235                 case MSG_CDMA_VOICE_PRIVACY_OFF:
    236                     Log.d(this, "MSG_CDMA_VOICE_PRIVACY_OFF received");
    237                     setCdmaVoicePrivacy(false);
    238                     break;
    239                 case MSG_HANGUP:
    240                     int cause = (int) msg.obj;
    241                     hangup(cause);
    242                     break;
    243             }
    244         }
    245     };
    246 
    247     /**
    248      * Handles {@link SuppServiceNotification}s pertinent to Telephony.
    249      * @param ssn the notification.
    250      */
    251     private void handleSuppServiceNotification(SuppServiceNotification ssn) {
    252         Log.i(this, "handleSuppServiceNotification: type=%d, code=%d", ssn.notificationType,
    253                 ssn.code);
    254         if (ssn.notificationType == SuppServiceNotification.NOTIFICATION_TYPE_CODE_1
    255                 && ssn.code == SuppServiceNotification.CODE_1_CALL_FORWARDED) {
    256             sendConnectionEvent(TelephonyManager.EVENT_CALL_FORWARDED, null);
    257         }
    258         sendSuppServiceNotificationEvent(ssn.notificationType, ssn.code);
    259     }
    260 
    261     /**
    262      * Sends a supplementary service notification connection event.
    263      * This connection event includes the type and code, as well as a human readable message which
    264      * is suitable for display to the user if the UI chooses to do so.
    265      * @param type the {@link SuppServiceNotification#type}.
    266      * @param code the {@link SuppServiceNotification#code}.
    267      */
    268     private void sendSuppServiceNotificationEvent(int type, int code) {
    269         Bundle extras = new Bundle();
    270         extras.putInt(TelephonyManager.EXTRA_NOTIFICATION_TYPE, type);
    271         extras.putInt(TelephonyManager.EXTRA_NOTIFICATION_CODE, code);
    272         extras.putCharSequence(TelephonyManager.EXTRA_NOTIFICATION_MESSAGE,
    273                 getSuppServiceMessage(type, code));
    274         sendConnectionEvent(TelephonyManager.EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION, extras);
    275     }
    276 
    277     /**
    278      * Retrieves a human-readable message for a supplementary service notification.
    279      * This message is suitable for display to the user.
    280      * @param type the code group.
    281      * @param code the code.
    282      * @return A {@link CharSequence} containing the message, or {@code null} if none defined.
    283      */
    284     private CharSequence getSuppServiceMessage(int type, int code) {
    285         int messageId = -1;
    286         if (type == SuppServiceNotification.NOTIFICATION_TYPE_CODE_1) {
    287             switch (code) {
    288                 case SuppServiceNotification.CODE_1_CALL_DEFLECTED:
    289                     messageId = R.string.supp_service_notification_call_deflected;
    290                     break;
    291                 case SuppServiceNotification.CODE_1_CALL_FORWARDED:
    292                     messageId = R.string.supp_service_notification_call_forwarded;
    293                     break;
    294                 case SuppServiceNotification.CODE_1_CALL_IS_WAITING:
    295                     messageId = R.string.supp_service_notification_call_waiting;
    296                     break;
    297                 case SuppServiceNotification.CODE_1_CLIR_SUPPRESSION_REJECTED:
    298                     messageId = R.string.supp_service_clir_suppression_rejected;
    299                     break;
    300                 case SuppServiceNotification.CODE_1_CUG_CALL:
    301                     messageId = R.string.supp_service_closed_user_group_call;
    302                     break;
    303                 case SuppServiceNotification.CODE_1_INCOMING_CALLS_BARRED:
    304                     messageId = R.string.supp_service_incoming_calls_barred;
    305                     break;
    306                 case SuppServiceNotification.CODE_1_OUTGOING_CALLS_BARRED:
    307                     messageId = R.string.supp_service_outgoing_calls_barred;
    308                     break;
    309                 case SuppServiceNotification.CODE_1_SOME_CF_ACTIVE:
    310                     // Intentional fall through.
    311                 case SuppServiceNotification.CODE_1_UNCONDITIONAL_CF_ACTIVE:
    312                     messageId = R.string.supp_service_call_forwarding_active;
    313                     break;
    314             }
    315         } else if (type == SuppServiceNotification.NOTIFICATION_TYPE_CODE_2) {
    316             switch (code) {
    317                 case SuppServiceNotification.CODE_2_ADDITIONAL_CALL_FORWARDED:
    318                     messageId = R.string.supp_service_additional_call_forwarded;
    319                     break;
    320                 case SuppServiceNotification.CODE_2_CALL_CONNECTED_ECT:
    321                     messageId = R.string.supp_service_additional_ect_connected;
    322                     break;
    323                 case SuppServiceNotification.CODE_2_CALL_CONNECTING_ECT:
    324                     messageId = R.string.supp_service_additional_ect_connecting;
    325                     break;
    326                 case SuppServiceNotification.CODE_2_CALL_ON_HOLD:
    327                     messageId = R.string.supp_service_call_on_hold;
    328                     break;
    329                 case SuppServiceNotification.CODE_2_CALL_RETRIEVED:
    330                     messageId = R.string.supp_service_call_resumed;
    331                     break;
    332                 case SuppServiceNotification.CODE_2_CUG_CALL:
    333                     messageId = R.string.supp_service_closed_user_group_call;
    334                     break;
    335                 case SuppServiceNotification.CODE_2_DEFLECTED_CALL:
    336                     messageId = R.string.supp_service_deflected_call;
    337                     break;
    338                 case SuppServiceNotification.CODE_2_FORWARDED_CALL:
    339                     messageId = R.string.supp_service_forwarded_call;
    340                     break;
    341                 case SuppServiceNotification.CODE_2_MULTI_PARTY_CALL:
    342                     messageId = R.string.supp_service_conference_call;
    343                     break;
    344                 case SuppServiceNotification.CODE_2_ON_HOLD_CALL_RELEASED:
    345                     messageId = R.string.supp_service_held_call_released;
    346                     break;
    347             }
    348         }
    349         if (messageId != -1 && getPhone() != null && getPhone().getContext() != null) {
    350             return getPhone().getContext().getText(messageId);
    351         } else {
    352             return null;
    353         }
    354     }
    355 
    356     /**
    357      * @return {@code true} if carrier video conferencing is supported, {@code false} otherwise.
    358      */
    359     public boolean isCarrierVideoConferencingSupported() {
    360         return mIsCarrierVideoConferencingSupported;
    361     }
    362 
    363     /**
    364      * A listener/callback mechanism that is specific communication from TelephonyConnections
    365      * to TelephonyConnectionService (for now). It is more specific that Connection.Listener
    366      * because it is only exposed in Telephony.
    367      */
    368     public abstract static class TelephonyConnectionListener {
    369         public void onOriginalConnectionConfigured(TelephonyConnection c) {}
    370         public void onOriginalConnectionRetry(TelephonyConnection c, boolean isPermanentFailure) {}
    371     }
    372 
    373     private final PostDialListener mPostDialListener = new PostDialListener() {
    374         @Override
    375         public void onPostDialWait() {
    376             Log.v(TelephonyConnection.this, "onPostDialWait");
    377             if (mOriginalConnection != null) {
    378                 setPostDialWait(mOriginalConnection.getRemainingPostDialString());
    379             }
    380         }
    381 
    382         @Override
    383         public void onPostDialChar(char c) {
    384             Log.v(TelephonyConnection.this, "onPostDialChar: %s", c);
    385             if (mOriginalConnection != null) {
    386                 setNextPostDialChar(c);
    387             }
    388         }
    389     };
    390 
    391     /**
    392      * Listener for listening to events in the {@link com.android.internal.telephony.Connection}.
    393      */
    394     private final com.android.internal.telephony.Connection.Listener mOriginalConnectionListener =
    395             new com.android.internal.telephony.Connection.ListenerBase() {
    396         @Override
    397         public void onVideoStateChanged(int videoState) {
    398             mHandler.obtainMessage(MSG_SET_VIDEO_STATE, videoState).sendToTarget();
    399         }
    400 
    401         /*
    402          * The {@link com.android.internal.telephony.Connection} has reported a change in
    403          * connection capability.
    404          * @param capabilities bit mask containing voice or video or both capabilities.
    405          */
    406         @Override
    407         public void onConnectionCapabilitiesChanged(int capabilities) {
    408             mHandler.obtainMessage(MSG_SET_ORIGNAL_CONNECTION_CAPABILITIES,
    409                     capabilities, 0).sendToTarget();
    410         }
    411 
    412         /**
    413          * The {@link com.android.internal.telephony.Connection} has reported a change in the
    414          * video call provider.
    415          *
    416          * @param videoProvider The video call provider.
    417          */
    418         @Override
    419         public void onVideoProviderChanged(VideoProvider videoProvider) {
    420             mHandler.obtainMessage(MSG_SET_VIDEO_PROVIDER, videoProvider).sendToTarget();
    421         }
    422 
    423         /**
    424          * Used by {@link com.android.internal.telephony.Connection} to report a change in whether
    425          * the call is being made over a wifi network.
    426          *
    427          * @param isWifi True if call is made over wifi.
    428          */
    429         @Override
    430         public void onWifiChanged(boolean isWifi) {
    431             setWifi(isWifi);
    432         }
    433 
    434         /**
    435          * Used by the {@link com.android.internal.telephony.Connection} to report a change in the
    436          * audio quality for the current call.
    437          *
    438          * @param audioQuality The audio quality.
    439          */
    440         @Override
    441         public void onAudioQualityChanged(int audioQuality) {
    442             mHandler.obtainMessage(MSG_SET_AUDIO_QUALITY, audioQuality).sendToTarget();
    443         }
    444         /**
    445          * Handles a change in the state of conference participant(s), as reported by the
    446          * {@link com.android.internal.telephony.Connection}.
    447          *
    448          * @param participants The participant(s) which changed.
    449          */
    450         @Override
    451         public void onConferenceParticipantsChanged(List<ConferenceParticipant> participants) {
    452             mHandler.obtainMessage(MSG_SET_CONFERENCE_PARTICIPANTS, participants).sendToTarget();
    453         }
    454 
    455         /*
    456          * Handles a change to the multiparty state for this connection.
    457          *
    458          * @param isMultiParty {@code true} if the call became multiparty, {@code false}
    459          *      otherwise.
    460          */
    461         @Override
    462         public void onMultipartyStateChanged(boolean isMultiParty) {
    463             handleMultipartyStateChange(isMultiParty);
    464         }
    465 
    466         /**
    467          * Handles the event that the request to merge calls failed.
    468          */
    469         @Override
    470         public void onConferenceMergedFailed() {
    471             handleConferenceMergeFailed();
    472         }
    473 
    474         @Override
    475         public void onExtrasChanged(Bundle extras) {
    476             mHandler.obtainMessage(MSG_CONNECTION_EXTRAS_CHANGED, extras).sendToTarget();
    477         }
    478 
    479         /**
    480          * Handles the phone exiting ECM mode by updating the connection capabilities.  During an
    481          * ongoing call, if ECM mode is exited, we will re-enable mute for CDMA calls.
    482          */
    483         @Override
    484         public void onExitedEcmMode() {
    485             handleExitedEcmMode();
    486         }
    487 
    488         /**
    489          * Called from {@link ImsPhoneCallTracker} when a request to pull an external call has
    490          * failed.
    491          * @param externalConnection
    492          */
    493         @Override
    494         public void onCallPullFailed(com.android.internal.telephony.Connection externalConnection) {
    495             if (externalConnection == null) {
    496                 return;
    497             }
    498 
    499             Log.i(this, "onCallPullFailed - pull failed; swapping back to call: %s",
    500                     externalConnection);
    501 
    502             // Inform the InCallService of the fact that the call pull failed (it may choose to
    503             // display a message informing the user of the pull failure).
    504             sendConnectionEvent(Connection.EVENT_CALL_PULL_FAILED, null);
    505 
    506             // Swap the ImsPhoneConnection we used to do the pull for the ImsExternalConnection
    507             // which originally represented the call.
    508             setOriginalConnection(externalConnection);
    509 
    510             // Set our state to active again since we're no longer pulling.
    511             setActiveInternal();
    512         }
    513 
    514         /**
    515          * Called from {@link ImsPhoneCallTracker} when a handover to WIFI has failed.
    516          */
    517         @Override
    518         public void onHandoverToWifiFailed() {
    519             sendConnectionEvent(TelephonyManager.EVENT_HANDOVER_TO_WIFI_FAILED, null);
    520         }
    521 
    522         /**
    523          * Informs the {@link android.telecom.ConnectionService} of a connection event raised by the
    524          * original connection.
    525          * @param event The connection event.
    526          * @param extras The extras.
    527          */
    528         @Override
    529         public void onConnectionEvent(String event, Bundle extras) {
    530             sendConnectionEvent(event, extras);
    531         }
    532 
    533         @Override
    534         public void onRttModifyRequestReceived() {
    535             sendRemoteRttRequest();
    536         }
    537 
    538         @Override
    539         public void onRttModifyResponseReceived(int status) {
    540             updateConnectionProperties();
    541             if (status == RttModifyStatus.SESSION_MODIFY_REQUEST_SUCCESS) {
    542                 sendRttInitiationSuccess();
    543             } else {
    544                 sendRttInitiationFailure(status);
    545             }
    546         }
    547 
    548         @Override
    549         public void onDisconnect(int cause) {
    550             Log.i(this, "onDisconnect: callId=%s, cause=%s", getTelecomCallId(),
    551                     DisconnectCause.toString(cause));
    552             mHandler.obtainMessage(MSG_DISCONNECT).sendToTarget();
    553         }
    554 
    555         @Override
    556         public void onRttInitiated() {
    557             updateConnectionProperties();
    558             sendRttInitiationSuccess();
    559         }
    560 
    561         @Override
    562         public void onRttTerminated() {
    563             updateConnectionProperties();
    564             sendRttSessionRemotelyTerminated();
    565         }
    566     };
    567 
    568     protected com.android.internal.telephony.Connection mOriginalConnection;
    569     private Call.State mConnectionState = Call.State.IDLE;
    570     private Bundle mOriginalConnectionExtras = new Bundle();
    571     private boolean mIsStateOverridden = false;
    572     private Call.State mOriginalConnectionState = Call.State.IDLE;
    573     private Call.State mConnectionOverriddenState = Call.State.IDLE;
    574     private RttTextStream mRttTextStream = null;
    575 
    576     private boolean mWasImsConnection;
    577 
    578     /**
    579      * Tracks the multiparty state of the ImsCall so that changes in the bit state can be detected.
    580      */
    581     private boolean mIsMultiParty = false;
    582 
    583     /**
    584      * The {@link com.android.internal.telephony.Connection} capabilities associated with the
    585      * current {@link #mOriginalConnection}.
    586      */
    587     private int mOriginalConnectionCapabilities;
    588 
    589     /**
    590      * Determines if the {@link TelephonyConnection} is using wifi.
    591      * This is used when {@link TelephonyConnection#updateConnectionProperties()} is called to
    592      * indicate whether a call has the {@link Connection#PROPERTY_WIFI} property.
    593      */
    594     private boolean mIsWifi;
    595 
    596     /**
    597      * Determines the audio quality is high for the {@link TelephonyConnection}.
    598      * This is used when {@link TelephonyConnection#updateConnectionProperties}} is called to
    599      * indicate whether a call has the {@link Connection#PROPERTY_HIGH_DEF_AUDIO} property.
    600      */
    601     private boolean mHasHighDefAudio;
    602 
    603     /**
    604      * Indicates that the connection should be treated as an emergency call because the
    605      * number dialed matches an internal list of emergency numbers. Does not guarantee whether
    606      * the network will treat the call as an emergency call.
    607      */
    608     private boolean mTreatAsEmergencyCall;
    609 
    610     /**
    611      * For video calls, indicates whether the outgoing video for the call can be paused using
    612      * the {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState.
    613      */
    614     private boolean mIsVideoPauseSupported;
    615 
    616     /**
    617      * Indicates whether this connection supports being a part of a conference..
    618      */
    619     private boolean mIsConferenceSupported;
    620 
    621     /**
    622      * Indicates whether managing conference call is supported after this connection being
    623      * a part of a IMS conference.
    624      */
    625     private boolean mIsManageImsConferenceCallSupported;
    626 
    627     /**
    628      * Indicates whether the carrier supports video conferencing; captures the current state of the
    629      * carrier config
    630      * {@link android.telephony.CarrierConfigManager#KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL}.
    631      */
    632     private boolean mIsCarrierVideoConferencingSupported;
    633 
    634     /**
    635      * Indicates whether or not this connection has CDMA Enhanced Voice Privacy enabled.
    636      */
    637     private boolean mIsCdmaVoicePrivacyEnabled;
    638 
    639     /**
    640      * Indicates whether this call is an outgoing call.
    641      */
    642     protected final boolean mIsOutgoing;
    643 
    644     /**
    645      * Indicates whether the connection can be held. This filed combined with the state of the
    646      * connection can determine whether {@link Connection#CAPABILITY_HOLD} should be added to the
    647      * connection.
    648      */
    649     private boolean mIsHoldable;
    650 
    651     /**
    652      * Indicates whether this call is using assisted dialing.
    653      */
    654     private boolean mIsUsingAssistedDialing;
    655 
    656     /**
    657      * Indicates whether this connection supports showing preciese call failed cause.
    658      */
    659     private boolean mShowPreciseFailedCause;
    660 
    661     /**
    662      * Listeners to our TelephonyConnection specific callbacks
    663      */
    664     private final Set<TelephonyConnectionListener> mTelephonyListeners = Collections.newSetFromMap(
    665             new ConcurrentHashMap<TelephonyConnectionListener, Boolean>(8, 0.9f, 1));
    666 
    667     protected TelephonyConnection(com.android.internal.telephony.Connection originalConnection,
    668             String callId, boolean isOutgoingCall) {
    669         mIsOutgoing = isOutgoingCall;
    670         setTelecomCallId(callId);
    671         if (originalConnection != null) {
    672             setOriginalConnection(originalConnection);
    673         }
    674     }
    675 
    676     /**
    677      * Creates a clone of the current {@link TelephonyConnection}.
    678      *
    679      * @return The clone.
    680      */
    681     public abstract TelephonyConnection cloneConnection();
    682 
    683     @Override
    684     public void onCallAudioStateChanged(CallAudioState audioState) {
    685         // TODO: update TTY mode.
    686         if (getPhone() != null) {
    687             getPhone().setEchoSuppressionEnabled();
    688         }
    689     }
    690 
    691     @Override
    692     public void onStateChanged(int state) {
    693         Log.v(this, "onStateChanged, state: " + Connection.stateToString(state));
    694         updateStatusHints();
    695     }
    696 
    697     @Override
    698     public void onDisconnect() {
    699         Log.v(this, "onDisconnect");
    700         mHandler.obtainMessage(MSG_HANGUP, android.telephony.DisconnectCause.LOCAL).sendToTarget();
    701     }
    702 
    703     /**
    704      * Notifies this Connection of a request to disconnect a participant of the conference managed
    705      * by the connection.
    706      *
    707      * @param endpoint the {@link Uri} of the participant to disconnect.
    708      */
    709     @Override
    710     public void onDisconnectConferenceParticipant(Uri endpoint) {
    711         Log.v(this, "onDisconnectConferenceParticipant %s", endpoint);
    712 
    713         if (mOriginalConnection == null) {
    714             return;
    715         }
    716 
    717         mOriginalConnection.onDisconnectConferenceParticipant(endpoint);
    718     }
    719 
    720     @Override
    721     public void onSeparate() {
    722         Log.v(this, "onSeparate");
    723         if (mOriginalConnection != null) {
    724             try {
    725                 mOriginalConnection.separate();
    726             } catch (CallStateException e) {
    727                 Log.e(this, e, "Call to Connection.separate failed with exception");
    728             }
    729         }
    730     }
    731 
    732     @Override
    733     public void onAbort() {
    734         Log.v(this, "onAbort");
    735         mHandler.obtainMessage(MSG_HANGUP, android.telephony.DisconnectCause.LOCAL).sendToTarget();
    736     }
    737 
    738     @Override
    739     public void onHold() {
    740         performHold();
    741     }
    742 
    743     @Override
    744     public void onUnhold() {
    745         performUnhold();
    746     }
    747 
    748     @Override
    749     public void onAnswer(int videoState) {
    750         Log.v(this, "onAnswer");
    751         if (isValidRingingCall() && getPhone() != null) {
    752             try {
    753                 getPhone().acceptCall(videoState);
    754             } catch (CallStateException e) {
    755                 Log.e(this, e, "Failed to accept call.");
    756             }
    757         }
    758     }
    759 
    760     @Override
    761     public void onDeflect(Uri address) {
    762         Log.v(this, "onDeflect");
    763         if (mOriginalConnection != null && isValidRingingCall()) {
    764             if (address == null) {
    765                 Log.w(this, "call deflect address uri is null");
    766                 return;
    767             }
    768             String scheme = address.getScheme();
    769             String deflectNumber = "";
    770             String uriString = address.getSchemeSpecificPart();
    771             if (!PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) {
    772                 if (!PhoneAccount.SCHEME_TEL.equals(scheme)) {
    773                     Log.w(this, "onDeflect, address scheme is not of type tel instead: " +
    774                             scheme);
    775                     return;
    776                 }
    777                 if (PhoneNumberUtils.isUriNumber(uriString)) {
    778                     Log.w(this, "Invalid deflect address. Not a legal PSTN number.");
    779                     return;
    780                 }
    781                 deflectNumber = PhoneNumberUtils.convertAndStrip(uriString);
    782                 if (TextUtils.isEmpty(deflectNumber)) {
    783                     Log.w(this, "Empty deflect number obtained from address uri");
    784                     return;
    785                 }
    786             } else {
    787                 Log.w(this, "Cannot deflect to voicemail uri");
    788                 return;
    789             }
    790 
    791             try {
    792                 mOriginalConnection.deflect(deflectNumber);
    793             } catch (CallStateException e) {
    794                 Log.e(this, e, "Failed to deflect call.");
    795             }
    796         }
    797     }
    798 
    799     @Override
    800     public void onReject() {
    801         Log.v(this, "onReject");
    802         if (isValidRingingCall()) {
    803             mHandler.obtainMessage(MSG_HANGUP, android.telephony.DisconnectCause.INCOMING_REJECTED)
    804                     .sendToTarget();
    805         }
    806         super.onReject();
    807     }
    808 
    809     @Override
    810     public void onPostDialContinue(boolean proceed) {
    811         Log.v(this, "onPostDialContinue, proceed: " + proceed);
    812         if (mOriginalConnection != null) {
    813             if (proceed) {
    814                 mOriginalConnection.proceedAfterWaitChar();
    815             } else {
    816                 mOriginalConnection.cancelPostDial();
    817             }
    818         }
    819     }
    820 
    821     /**
    822      * Handles requests to pull an external call.
    823      */
    824     @Override
    825     public void onPullExternalCall() {
    826         if ((getConnectionProperties() & Connection.PROPERTY_IS_EXTERNAL_CALL) !=
    827                 Connection.PROPERTY_IS_EXTERNAL_CALL) {
    828             Log.w(this, "onPullExternalCall - cannot pull non-external call");
    829             return;
    830         }
    831 
    832         if (mOriginalConnection != null) {
    833             mOriginalConnection.pullExternalCall();
    834         }
    835     }
    836 
    837     @Override
    838     public void onStartRtt(RttTextStream textStream) {
    839         if (isImsConnection()) {
    840             ImsPhoneConnection originalConnection = (ImsPhoneConnection) mOriginalConnection;
    841             if (originalConnection.isRttEnabledForCall()) {
    842                 originalConnection.setCurrentRttTextStream(textStream);
    843             } else {
    844                 originalConnection.sendRttModifyRequest(textStream);
    845             }
    846         } else {
    847             Log.w(this, "onStartRtt - not in IMS, so RTT cannot be enabled.");
    848         }
    849     }
    850 
    851     @Override
    852     public void onStopRtt() {
    853         Log.i(this, "Stopping RTT currently not supported. Doing nothing.");
    854     }
    855 
    856     @Override
    857     public void handleRttUpgradeResponse(RttTextStream textStream) {
    858         if (!isImsConnection()) {
    859             Log.w(this, "handleRttUpgradeResponse - not in IMS, so RTT cannot be enabled.");
    860             return;
    861         }
    862         ImsPhoneConnection originalConnection = (ImsPhoneConnection) mOriginalConnection;
    863         originalConnection.sendRttModifyResponse(textStream);
    864     }
    865 
    866     public void performHold() {
    867         Log.v(this, "performHold");
    868         // TODO: Can dialing calls be put on hold as well since they take up the
    869         // foreground call slot?
    870         if (Call.State.ACTIVE == mConnectionState) {
    871             Log.v(this, "Holding active call");
    872             try {
    873                 Phone phone = mOriginalConnection.getCall().getPhone();
    874                 Call ringingCall = phone.getRingingCall();
    875 
    876                 // Although the method says switchHoldingAndActive, it eventually calls a RIL method
    877                 // called switchWaitingOrHoldingAndActive. What this means is that if we try to put
    878                 // a call on hold while a call-waiting call exists, it'll end up accepting the
    879                 // call-waiting call, which is bad if that was not the user's intention. We are
    880                 // cheating here and simply skipping it because we know any attempt to hold a call
    881                 // while a call-waiting call is happening is likely a request from Telecom prior to
    882                 // accepting the call-waiting call.
    883                 // TODO: Investigate a better solution. It would be great here if we
    884                 // could "fake" hold by silencing the audio and microphone streams for this call
    885                 // instead of actually putting it on hold.
    886                 if (ringingCall.getState() != Call.State.WAITING) {
    887                     phone.switchHoldingAndActive();
    888                 }
    889 
    890                 // TODO: Cdma calls are slightly different.
    891             } catch (CallStateException e) {
    892                 Log.e(this, e, "Exception occurred while trying to put call on hold.");
    893             }
    894         } else {
    895             Log.w(this, "Cannot put a call that is not currently active on hold.");
    896         }
    897     }
    898 
    899     public void performUnhold() {
    900         Log.v(this, "performUnhold");
    901         if (Call.State.HOLDING == mConnectionState) {
    902             try {
    903                 // Here's the deal--Telephony hold/unhold is weird because whenever there exists
    904                 // more than one call, one of them must always be active. In other words, if you
    905                 // have an active call and holding call, and you put the active call on hold, it
    906                 // will automatically activate the holding call. This is weird with how Telecom
    907                 // sends its commands. When a user opts to "unhold" a background call, telecom
    908                 // issues hold commands to all active calls, and then the unhold command to the
    909                 // background call. This means that we get two commands...each of which reduces to
    910                 // switchHoldingAndActive(). The result is that they simply cancel each other out.
    911                 // To fix this so that it works well with telecom we add a minor hack. If we
    912                 // have one telephony call, everything works as normally expected. But if we have
    913                 // two or more calls, we will ignore all requests to "unhold" knowing that the hold
    914                 // requests already do what we want. If you've read up to this point, I'm very sorry
    915                 // that we are doing this. I didn't think of a better solution that wouldn't also
    916                 // make the Telecom APIs very ugly.
    917 
    918                 if (!hasMultipleTopLevelCalls()) {
    919                     mOriginalConnection.getCall().getPhone().switchHoldingAndActive();
    920                 } else {
    921                     Log.i(this, "Skipping unhold command for %s", this);
    922                 }
    923             } catch (CallStateException e) {
    924                 Log.e(this, e, "Exception occurred while trying to release call from hold.");
    925             }
    926         } else {
    927             Log.w(this, "Cannot release a call that is not already on hold from hold.");
    928         }
    929     }
    930 
    931     public void performConference(Connection otherConnection) {
    932         Log.d(this, "performConference - %s", this);
    933         if (getPhone() != null) {
    934             try {
    935                 // We dont use the "other" connection because there is no concept of that in the
    936                 // implementation of calls inside telephony. Basically, you can "conference" and it
    937                 // will conference with the background call.  We know that otherConnection is the
    938                 // background call because it would never have called setConferenceableConnections()
    939                 // otherwise.
    940                 getPhone().conference();
    941             } catch (CallStateException e) {
    942                 Log.e(this, e, "Failed to conference call.");
    943             }
    944         }
    945     }
    946 
    947     /**
    948      * Builds connection capabilities common to all TelephonyConnections. Namely, apply IMS-based
    949      * capabilities.
    950      */
    951     protected int buildConnectionCapabilities() {
    952         int callCapabilities = 0;
    953         if (mOriginalConnection != null && mOriginalConnection.isIncoming()) {
    954             callCapabilities |= CAPABILITY_SPEED_UP_MT_AUDIO;
    955         }
    956         if (!shouldTreatAsEmergencyCall() && isImsConnection() && canHoldImsCalls()) {
    957             callCapabilities |= CAPABILITY_SUPPORT_HOLD;
    958             if (mIsHoldable && (getState() == STATE_ACTIVE || getState() == STATE_HOLDING)) {
    959                 callCapabilities |= CAPABILITY_HOLD;
    960             }
    961         }
    962 
    963         Log.d(this, "buildConnectionCapabilities: isHoldable = "
    964                 + mIsHoldable + " State = " + getState() + " capabilities = " + callCapabilities);
    965 
    966         return callCapabilities;
    967     }
    968 
    969     protected final void updateConnectionCapabilities() {
    970         int newCapabilities = buildConnectionCapabilities();
    971 
    972         newCapabilities = applyOriginalConnectionCapabilities(newCapabilities);
    973         newCapabilities = changeBitmask(newCapabilities, CAPABILITY_CAN_PAUSE_VIDEO,
    974                 mIsVideoPauseSupported && isVideoCapable());
    975         newCapabilities = changeBitmask(newCapabilities, CAPABILITY_CAN_PULL_CALL,
    976                 isExternalConnection() && isPullable());
    977         newCapabilities = applyConferenceTerminationCapabilities(newCapabilities);
    978         newCapabilities = changeBitmask(newCapabilities, CAPABILITY_SUPPORT_DEFLECT,
    979                 isImsConnection() && canDeflectImsCalls());
    980 
    981         if (getConnectionCapabilities() != newCapabilities) {
    982             setConnectionCapabilities(newCapabilities);
    983         }
    984     }
    985 
    986     protected int buildConnectionProperties() {
    987         int connectionProperties = 0;
    988 
    989         // If the phone is in ECM mode, mark the call to indicate that the callback number should be
    990         // shown.
    991         Phone phone = getPhone();
    992         if (phone != null && phone.isInEcm()) {
    993             connectionProperties |= PROPERTY_EMERGENCY_CALLBACK_MODE;
    994         }
    995 
    996         return connectionProperties;
    997     }
    998 
    999     /**
   1000      * Updates the properties of the connection.
   1001      */
   1002     protected final void updateConnectionProperties() {
   1003         int newProperties = buildConnectionProperties();
   1004 
   1005         newProperties = changeBitmask(newProperties, PROPERTY_HIGH_DEF_AUDIO,
   1006                 hasHighDefAudioProperty());
   1007         newProperties = changeBitmask(newProperties, PROPERTY_WIFI, mIsWifi);
   1008         newProperties = changeBitmask(newProperties, PROPERTY_IS_EXTERNAL_CALL,
   1009                 isExternalConnection());
   1010         newProperties = changeBitmask(newProperties, PROPERTY_HAS_CDMA_VOICE_PRIVACY,
   1011                 mIsCdmaVoicePrivacyEnabled);
   1012         newProperties = changeBitmask(newProperties, PROPERTY_ASSISTED_DIALING_USED,
   1013                 mIsUsingAssistedDialing);
   1014         newProperties = changeBitmask(newProperties, PROPERTY_IS_RTT, isRtt());
   1015 
   1016         if (getConnectionProperties() != newProperties) {
   1017             setConnectionProperties(newProperties);
   1018         }
   1019     }
   1020 
   1021     protected final void updateAddress() {
   1022         updateConnectionCapabilities();
   1023         updateConnectionProperties();
   1024         if (mOriginalConnection != null) {
   1025             Uri address = getAddressFromNumber(mOriginalConnection.getAddress());
   1026             int presentation = mOriginalConnection.getNumberPresentation();
   1027             if (!Objects.equals(address, getAddress()) ||
   1028                     presentation != getAddressPresentation()) {
   1029                 Log.v(this, "updateAddress, address changed");
   1030                 if ((getConnectionProperties() & PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0) {
   1031                     address = null;
   1032                 }
   1033                 setAddress(address, presentation);
   1034             }
   1035 
   1036             String name = filterCnapName(mOriginalConnection.getCnapName());
   1037             int namePresentation = mOriginalConnection.getCnapNamePresentation();
   1038             if (!Objects.equals(name, getCallerDisplayName()) ||
   1039                     namePresentation != getCallerDisplayNamePresentation()) {
   1040                 Log.v(this, "updateAddress, caller display name changed");
   1041                 setCallerDisplayName(name, namePresentation);
   1042             }
   1043 
   1044             if (PhoneNumberUtils.isEmergencyNumber(mOriginalConnection.getAddress())) {
   1045                 mTreatAsEmergencyCall = true;
   1046             }
   1047 
   1048             // Changing the address of the connection can change whether it is an emergency call or
   1049             // not, which can impact whether it can be part of a conference.
   1050             refreshConferenceSupported();
   1051         }
   1052     }
   1053 
   1054     void onRemovedFromCallService() {
   1055         // Subclass can override this to do cleanup.
   1056     }
   1057 
   1058     void setOriginalConnection(com.android.internal.telephony.Connection originalConnection) {
   1059         Log.v(this, "new TelephonyConnection, originalConnection: " + originalConnection);
   1060         clearOriginalConnection();
   1061         mOriginalConnectionExtras.clear();
   1062         mOriginalConnection = originalConnection;
   1063         mOriginalConnection.setTelecomCallId(getTelecomCallId());
   1064         getPhone().registerForPreciseCallStateChanged(
   1065                 mHandler, MSG_PRECISE_CALL_STATE_CHANGED, null);
   1066         getPhone().registerForHandoverStateChanged(
   1067                 mHandler, MSG_HANDOVER_STATE_CHANGED, null);
   1068         getPhone().registerForRingbackTone(mHandler, MSG_RINGBACK_TONE, null);
   1069         getPhone().registerForSuppServiceNotification(mHandler, MSG_SUPP_SERVICE_NOTIFY, null);
   1070         getPhone().registerForOnHoldTone(mHandler, MSG_ON_HOLD_TONE, null);
   1071         getPhone().registerForInCallVoicePrivacyOn(mHandler, MSG_CDMA_VOICE_PRIVACY_ON, null);
   1072         getPhone().registerForInCallVoicePrivacyOff(mHandler, MSG_CDMA_VOICE_PRIVACY_OFF, null);
   1073         mOriginalConnection.addPostDialListener(mPostDialListener);
   1074         mOriginalConnection.addListener(mOriginalConnectionListener);
   1075 
   1076         // Set video state and capabilities
   1077         setVideoState(mOriginalConnection.getVideoState());
   1078         setOriginalConnectionCapabilities(mOriginalConnection.getConnectionCapabilities());
   1079         setWifi(mOriginalConnection.isWifi());
   1080         setAudioModeIsVoip(mOriginalConnection.getAudioModeIsVoip());
   1081         setVideoProvider(mOriginalConnection.getVideoProvider());
   1082         setAudioQuality(mOriginalConnection.getAudioQuality());
   1083         setTechnologyTypeExtra();
   1084 
   1085         // Post update of extras to the handler; extras are updated via the handler to ensure thread
   1086         // safety. The Extras Bundle is cloned in case the original extras are modified while they
   1087         // are being added to mOriginalConnectionExtras in updateExtras.
   1088         Bundle connExtras = mOriginalConnection.getConnectionExtras();
   1089             mHandler.obtainMessage(MSG_CONNECTION_EXTRAS_CHANGED, connExtras == null ? null :
   1090                     new Bundle(connExtras)).sendToTarget();
   1091 
   1092         if (PhoneNumberUtils.isEmergencyNumber(mOriginalConnection.getAddress())) {
   1093             mTreatAsEmergencyCall = true;
   1094         }
   1095 
   1096         if (isImsConnection()) {
   1097             mWasImsConnection = true;
   1098         }
   1099         mIsMultiParty = mOriginalConnection.isMultiparty();
   1100 
   1101         Bundle extrasToPut = new Bundle();
   1102         List<String> extrasToRemove = new ArrayList<>();
   1103         if (mOriginalConnection.isActiveCallDisconnectedOnAnswer()) {
   1104             extrasToPut.putBoolean(Connection.EXTRA_ANSWERING_DROPS_FG_CALL, true);
   1105         } else {
   1106             extrasToRemove.add(Connection.EXTRA_ANSWERING_DROPS_FG_CALL);
   1107         }
   1108 
   1109         if (shouldSetDisableAddCallExtra()) {
   1110             extrasToPut.putBoolean(Connection.EXTRA_DISABLE_ADD_CALL, true);
   1111         } else {
   1112             extrasToRemove.add(Connection.EXTRA_DISABLE_ADD_CALL);
   1113         }
   1114         putExtras(extrasToPut);
   1115         removeExtras(extrasToRemove);
   1116 
   1117         // updateState can set mOriginalConnection to null if its state is DISCONNECTED, so this
   1118         // should be executed *after* the above setters have run.
   1119         updateState();
   1120         if (mOriginalConnection == null) {
   1121             Log.w(this, "original Connection was nulled out as part of setOriginalConnection. " +
   1122                     originalConnection);
   1123         }
   1124 
   1125         fireOnOriginalConnectionConfigured();
   1126     }
   1127 
   1128     /**
   1129      * Filters the CNAP name to not include a list of names that are unhelpful to the user for
   1130      * Caller ID purposes.
   1131      */
   1132     private String filterCnapName(final String cnapName) {
   1133         if (cnapName == null) {
   1134             return null;
   1135         }
   1136         PersistableBundle carrierConfig = getCarrierConfig();
   1137         String[] filteredCnapNames = null;
   1138         if (carrierConfig != null) {
   1139             filteredCnapNames = carrierConfig.getStringArray(
   1140                     CarrierConfigManager.KEY_FILTERED_CNAP_NAMES_STRING_ARRAY);
   1141         }
   1142         if (filteredCnapNames != null) {
   1143             long cnapNameMatches = Arrays.asList(filteredCnapNames)
   1144                     .stream()
   1145                     .filter(filteredCnapName -> filteredCnapName.equals(cnapName.toUpperCase()))
   1146                     .count();
   1147             if (cnapNameMatches > 0) {
   1148                 Log.i(this, "filterCnapName: Filtered CNAP Name: " + cnapName);
   1149                 return "";
   1150             }
   1151         }
   1152         return cnapName;
   1153     }
   1154 
   1155     /**
   1156      * Sets the EXTRA_CALL_TECHNOLOGY_TYPE extra on the connection to report back to Telecom.
   1157      */
   1158     private void setTechnologyTypeExtra() {
   1159         if (getPhone() != null) {
   1160             putExtra(TelecomManager.EXTRA_CALL_TECHNOLOGY_TYPE, getPhone().getPhoneType());
   1161         }
   1162     }
   1163 
   1164     private void refreshDisableAddCall() {
   1165         if (shouldSetDisableAddCallExtra()) {
   1166             putExtra(Connection.EXTRA_DISABLE_ADD_CALL, true);
   1167         } else {
   1168             removeExtras(Connection.EXTRA_DISABLE_ADD_CALL);
   1169         }
   1170     }
   1171 
   1172     private boolean shouldSetDisableAddCallExtra() {
   1173         if (mOriginalConnection == null) {
   1174             return false;
   1175         }
   1176         boolean carrierShouldAllowAddCall = mOriginalConnection.shouldAllowAddCallDuringVideoCall();
   1177         if (carrierShouldAllowAddCall) {
   1178             return false;
   1179         }
   1180         Phone phone = getPhone();
   1181         if (phone == null) {
   1182             return false;
   1183         }
   1184         boolean isCurrentVideoCall = false;
   1185         boolean wasVideoCall = false;
   1186         boolean isVowifiEnabled = false;
   1187         if (phone instanceof ImsPhone) {
   1188             ImsPhone imsPhone = (ImsPhone) phone;
   1189             if (imsPhone.getForegroundCall() != null
   1190                     && imsPhone.getForegroundCall().getImsCall() != null) {
   1191                 ImsCall call = imsPhone.getForegroundCall().getImsCall();
   1192                 isCurrentVideoCall = call.isVideoCall();
   1193                 wasVideoCall = call.wasVideoCall();
   1194             }
   1195 
   1196             isVowifiEnabled = ImsUtil.isWfcEnabled(phone.getContext());
   1197         }
   1198 
   1199         if (isCurrentVideoCall) {
   1200             return true;
   1201         } else if (wasVideoCall && mIsWifi && !isVowifiEnabled) {
   1202             return true;
   1203         }
   1204         return false;
   1205     }
   1206 
   1207     private boolean hasHighDefAudioProperty() {
   1208         if (!mHasHighDefAudio) {
   1209             return false;
   1210         }
   1211 
   1212         boolean isVideoCall = VideoProfile.isVideo(getVideoState());
   1213 
   1214         PersistableBundle b = getCarrierConfig();
   1215         boolean canWifiCallsBeHdAudio =
   1216                 b != null && b.getBoolean(CarrierConfigManager.KEY_WIFI_CALLS_CAN_BE_HD_AUDIO);
   1217         boolean canVideoCallsBeHdAudio =
   1218                 b != null && b.getBoolean(CarrierConfigManager.KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO);
   1219         boolean canGsmCdmaCallsBeHdAudio =
   1220                 b != null && b.getBoolean(CarrierConfigManager.KEY_GSM_CDMA_CALLS_CAN_BE_HD_AUDIO);
   1221         boolean shouldDisplayHdAudio =
   1222                 b != null && b.getBoolean(CarrierConfigManager.KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL);
   1223 
   1224         if (!shouldDisplayHdAudio) {
   1225             return false;
   1226         }
   1227 
   1228         if (isGsmCdmaConnection() && !canGsmCdmaCallsBeHdAudio) {
   1229             return false;
   1230         }
   1231 
   1232         if (isVideoCall && !canVideoCallsBeHdAudio) {
   1233             return false;
   1234         }
   1235 
   1236         if (mIsWifi && !canWifiCallsBeHdAudio) {
   1237             return false;
   1238         }
   1239 
   1240         return true;
   1241     }
   1242 
   1243     private boolean canHoldImsCalls() {
   1244         PersistableBundle b = getCarrierConfig();
   1245         // Return true if the CarrierConfig is unavailable
   1246         return !doesDeviceRespectHoldCarrierConfig() || b == null ||
   1247                 b.getBoolean(CarrierConfigManager.KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL);
   1248     }
   1249 
   1250     private PersistableBundle getCarrierConfig() {
   1251         Phone phone = getPhone();
   1252         if (phone == null) {
   1253             return null;
   1254         }
   1255         return PhoneGlobals.getInstance().getCarrierConfigForSubId(phone.getSubId());
   1256     }
   1257 
   1258     private boolean canDeflectImsCalls() {
   1259         PersistableBundle b = getCarrierConfig();
   1260         // Return false if the CarrierConfig is unavailable
   1261         if (b != null) {
   1262             return b.getBoolean(
   1263                     CarrierConfigManager.KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL) &&
   1264                     isValidRingingCall();
   1265         }
   1266         return false;
   1267     }
   1268 
   1269     /**
   1270      * Determines if the device will respect the value of the
   1271      * {@link CarrierConfigManager#KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL} configuration option.
   1272      *
   1273      * @return {@code false} if the device always supports holding IMS calls, {@code true} if it
   1274      *      will use {@link CarrierConfigManager#KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL} to determine if
   1275      *      hold is supported.
   1276      */
   1277     private boolean doesDeviceRespectHoldCarrierConfig() {
   1278         Phone phone = getPhone();
   1279         if (phone == null) {
   1280             return true;
   1281         }
   1282         return phone.getContext().getResources().getBoolean(
   1283                 com.android.internal.R.bool.config_device_respects_hold_carrier_config);
   1284     }
   1285 
   1286     /**
   1287      * Whether the connection should be treated as an emergency.
   1288      * @return {@code true} if the connection should be treated as an emergency call based
   1289      * on the number dialed, {@code false} otherwise.
   1290      */
   1291     protected boolean shouldTreatAsEmergencyCall() {
   1292         return mTreatAsEmergencyCall;
   1293     }
   1294 
   1295     /**
   1296      * Un-sets the underlying radio connection.
   1297      */
   1298     void clearOriginalConnection() {
   1299         if (mOriginalConnection != null) {
   1300             if (getPhone() != null) {
   1301                 getPhone().unregisterForPreciseCallStateChanged(mHandler);
   1302                 getPhone().unregisterForRingbackTone(mHandler);
   1303                 getPhone().unregisterForHandoverStateChanged(mHandler);
   1304                 getPhone().unregisterForDisconnect(mHandler);
   1305                 getPhone().unregisterForSuppServiceNotification(mHandler);
   1306                 getPhone().unregisterForOnHoldTone(mHandler);
   1307                 getPhone().unregisterForInCallVoicePrivacyOn(mHandler);
   1308                 getPhone().unregisterForInCallVoicePrivacyOff(mHandler);
   1309             }
   1310             mOriginalConnection.removePostDialListener(mPostDialListener);
   1311             mOriginalConnection.removeListener(mOriginalConnectionListener);
   1312             mOriginalConnection = null;
   1313         }
   1314     }
   1315 
   1316     protected void hangup(int telephonyDisconnectCode) {
   1317         if (mOriginalConnection != null) {
   1318             try {
   1319                 // Hanging up a ringing call requires that we invoke call.hangup() as opposed to
   1320                 // connection.hangup(). Without this change, the party originating the call
   1321                 // will not get sent to voicemail if the user opts to reject the call.
   1322                 if (isValidRingingCall()) {
   1323                     Call call = getCall();
   1324                     if (call != null) {
   1325                         call.hangup();
   1326                     } else {
   1327                         Log.w(this, "Attempting to hangup a connection without backing call.");
   1328                     }
   1329                 } else {
   1330                     // We still prefer to call connection.hangup() for non-ringing calls
   1331                     // in order to support hanging-up specific calls within a conference call.
   1332                     // If we invoked call.hangup() while in a conference, we would end up
   1333                     // hanging up the entire conference call instead of the specific connection.
   1334                     mOriginalConnection.hangup();
   1335                 }
   1336             } catch (CallStateException e) {
   1337                 Log.e(this, e, "Call to Connection.hangup failed with exception");
   1338             }
   1339         } else {
   1340             if (getState() == STATE_DISCONNECTED) {
   1341                 Log.i(this, "hangup called on an already disconnected call!");
   1342                 close();
   1343             } else {
   1344                 // There are a few cases where mOriginalConnection has not been set yet. For
   1345                 // example, when the radio has to be turned on to make an emergency call,
   1346                 // mOriginalConnection could not be set for many seconds.
   1347                 setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
   1348                         android.telephony.DisconnectCause.LOCAL,
   1349                         "Local Disconnect before connection established."));
   1350                 close();
   1351             }
   1352         }
   1353     }
   1354 
   1355     com.android.internal.telephony.Connection getOriginalConnection() {
   1356         return mOriginalConnection;
   1357     }
   1358 
   1359     protected Call getCall() {
   1360         if (mOriginalConnection != null) {
   1361             return mOriginalConnection.getCall();
   1362         }
   1363         return null;
   1364     }
   1365 
   1366     Phone getPhone() {
   1367         Call call = getCall();
   1368         if (call != null) {
   1369             return call.getPhone();
   1370         }
   1371         return null;
   1372     }
   1373 
   1374     private boolean hasMultipleTopLevelCalls() {
   1375         int numCalls = 0;
   1376         Phone phone = getPhone();
   1377         if (phone != null) {
   1378             if (!phone.getRingingCall().isIdle()) {
   1379                 numCalls++;
   1380             }
   1381             if (!phone.getForegroundCall().isIdle()) {
   1382                 numCalls++;
   1383             }
   1384             if (!phone.getBackgroundCall().isIdle()) {
   1385                 numCalls++;
   1386             }
   1387         }
   1388         return numCalls > 1;
   1389     }
   1390 
   1391     private com.android.internal.telephony.Connection getForegroundConnection() {
   1392         if (getPhone() != null) {
   1393             return getPhone().getForegroundCall().getEarliestConnection();
   1394         }
   1395         return null;
   1396     }
   1397 
   1398      /**
   1399      * Checks for and returns the list of conference participants
   1400      * associated with this connection.
   1401      */
   1402     public List<ConferenceParticipant> getConferenceParticipants() {
   1403         if (mOriginalConnection == null) {
   1404             Log.v(this, "Null mOriginalConnection, cannot get conf participants.");
   1405             return null;
   1406         }
   1407         return mOriginalConnection.getConferenceParticipants();
   1408     }
   1409 
   1410     /**
   1411      * Checks to see the original connection corresponds to an active incoming call. Returns false
   1412      * if there is no such actual call, or if the associated call is not incoming (See
   1413      * {@link Call.State#isRinging}).
   1414      */
   1415     private boolean isValidRingingCall() {
   1416         if (getPhone() == null) {
   1417             Log.v(this, "isValidRingingCall, phone is null");
   1418             return false;
   1419         }
   1420 
   1421         Call ringingCall = getPhone().getRingingCall();
   1422         if (!ringingCall.getState().isRinging()) {
   1423             Log.v(this, "isValidRingingCall, ringing call is not in ringing state");
   1424             return false;
   1425         }
   1426 
   1427         if (ringingCall.getEarliestConnection() != mOriginalConnection) {
   1428             Log.v(this, "isValidRingingCall, ringing call connection does not match");
   1429             return false;
   1430         }
   1431 
   1432         Log.v(this, "isValidRingingCall, returning true");
   1433         return true;
   1434     }
   1435 
   1436     // Make sure the extras being passed into this method is a COPY of the original extras Bundle.
   1437     // We do not want the extras to be cleared or modified during mOriginalConnectionExtras.putAll
   1438     // below.
   1439     protected void updateExtras(Bundle extras) {
   1440         if (mOriginalConnection != null) {
   1441             if (extras != null) {
   1442                 // Check if extras have changed and need updating.
   1443                 if (!areBundlesEqual(mOriginalConnectionExtras, extras)) {
   1444                     if (Log.DEBUG) {
   1445                         Log.d(TelephonyConnection.this, "Updating extras:");
   1446                         for (String key : extras.keySet()) {
   1447                             Object value = extras.get(key);
   1448                             if (value instanceof String) {
   1449                                 Log.d(this, "updateExtras Key=" + Log.pii(key) +
   1450                                              " value=" + Log.pii((String)value));
   1451                             }
   1452                         }
   1453                     }
   1454                     mOriginalConnectionExtras.clear();
   1455 
   1456                     mOriginalConnectionExtras.putAll(extras);
   1457 
   1458                     // Remap any string extras that have a remapping defined.
   1459                     for (String key : mOriginalConnectionExtras.keySet()) {
   1460                         if (sExtrasMap.containsKey(key)) {
   1461                             String newKey = sExtrasMap.get(key);
   1462                             mOriginalConnectionExtras.putString(newKey, extras.getString(key));
   1463                             mOriginalConnectionExtras.remove(key);
   1464                         }
   1465                     }
   1466 
   1467                     // Ensure extras are propagated to Telecom.
   1468                     putExtras(mOriginalConnectionExtras);
   1469                 } else {
   1470                     Log.d(this, "Extras update not required");
   1471                 }
   1472             } else {
   1473                 Log.d(this, "updateExtras extras: " + Log.pii(extras));
   1474             }
   1475         }
   1476     }
   1477 
   1478     private static boolean areBundlesEqual(Bundle extras, Bundle newExtras) {
   1479         if (extras == null || newExtras == null) {
   1480             return extras == newExtras;
   1481         }
   1482 
   1483         if (extras.size() != newExtras.size()) {
   1484             return false;
   1485         }
   1486 
   1487         for(String key : extras.keySet()) {
   1488             if (key != null) {
   1489                 final Object value = extras.get(key);
   1490                 final Object newValue = newExtras.get(key);
   1491                 if (!Objects.equals(value, newValue)) {
   1492                     return false;
   1493                 }
   1494             }
   1495         }
   1496         return true;
   1497     }
   1498 
   1499     void setStateOverride(Call.State state) {
   1500         mIsStateOverridden = true;
   1501         mConnectionOverriddenState = state;
   1502         // Need to keep track of the original connection's state before override.
   1503         mOriginalConnectionState = mOriginalConnection.getState();
   1504         updateStateInternal();
   1505     }
   1506 
   1507     void resetStateOverride() {
   1508         mIsStateOverridden = false;
   1509         updateStateInternal();
   1510     }
   1511 
   1512     void updateStateInternal() {
   1513         if (mOriginalConnection == null) {
   1514             return;
   1515         }
   1516         Call.State newState;
   1517         // If the state is overridden and the state of the original connection hasn't changed since,
   1518         // then we continue in the overridden state, else we go to the original connection's state.
   1519         if (mIsStateOverridden && mOriginalConnectionState == mOriginalConnection.getState()) {
   1520             newState = mConnectionOverriddenState;
   1521         } else {
   1522             newState = mOriginalConnection.getState();
   1523         }
   1524         int cause = mOriginalConnection.getDisconnectCause();
   1525         Log.v(this, "Update state from %s to %s for %s", mConnectionState, newState,
   1526                 getTelecomCallId());
   1527 
   1528         if (mConnectionState != newState) {
   1529             mConnectionState = newState;
   1530             switch (newState) {
   1531                 case IDLE:
   1532                     break;
   1533                 case ACTIVE:
   1534                     setActiveInternal();
   1535                     break;
   1536                 case HOLDING:
   1537                     setOnHold();
   1538                     break;
   1539                 case DIALING:
   1540                 case ALERTING:
   1541                     if (mOriginalConnection != null && mOriginalConnection.isPulledCall()) {
   1542                         setPulling();
   1543                     } else {
   1544                         setDialing();
   1545                     }
   1546                     break;
   1547                 case INCOMING:
   1548                 case WAITING:
   1549                     setRinging();
   1550                     break;
   1551                 case DISCONNECTED:
   1552                     if (shouldTreatAsEmergencyCall()
   1553                             && (cause
   1554                             == android.telephony.DisconnectCause.EMERGENCY_TEMP_FAILURE
   1555                             || cause
   1556                             == android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE)) {
   1557                         // We can get into a situation where the radio wants us to redial the
   1558                         // same emergency call on the other available slot. This will not set
   1559                         // the state to disconnected and will instead tell the
   1560                         // TelephonyConnectionService to
   1561                         // create a new originalConnection using the new Slot.
   1562                         fireOnOriginalConnectionRetryDial(cause
   1563                                 == android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE);
   1564                     } else {
   1565                         int preciseDisconnectCause = CallFailCause.NOT_VALID;
   1566                         if (mShowPreciseFailedCause) {
   1567                             preciseDisconnectCause =
   1568                                     mOriginalConnection.getPreciseDisconnectCause();
   1569                         }
   1570                         setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
   1571                                 mOriginalConnection.getDisconnectCause(),
   1572                                 preciseDisconnectCause,
   1573                                 mOriginalConnection.getVendorDisconnectCause()));
   1574                         close();
   1575                     }
   1576                     break;
   1577                 case DISCONNECTING:
   1578                     break;
   1579             }
   1580         }
   1581     }
   1582 
   1583     void updateState() {
   1584         if (mOriginalConnection == null) {
   1585             return;
   1586         }
   1587 
   1588         updateStateInternal();
   1589         updateStatusHints();
   1590         updateConnectionCapabilities();
   1591         updateConnectionProperties();
   1592         updateAddress();
   1593         updateMultiparty();
   1594         refreshDisableAddCall();
   1595     }
   1596 
   1597     /**
   1598      * Checks for changes to the multiparty bit.  If a conference has started, informs listeners.
   1599      */
   1600     private void updateMultiparty() {
   1601         if (mOriginalConnection == null) {
   1602             return;
   1603         }
   1604 
   1605         if (mIsMultiParty != mOriginalConnection.isMultiparty()) {
   1606             mIsMultiParty = mOriginalConnection.isMultiparty();
   1607 
   1608             if (mIsMultiParty) {
   1609                 notifyConferenceStarted();
   1610             }
   1611         }
   1612     }
   1613 
   1614     /**
   1615      * Handles a failure when merging calls into a conference.
   1616      * {@link com.android.internal.telephony.Connection.Listener#onConferenceMergedFailed()}
   1617      * listener.
   1618      */
   1619     private void handleConferenceMergeFailed(){
   1620         mHandler.obtainMessage(MSG_CONFERENCE_MERGE_FAILED).sendToTarget();
   1621     }
   1622 
   1623     /**
   1624      * Handles requests to update the multiparty state received via the
   1625      * {@link com.android.internal.telephony.Connection.Listener#onMultipartyStateChanged(boolean)}
   1626      * listener.
   1627      * <p>
   1628      * Note: We post this to the mHandler to ensure that if a conference must be created as a
   1629      * result of the multiparty state change, the conference creation happens on the correct
   1630      * thread.  This ensures that the thread check in
   1631      * {@link com.android.internal.telephony.Phone#checkCorrectThread(android.os.Handler)}
   1632      * does not fire.
   1633      *
   1634      * @param isMultiParty {@code true} if this connection is multiparty, {@code false} otherwise.
   1635      */
   1636     private void handleMultipartyStateChange(boolean isMultiParty) {
   1637         Log.i(this, "Update multiparty state to %s", isMultiParty ? "Y" : "N");
   1638         mHandler.obtainMessage(MSG_MULTIPARTY_STATE_CHANGED, isMultiParty).sendToTarget();
   1639     }
   1640 
   1641     private void setActiveInternal() {
   1642         if (getState() == STATE_ACTIVE) {
   1643             Log.w(this, "Should not be called if this is already ACTIVE");
   1644             return;
   1645         }
   1646 
   1647         // When we set a call to active, we need to make sure that there are no other active
   1648         // calls. However, the ordering of state updates to connections can be non-deterministic
   1649         // since all connections register for state changes on the phone independently.
   1650         // To "optimize", we check here to see if there already exists any active calls.  If so,
   1651         // we issue an update for those calls first to make sure we only have one top-level
   1652         // active call.
   1653         if (getConnectionService() != null) {
   1654             for (Connection current : getConnectionService().getAllConnections()) {
   1655                 if (current != this && current instanceof TelephonyConnection) {
   1656                     TelephonyConnection other = (TelephonyConnection) current;
   1657                     if (other.getState() == STATE_ACTIVE) {
   1658                         other.updateState();
   1659                     }
   1660                 }
   1661             }
   1662         }
   1663         setActive();
   1664     }
   1665 
   1666     private void close() {
   1667         Log.v(this, "close");
   1668         clearOriginalConnection();
   1669         destroy();
   1670     }
   1671 
   1672     /**
   1673      * Determines if the current connection is video capable.
   1674      *
   1675      * A connection is deemed to be video capable if the original connection capabilities state that
   1676      * both local and remote video is supported.
   1677      *
   1678      * @return {@code true} if the connection is video capable, {@code false} otherwise.
   1679      */
   1680     private boolean isVideoCapable() {
   1681         return can(mOriginalConnectionCapabilities, Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL)
   1682                 && can(mOriginalConnectionCapabilities,
   1683                 Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
   1684     }
   1685 
   1686     /**
   1687      * Determines if the current connection is an external connection.
   1688      *
   1689      * A connection is deemed to be external if the original connection capabilities state that it
   1690      * is.
   1691      *
   1692      * @return {@code true} if the connection is external, {@code false} otherwise.
   1693      */
   1694     private boolean isExternalConnection() {
   1695         return can(mOriginalConnectionCapabilities, Capability.IS_EXTERNAL_CONNECTION);
   1696     }
   1697 
   1698     /**
   1699      * Determines if the current connection has RTT enabled.
   1700      */
   1701     private boolean isRtt() {
   1702         return mOriginalConnection != null
   1703                 && mOriginalConnection.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS
   1704                 && mOriginalConnection instanceof ImsPhoneConnection
   1705                 && ((ImsPhoneConnection) mOriginalConnection).isRttEnabledForCall();
   1706     }
   1707 
   1708     /**
   1709      * Determines if the current connection is pullable.
   1710      *
   1711      * A connection is deemed to be pullable if the original connection capabilities state that it
   1712      * is.
   1713      *
   1714      * @return {@code true} if the connection is pullable, {@code false} otherwise.
   1715      */
   1716     private boolean isPullable() {
   1717         return can(mOriginalConnectionCapabilities, Capability.IS_EXTERNAL_CONNECTION)
   1718                 && can(mOriginalConnectionCapabilities, Capability.IS_PULLABLE);
   1719     }
   1720 
   1721     /**
   1722      * Sets whether or not CDMA enhanced call privacy is enabled for this connection.
   1723      */
   1724     private void setCdmaVoicePrivacy(boolean isEnabled) {
   1725         if(mIsCdmaVoicePrivacyEnabled != isEnabled) {
   1726             mIsCdmaVoicePrivacyEnabled = isEnabled;
   1727             updateConnectionProperties();
   1728         }
   1729     }
   1730 
   1731     /**
   1732      * Applies capabilities specific to conferences termination to the
   1733      * {@code ConnectionCapabilities} bit-mask.
   1734      *
   1735      * @param capabilities The {@code ConnectionCapabilities} bit-mask.
   1736      * @return The capabilities with the IMS conference capabilities applied.
   1737      */
   1738     private int applyConferenceTerminationCapabilities(int capabilities) {
   1739         int currentCapabilities = capabilities;
   1740 
   1741         // An IMS call cannot be individually disconnected or separated from its parent conference.
   1742         // If the call was IMS, even if it hands over to GMS, these capabilities are not supported.
   1743         if (!mWasImsConnection) {
   1744             currentCapabilities |= CAPABILITY_DISCONNECT_FROM_CONFERENCE;
   1745             currentCapabilities |= CAPABILITY_SEPARATE_FROM_CONFERENCE;
   1746         }
   1747 
   1748         return currentCapabilities;
   1749     }
   1750 
   1751     /**
   1752      * Stores the new original connection capabilities, and applies them to the current connection,
   1753      * notifying any listeners as necessary.
   1754      *
   1755      * @param connectionCapabilities The original connection capabilties.
   1756      */
   1757     public void setOriginalConnectionCapabilities(int connectionCapabilities) {
   1758         mOriginalConnectionCapabilities = connectionCapabilities;
   1759         updateConnectionCapabilities();
   1760         updateConnectionProperties();
   1761     }
   1762 
   1763     /**
   1764      * Called to apply the capabilities present in the {@link #mOriginalConnection} to this
   1765      * {@link Connection}.  Provides a mapping between the capabilities present in the original
   1766      * connection (see {@link com.android.internal.telephony.Connection.Capability}) and those in
   1767      * this {@link Connection}.
   1768      *
   1769      * @param capabilities The capabilities bitmask from the {@link Connection}.
   1770      * @return the capabilities bitmask with the original connection capabilities remapped and
   1771      *      applied.
   1772      */
   1773     public int applyOriginalConnectionCapabilities(int capabilities) {
   1774         // We only support downgrading to audio if both the remote and local side support
   1775         // downgrading to audio.
   1776         boolean supportsDowngradeToAudio = can(mOriginalConnectionCapabilities,
   1777                 Capability.SUPPORTS_DOWNGRADE_TO_VOICE_LOCAL |
   1778                         Capability.SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE);
   1779         capabilities = changeBitmask(capabilities,
   1780                 CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO, !supportsDowngradeToAudio);
   1781 
   1782         capabilities = changeBitmask(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL,
   1783                 can(mOriginalConnectionCapabilities, Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL));
   1784 
   1785         capabilities = changeBitmask(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL,
   1786                 can(mOriginalConnectionCapabilities, Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL));
   1787 
   1788         return capabilities;
   1789     }
   1790 
   1791     /**
   1792      * Sets whether the call is using wifi. Used when rebuilding the capabilities to set or unset
   1793      * the {@link Connection#PROPERTY_WIFI} property.
   1794      */
   1795     public void setWifi(boolean isWifi) {
   1796         mIsWifi = isWifi;
   1797         updateConnectionProperties();
   1798         updateStatusHints();
   1799         refreshDisableAddCall();
   1800     }
   1801 
   1802     /**
   1803      * Whether the call is using wifi.
   1804      */
   1805     boolean isWifi() {
   1806         return mIsWifi;
   1807     }
   1808 
   1809     /**
   1810      * @return {@code true} if this is an outgoing call, {@code false} otherwise.
   1811      */
   1812     boolean isOutgoingCall() {
   1813         return mIsOutgoing;
   1814     }
   1815 
   1816     /**
   1817      * Sets the current call audio quality. Used during rebuild of the properties
   1818      * to set or unset the {@link Connection#PROPERTY_HIGH_DEF_AUDIO} property.
   1819      *
   1820      * @param audioQuality The audio quality.
   1821      */
   1822     public void setAudioQuality(int audioQuality) {
   1823         mHasHighDefAudio = audioQuality ==
   1824                 com.android.internal.telephony.Connection.AUDIO_QUALITY_HIGH_DEFINITION;
   1825         updateConnectionProperties();
   1826     }
   1827 
   1828     void resetStateForConference() {
   1829         if (getState() == Connection.STATE_HOLDING) {
   1830             resetStateOverride();
   1831         }
   1832     }
   1833 
   1834     boolean setHoldingForConference() {
   1835         if (getState() == Connection.STATE_ACTIVE) {
   1836             setStateOverride(Call.State.HOLDING);
   1837             return true;
   1838         }
   1839         return false;
   1840     }
   1841 
   1842     public void setRttTextStream(RttTextStream s) {
   1843         mRttTextStream = s;
   1844     }
   1845 
   1846     public RttTextStream getRttTextStream() {
   1847         return mRttTextStream;
   1848     }
   1849 
   1850     /**
   1851      * For video calls, sets whether this connection supports pausing the outgoing video for the
   1852      * call using the {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState.
   1853      *
   1854      * @param isVideoPauseSupported {@code true} if pause state supported, {@code false} otherwise.
   1855      */
   1856     public void setVideoPauseSupported(boolean isVideoPauseSupported) {
   1857         mIsVideoPauseSupported = isVideoPauseSupported;
   1858     }
   1859 
   1860     /**
   1861      * @return {@code true} if this connection supports pausing the outgoing video using the
   1862      * {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState.
   1863      */
   1864     public boolean getVideoPauseSupported() {
   1865         return mIsVideoPauseSupported;
   1866     }
   1867 
   1868     /**
   1869      * Sets whether this connection supports conference calling.
   1870      * @param isConferenceSupported {@code true} if conference calling is supported by this
   1871      *                                         connection, {@code false} otherwise.
   1872      */
   1873     public void setConferenceSupported(boolean isConferenceSupported) {
   1874         mIsConferenceSupported = isConferenceSupported;
   1875     }
   1876 
   1877     /**
   1878      * @return {@code true} if this connection supports merging calls into a conference.
   1879      */
   1880     public boolean isConferenceSupported() {
   1881         return mIsConferenceSupported;
   1882     }
   1883 
   1884     /**
   1885      * Sets whether managing conference call is supported after this connection being a part of a
   1886      * Ims conference.
   1887      *
   1888      * @param isManageImsConferenceCallSupported {@code true} if manage conference calling is
   1889      *        supported after this connection being a part of a IMS conference,
   1890      *        {@code false} otherwise.
   1891      */
   1892     public void setManageImsConferenceCallSupported(boolean isManageImsConferenceCallSupported) {
   1893         mIsManageImsConferenceCallSupported = isManageImsConferenceCallSupported;
   1894     }
   1895 
   1896     /**
   1897      * @return {@code true} if manage conference calling is supported after this connection being a
   1898      * part of a IMS conference.
   1899      */
   1900     public boolean isManageImsConferenceCallSupported() {
   1901         return mIsManageImsConferenceCallSupported;
   1902     }
   1903 
   1904     /**
   1905      * Sets whether this connection supports showing precise call disconnect cause.
   1906      * @param showPreciseFailedCause  {@code true} if showing precise call
   1907      * disconnect cause is supported by this connection, {@code false} otherwise.
   1908      */
   1909     public void setShowPreciseFailedCause(boolean showPreciseFailedCause) {
   1910         mShowPreciseFailedCause = showPreciseFailedCause;
   1911     }
   1912 
   1913     /**
   1914      * Whether the original connection is an IMS connection.
   1915      * @return {@code True} if the original connection is an IMS connection, {@code false}
   1916      *     otherwise.
   1917      */
   1918     protected boolean isImsConnection() {
   1919         com.android.internal.telephony.Connection originalConnection = getOriginalConnection();
   1920         return originalConnection != null &&
   1921                 originalConnection.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS;
   1922     }
   1923 
   1924     /**
   1925      * Whether the original connection is an GSM/CDMA connection.
   1926      * @return {@code True} if the original connection is an GSM/CDMA connection, {@code false}
   1927      *     otherwise.
   1928      */
   1929     protected boolean isGsmCdmaConnection() {
   1930         Phone phone = getPhone();
   1931         if (phone != null) {
   1932             switch (phone.getPhoneType()) {
   1933                 case PhoneConstants.PHONE_TYPE_GSM:
   1934                 case PhoneConstants.PHONE_TYPE_CDMA:
   1935                     return true;
   1936                 default:
   1937                     return false;
   1938             }
   1939         }
   1940         return false;
   1941     }
   1942 
   1943     /**
   1944      * Whether the original connection was ever an IMS connection, either before or now.
   1945      * @return {@code True} if the original connection was ever an IMS connection, {@code false}
   1946      *     otherwise.
   1947      */
   1948     public boolean wasImsConnection() {
   1949         return mWasImsConnection;
   1950     }
   1951 
   1952     boolean getIsUsingAssistedDialing() {
   1953         return mIsUsingAssistedDialing;
   1954     }
   1955 
   1956     void setIsUsingAssistedDialing(Boolean isUsingAssistedDialing) {
   1957         mIsUsingAssistedDialing = isUsingAssistedDialing;
   1958         updateConnectionProperties();
   1959     }
   1960 
   1961     private static Uri getAddressFromNumber(String number) {
   1962         // Address can be null for blocked calls.
   1963         if (number == null) {
   1964             number = "";
   1965         }
   1966         return Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null);
   1967     }
   1968 
   1969     /**
   1970      * Changes a capabilities bit-mask to add or remove a capability.
   1971      *
   1972      * @param bitmask The bit-mask.
   1973      * @param bitfield The bit-field to change.
   1974      * @param enabled Whether the bit-field should be set or removed.
   1975      * @return The bit-mask with the bit-field changed.
   1976      */
   1977     private int changeBitmask(int bitmask, int bitfield, boolean enabled) {
   1978         if (enabled) {
   1979             return bitmask | bitfield;
   1980         } else {
   1981             return bitmask & ~bitfield;
   1982         }
   1983     }
   1984 
   1985     private void updateStatusHints() {
   1986         boolean isIncoming = isValidRingingCall();
   1987         if (mIsWifi && (isIncoming || getState() == STATE_ACTIVE)) {
   1988             int labelId = isIncoming
   1989                     ? R.string.status_hint_label_incoming_wifi_call
   1990                     : R.string.status_hint_label_wifi_call;
   1991 
   1992             Context context = getPhone().getContext();
   1993             setStatusHints(new StatusHints(
   1994                     context.getString(labelId),
   1995                     Icon.createWithResource(
   1996                             context.getResources(),
   1997                             R.drawable.ic_signal_wifi_4_bar_24dp),
   1998                     null /* extras */));
   1999         } else {
   2000             setStatusHints(null);
   2001         }
   2002     }
   2003 
   2004     /**
   2005      * Register a listener for {@link TelephonyConnection} specific triggers.
   2006      * @param l The instance of the listener to add
   2007      * @return The connection being listened to
   2008      */
   2009     public final TelephonyConnection addTelephonyConnectionListener(TelephonyConnectionListener l) {
   2010         mTelephonyListeners.add(l);
   2011         // If we already have an original connection, let's call back immediately.
   2012         // This would be the case for incoming calls.
   2013         if (mOriginalConnection != null) {
   2014             fireOnOriginalConnectionConfigured();
   2015         }
   2016         return this;
   2017     }
   2018 
   2019     /**
   2020      * Remove a listener for {@link TelephonyConnection} specific triggers.
   2021      * @param l The instance of the listener to remove
   2022      * @return The connection being listened to
   2023      */
   2024     public final TelephonyConnection removeTelephonyConnectionListener(
   2025             TelephonyConnectionListener l) {
   2026         if (l != null) {
   2027             mTelephonyListeners.remove(l);
   2028         }
   2029         return this;
   2030     }
   2031 
   2032     @Override
   2033     public void setHoldable(boolean isHoldable) {
   2034         mIsHoldable = isHoldable;
   2035         updateConnectionCapabilities();
   2036     }
   2037 
   2038     @Override
   2039     public boolean isChildHoldable() {
   2040         return getConference() != null;
   2041     }
   2042 
   2043     public boolean isHoldable() {
   2044         return mIsHoldable;
   2045     }
   2046 
   2047     /**
   2048      * Fire a callback to the various listeners for when the original connection is
   2049      * set in this {@link TelephonyConnection}
   2050      */
   2051     private final void fireOnOriginalConnectionConfigured() {
   2052         for (TelephonyConnectionListener l : mTelephonyListeners) {
   2053             l.onOriginalConnectionConfigured(this);
   2054         }
   2055     }
   2056 
   2057     private final void fireOnOriginalConnectionRetryDial(boolean isPermanentFailure) {
   2058         for (TelephonyConnectionListener l : mTelephonyListeners) {
   2059             l.onOriginalConnectionRetry(this, isPermanentFailure);
   2060         }
   2061     }
   2062 
   2063     /**
   2064      * Handles exiting ECM mode.
   2065      */
   2066     protected void handleExitedEcmMode() {
   2067         updateConnectionProperties();
   2068     }
   2069 
   2070     /**
   2071      * Determines whether the connection supports conference calling.  A connection supports
   2072      * conference calling if it:
   2073      * 1. Is not an emergency call.
   2074      * 2. Carrier supports conference calls.
   2075      * 3. If call is a video call, carrier supports video conference calls.
   2076      * 4. If call is a wifi call and VoWIFI is disabled and carrier supports merging these calls.
   2077      */
   2078     private void refreshConferenceSupported() {
   2079         boolean isVideoCall = VideoProfile.isVideo(getVideoState());
   2080         Phone phone = getPhone();
   2081         if (phone == null) {
   2082             Log.w(this, "refreshConferenceSupported = false; phone is null");
   2083             if (isConferenceSupported()) {
   2084                 setConferenceSupported(false);
   2085                 notifyConferenceSupportedChanged(false);
   2086             }
   2087             return;
   2088         }
   2089 
   2090         boolean isIms = phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS;
   2091         boolean isVoWifiEnabled = false;
   2092         if (isIms) {
   2093             ImsPhone imsPhone = (ImsPhone) phone;
   2094             isVoWifiEnabled = ImsUtil.isWfcEnabled(phone.getContext());
   2095         }
   2096         PhoneAccountHandle phoneAccountHandle = isIms ? PhoneUtils
   2097                 .makePstnPhoneAccountHandle(phone.getDefaultPhone())
   2098                 : PhoneUtils.makePstnPhoneAccountHandle(phone);
   2099         TelecomAccountRegistry telecomAccountRegistry = TelecomAccountRegistry
   2100                 .getInstance(getPhone().getContext());
   2101         boolean isConferencingSupported = telecomAccountRegistry
   2102                 .isMergeCallSupported(phoneAccountHandle);
   2103         boolean isImsConferencingSupported = telecomAccountRegistry
   2104                 .isMergeImsCallSupported(phoneAccountHandle);
   2105         mIsCarrierVideoConferencingSupported = telecomAccountRegistry
   2106                 .isVideoConferencingSupported(phoneAccountHandle);
   2107         boolean isMergeOfWifiCallsAllowedWhenVoWifiOff = telecomAccountRegistry
   2108                 .isMergeOfWifiCallsAllowedWhenVoWifiOff(phoneAccountHandle);
   2109 
   2110         Log.v(this, "refreshConferenceSupported : isConfSupp=%b, isImsConfSupp=%b, " +
   2111                 "isVidConfSupp=%b, isMergeOfWifiAllowed=%b, " +
   2112                 "isWifi=%b, isVoWifiEnabled=%b",
   2113                 isConferencingSupported, isImsConferencingSupported,
   2114                 mIsCarrierVideoConferencingSupported, isMergeOfWifiCallsAllowedWhenVoWifiOff,
   2115                 isWifi(), isVoWifiEnabled);
   2116         boolean isConferenceSupported = true;
   2117         if (mTreatAsEmergencyCall) {
   2118             isConferenceSupported = false;
   2119             Log.d(this, "refreshConferenceSupported = false; emergency call");
   2120         } else if (!isConferencingSupported || isIms && !isImsConferencingSupported) {
   2121             isConferenceSupported = false;
   2122             Log.d(this, "refreshConferenceSupported = false; carrier doesn't support conf.");
   2123         } else if (isVideoCall && !mIsCarrierVideoConferencingSupported) {
   2124             isConferenceSupported = false;
   2125             Log.d(this, "refreshConferenceSupported = false; video conf not supported.");
   2126         } else if (!isMergeOfWifiCallsAllowedWhenVoWifiOff && isWifi() && !isVoWifiEnabled) {
   2127             isConferenceSupported = false;
   2128             Log.d(this,
   2129                     "refreshConferenceSupported = false; can't merge wifi calls when voWifi off.");
   2130         } else {
   2131             Log.d(this, "refreshConferenceSupported = true.");
   2132         }
   2133 
   2134         if (isConferenceSupported != isConferenceSupported()) {
   2135             setConferenceSupported(isConferenceSupported);
   2136             notifyConferenceSupportedChanged(isConferenceSupported);
   2137         }
   2138     }
   2139     /**
   2140      * Provides a mapping from extras keys which may be found in the
   2141      * {@link com.android.internal.telephony.Connection} to their equivalents defined in
   2142      * {@link android.telecom.Connection}.
   2143      *
   2144      * @return Map containing key mappings.
   2145      */
   2146     private static Map<String, String> createExtrasMap() {
   2147         Map<String, String> result = new HashMap<String, String>();
   2148         result.put(ImsCallProfile.EXTRA_CHILD_NUMBER,
   2149                 android.telecom.Connection.EXTRA_CHILD_ADDRESS);
   2150         result.put(ImsCallProfile.EXTRA_DISPLAY_TEXT,
   2151                 android.telecom.Connection.EXTRA_CALL_SUBJECT);
   2152         return Collections.unmodifiableMap(result);
   2153     }
   2154 
   2155     /**
   2156      * Creates a string representation of this {@link TelephonyConnection}.  Primarily intended for
   2157      * use in log statements.
   2158      *
   2159      * @return String representation of the connection.
   2160      */
   2161     @Override
   2162     public String toString() {
   2163         StringBuilder sb = new StringBuilder();
   2164         sb.append("[TelephonyConnection objId:");
   2165         sb.append(System.identityHashCode(this));
   2166         sb.append(" telecomCallID:");
   2167         sb.append(getTelecomCallId());
   2168         sb.append(" type:");
   2169         if (isImsConnection()) {
   2170             sb.append("ims");
   2171         } else if (this instanceof com.android.services.telephony.GsmConnection) {
   2172             sb.append("gsm");
   2173         } else if (this instanceof CdmaConnection) {
   2174             sb.append("cdma");
   2175         }
   2176         sb.append(" state:");
   2177         sb.append(Connection.stateToString(getState()));
   2178         sb.append(" capabilities:");
   2179         sb.append(capabilitiesToString(getConnectionCapabilities()));
   2180         sb.append(" properties:");
   2181         sb.append(propertiesToString(getConnectionProperties()));
   2182         sb.append(" address:");
   2183         sb.append(Log.pii(getAddress()));
   2184         sb.append(" originalConnection:");
   2185         sb.append(mOriginalConnection);
   2186         sb.append(" partOfConf:");
   2187         if (getConference() == null) {
   2188             sb.append("N");
   2189         } else {
   2190             sb.append("Y");
   2191         }
   2192         sb.append(" confSupported:");
   2193         sb.append(mIsConferenceSupported ? "Y" : "N");
   2194         sb.append("]");
   2195         return sb.toString();
   2196     }
   2197 }
   2198