Home | History | Annotate | Download | only in imsphone
      1 /*
      2  * Copyright (C) 2013 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.internal.telephony.imsphone;
     18 
     19 import android.content.Context;
     20 import android.net.Uri;
     21 import android.os.AsyncResult;
     22 import android.os.Handler;
     23 import android.os.Looper;
     24 import android.os.Message;
     25 import android.os.PowerManager;
     26 import android.os.Registrant;
     27 import android.os.SystemClock;
     28 import android.telecom.Log;
     29 import android.telephony.DisconnectCause;
     30 import android.telephony.PhoneNumberUtils;
     31 import android.telephony.Rlog;
     32 import android.text.TextUtils;
     33 
     34 import com.android.ims.ImsException;
     35 import com.android.ims.ImsStreamMediaProfile;
     36 import com.android.internal.telephony.CallStateException;
     37 import com.android.internal.telephony.Connection;
     38 import com.android.internal.telephony.Phone;
     39 import com.android.internal.telephony.PhoneConstants;
     40 import com.android.internal.telephony.UUSInfo;
     41 
     42 import com.android.ims.ImsCall;
     43 import com.android.ims.ImsCallProfile;
     44 
     45 /**
     46  * {@hide}
     47  */
     48 public class ImsPhoneConnection extends Connection {
     49     private static final String LOG_TAG = "ImsPhoneConnection";
     50     private static final boolean DBG = true;
     51 
     52     //***** Instance Variables
     53 
     54     private ImsPhoneCallTracker mOwner;
     55     private ImsPhoneCall mParent;
     56     private ImsCall mImsCall;
     57 
     58     private String mPostDialString;      // outgoing calls only
     59     private boolean mDisconnected;
     60 
     61     /*
     62     int mIndex;          // index in ImsPhoneCallTracker.connections[], -1 if unassigned
     63                         // The GSM index is 1 + this
     64     */
     65 
     66     /*
     67      * These time/timespan values are based on System.currentTimeMillis(),
     68      * i.e., "wall clock" time.
     69      */
     70     private long mDisconnectTime;
     71 
     72     private int mNextPostDialChar;       // index into postDialString
     73 
     74     private int mCause = DisconnectCause.NOT_DISCONNECTED;
     75     private PostDialState mPostDialState = PostDialState.NOT_STARTED;
     76     private UUSInfo mUusInfo;
     77     private Handler mHandler;
     78 
     79     private PowerManager.WakeLock mPartialWakeLock;
     80 
     81     // The cached connect time of the connection when it turns into a conference.
     82     private long mConferenceConnectTime = 0;
     83 
     84     //***** Event Constants
     85     private static final int EVENT_DTMF_DONE = 1;
     86     private static final int EVENT_PAUSE_DONE = 2;
     87     private static final int EVENT_NEXT_POST_DIAL = 3;
     88     private static final int EVENT_WAKE_LOCK_TIMEOUT = 4;
     89 
     90     //***** Constants
     91     private static final int PAUSE_DELAY_MILLIS = 3 * 1000;
     92     private static final int WAKE_LOCK_TIMEOUT_MILLIS = 60*1000;
     93 
     94     //***** Inner Classes
     95 
     96     class MyHandler extends Handler {
     97         MyHandler(Looper l) {super(l);}
     98 
     99         @Override
    100         public void
    101         handleMessage(Message msg) {
    102 
    103             switch (msg.what) {
    104                 case EVENT_NEXT_POST_DIAL:
    105                 case EVENT_DTMF_DONE:
    106                 case EVENT_PAUSE_DONE:
    107                     processNextPostDialChar();
    108                     break;
    109                 case EVENT_WAKE_LOCK_TIMEOUT:
    110                     releaseWakeLock();
    111                     break;
    112             }
    113         }
    114     }
    115 
    116     //***** Constructors
    117 
    118     /** This is probably an MT call */
    119     /*package*/
    120     ImsPhoneConnection(Context context, ImsCall imsCall, ImsPhoneCallTracker ct,
    121             ImsPhoneCall parent) {
    122         createWakeLock(context);
    123         acquireWakeLock();
    124 
    125         mOwner = ct;
    126         mHandler = new MyHandler(mOwner.getLooper());
    127         mImsCall = imsCall;
    128 
    129         if ((imsCall != null) && (imsCall.getCallProfile() != null)) {
    130             mAddress = imsCall.getCallProfile().getCallExtra(ImsCallProfile.EXTRA_OI);
    131             mCnapName = imsCall.getCallProfile().getCallExtra(ImsCallProfile.EXTRA_CNA);
    132             mNumberPresentation = ImsCallProfile.OIRToPresentation(
    133                     imsCall.getCallProfile().getCallExtraInt(ImsCallProfile.EXTRA_OIR));
    134             mCnapNamePresentation = ImsCallProfile.OIRToPresentation(
    135                     imsCall.getCallProfile().getCallExtraInt(ImsCallProfile.EXTRA_CNAP));
    136             updateMediaCapabilities(imsCall);
    137         } else {
    138             mNumberPresentation = PhoneConstants.PRESENTATION_UNKNOWN;
    139             mCnapNamePresentation = PhoneConstants.PRESENTATION_UNKNOWN;
    140         }
    141 
    142         mIsIncoming = true;
    143         mCreateTime = System.currentTimeMillis();
    144         mUusInfo = null;
    145 
    146         //mIndex = index;
    147 
    148         updateWifiState();
    149 
    150         mParent = parent;
    151         mParent.attach(this, ImsPhoneCall.State.INCOMING);
    152     }
    153 
    154     /** This is an MO call, created when dialing */
    155     /*package*/
    156     ImsPhoneConnection(Context context, String dialString, ImsPhoneCallTracker ct,
    157             ImsPhoneCall parent) {
    158         createWakeLock(context);
    159         acquireWakeLock();
    160 
    161         mOwner = ct;
    162         mHandler = new MyHandler(mOwner.getLooper());
    163 
    164         mDialString = dialString;
    165 
    166         mAddress = PhoneNumberUtils.extractNetworkPortionAlt(dialString);
    167         mPostDialString = PhoneNumberUtils.extractPostDialPortion(dialString);
    168 
    169         //mIndex = -1;
    170 
    171         mIsIncoming = false;
    172         mCnapName = null;
    173         mCnapNamePresentation = PhoneConstants.PRESENTATION_ALLOWED;
    174         mNumberPresentation = PhoneConstants.PRESENTATION_ALLOWED;
    175         mCreateTime = System.currentTimeMillis();
    176 
    177         mParent = parent;
    178         parent.attachFake(this, ImsPhoneCall.State.DIALING);
    179     }
    180 
    181     public void dispose() {
    182     }
    183 
    184     static boolean
    185     equalsHandlesNulls (Object a, Object b) {
    186         return (a == null) ? (b == null) : a.equals (b);
    187     }
    188 
    189     @Override
    190     public String getOrigDialString(){
    191         return mDialString;
    192     }
    193 
    194     @Override
    195     public ImsPhoneCall getCall() {
    196         return mParent;
    197     }
    198 
    199     @Override
    200     public long getDisconnectTime() {
    201         return mDisconnectTime;
    202     }
    203 
    204     @Override
    205     public long getHoldingStartTime() {
    206         return mHoldingStartTime;
    207     }
    208 
    209     @Override
    210     public long getHoldDurationMillis() {
    211         if (getState() != ImsPhoneCall.State.HOLDING) {
    212             // If not holding, return 0
    213             return 0;
    214         } else {
    215             return SystemClock.elapsedRealtime() - mHoldingStartTime;
    216         }
    217     }
    218 
    219     @Override
    220     public int getDisconnectCause() {
    221         return mCause;
    222     }
    223 
    224     public void setDisconnectCause(int cause) {
    225         mCause = cause;
    226     }
    227 
    228     @Override
    229     public String getVendorDisconnectCause() {
    230       return null;
    231     }
    232 
    233     public ImsPhoneCallTracker getOwner () {
    234         return mOwner;
    235     }
    236 
    237     @Override
    238     public ImsPhoneCall.State getState() {
    239         if (mDisconnected) {
    240             return ImsPhoneCall.State.DISCONNECTED;
    241         } else {
    242             return super.getState();
    243         }
    244     }
    245 
    246     @Override
    247     public void hangup() throws CallStateException {
    248         if (!mDisconnected) {
    249             mOwner.hangup(this);
    250         } else {
    251             throw new CallStateException ("disconnected");
    252         }
    253     }
    254 
    255     @Override
    256     public void separate() throws CallStateException {
    257         throw new CallStateException ("not supported");
    258     }
    259 
    260     @Override
    261     public PostDialState getPostDialState() {
    262         return mPostDialState;
    263     }
    264 
    265     @Override
    266     public void proceedAfterWaitChar() {
    267         if (mPostDialState != PostDialState.WAIT) {
    268             Rlog.w(LOG_TAG, "ImsPhoneConnection.proceedAfterWaitChar(): Expected "
    269                     + "getPostDialState() to be WAIT but was " + mPostDialState);
    270             return;
    271         }
    272 
    273         setPostDialState(PostDialState.STARTED);
    274 
    275         processNextPostDialChar();
    276     }
    277 
    278     @Override
    279     public void proceedAfterWildChar(String str) {
    280         if (mPostDialState != PostDialState.WILD) {
    281             Rlog.w(LOG_TAG, "ImsPhoneConnection.proceedAfterWaitChar(): Expected "
    282                     + "getPostDialState() to be WILD but was " + mPostDialState);
    283             return;
    284         }
    285 
    286         setPostDialState(PostDialState.STARTED);
    287 
    288         // make a new postDialString, with the wild char replacement string
    289         // at the beginning, followed by the remaining postDialString.
    290 
    291         StringBuilder buf = new StringBuilder(str);
    292         buf.append(mPostDialString.substring(mNextPostDialChar));
    293         mPostDialString = buf.toString();
    294         mNextPostDialChar = 0;
    295         if (Phone.DEBUG_PHONE) {
    296             Rlog.d(LOG_TAG, "proceedAfterWildChar: new postDialString is " +
    297                     mPostDialString);
    298         }
    299 
    300         processNextPostDialChar();
    301     }
    302 
    303     @Override
    304     public void cancelPostDial() {
    305         setPostDialState(PostDialState.CANCELLED);
    306     }
    307 
    308     /**
    309      * Called when this Connection is being hung up locally (eg, user pressed "end")
    310      */
    311     void
    312     onHangupLocal() {
    313         mCause = DisconnectCause.LOCAL;
    314     }
    315 
    316     /** Called when the connection has been disconnected */
    317     public boolean
    318     onDisconnect(int cause) {
    319         Rlog.d(LOG_TAG, "onDisconnect: cause=" + cause);
    320         if (mCause != DisconnectCause.LOCAL) mCause = cause;
    321         return onDisconnect();
    322     }
    323 
    324     /*package*/ boolean
    325     onDisconnect() {
    326         boolean changed = false;
    327 
    328         if (!mDisconnected) {
    329             //mIndex = -1;
    330 
    331             mDisconnectTime = System.currentTimeMillis();
    332             mDuration = SystemClock.elapsedRealtime() - mConnectTimeReal;
    333             mDisconnected = true;
    334 
    335             mOwner.mPhone.notifyDisconnect(this);
    336 
    337             if (mParent != null) {
    338                 changed = mParent.connectionDisconnected(this);
    339             } else {
    340                 Rlog.d(LOG_TAG, "onDisconnect: no parent");
    341             }
    342             if (mImsCall != null) mImsCall.close();
    343             mImsCall = null;
    344         }
    345         releaseWakeLock();
    346         return changed;
    347     }
    348 
    349     /**
    350      * An incoming or outgoing call has connected
    351      */
    352     void
    353     onConnectedInOrOut() {
    354         mConnectTime = System.currentTimeMillis();
    355         mConnectTimeReal = SystemClock.elapsedRealtime();
    356         mDuration = 0;
    357 
    358         if (Phone.DEBUG_PHONE) {
    359             Rlog.d(LOG_TAG, "onConnectedInOrOut: connectTime=" + mConnectTime);
    360         }
    361 
    362         if (!mIsIncoming) {
    363             // outgoing calls only
    364             processNextPostDialChar();
    365         }
    366         releaseWakeLock();
    367     }
    368 
    369     /*package*/ void
    370     onStartedHolding() {
    371         mHoldingStartTime = SystemClock.elapsedRealtime();
    372     }
    373     /**
    374      * Performs the appropriate action for a post-dial char, but does not
    375      * notify application. returns false if the character is invalid and
    376      * should be ignored
    377      */
    378     private boolean
    379     processPostDialChar(char c) {
    380         if (PhoneNumberUtils.is12Key(c)) {
    381             mOwner.sendDtmf(c, mHandler.obtainMessage(EVENT_DTMF_DONE));
    382         } else if (c == PhoneNumberUtils.PAUSE) {
    383             // From TS 22.101:
    384             // It continues...
    385             // Upon the called party answering the UE shall send the DTMF digits
    386             // automatically to the network after a delay of 3 seconds( 20 ).
    387             // The digits shall be sent according to the procedures and timing
    388             // specified in 3GPP TS 24.008 [13]. The first occurrence of the
    389             // "DTMF Control Digits Separator" shall be used by the ME to
    390             // distinguish between the addressing digits (i.e. the phone number)
    391             // and the DTMF digits. Upon subsequent occurrences of the
    392             // separator,
    393             // the UE shall pause again for 3 seconds ( 20 ) before sending
    394             // any further DTMF digits.
    395             mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_PAUSE_DONE),
    396                     PAUSE_DELAY_MILLIS);
    397         } else if (c == PhoneNumberUtils.WAIT) {
    398             setPostDialState(PostDialState.WAIT);
    399         } else if (c == PhoneNumberUtils.WILD) {
    400             setPostDialState(PostDialState.WILD);
    401         } else {
    402             return false;
    403         }
    404 
    405         return true;
    406     }
    407 
    408     @Override
    409     public String
    410     getRemainingPostDialString() {
    411         if (mPostDialState == PostDialState.CANCELLED
    412             || mPostDialState == PostDialState.COMPLETE
    413             || mPostDialString == null
    414             || mPostDialString.length() <= mNextPostDialChar
    415         ) {
    416             return "";
    417         }
    418 
    419         return mPostDialString.substring(mNextPostDialChar);
    420     }
    421 
    422     @Override
    423     protected void finalize()
    424     {
    425         releaseWakeLock();
    426     }
    427 
    428     private void
    429     processNextPostDialChar() {
    430         char c = 0;
    431         Registrant postDialHandler;
    432 
    433         if (mPostDialState == PostDialState.CANCELLED) {
    434             //Rlog.d(LOG_TAG, "##### processNextPostDialChar: postDialState == CANCELLED, bail");
    435             return;
    436         }
    437 
    438         if (mPostDialString == null || mPostDialString.length() <= mNextPostDialChar) {
    439             setPostDialState(PostDialState.COMPLETE);
    440 
    441             // notifyMessage.arg1 is 0 on complete
    442             c = 0;
    443         } else {
    444             boolean isValid;
    445 
    446             setPostDialState(PostDialState.STARTED);
    447 
    448             c = mPostDialString.charAt(mNextPostDialChar++);
    449 
    450             isValid = processPostDialChar(c);
    451 
    452             if (!isValid) {
    453                 // Will call processNextPostDialChar
    454                 mHandler.obtainMessage(EVENT_NEXT_POST_DIAL).sendToTarget();
    455                 // Don't notify application
    456                 Rlog.e(LOG_TAG, "processNextPostDialChar: c=" + c + " isn't valid!");
    457                 return;
    458             }
    459         }
    460 
    461         notifyPostDialListenersNextChar(c);
    462 
    463         // TODO: remove the following code since the handler no longer executes anything.
    464         postDialHandler = mOwner.mPhone.mPostDialHandler;
    465 
    466         Message notifyMessage;
    467 
    468         if (postDialHandler != null
    469                 && (notifyMessage = postDialHandler.messageForRegistrant()) != null) {
    470             // The AsyncResult.result is the Connection object
    471             PostDialState state = mPostDialState;
    472             AsyncResult ar = AsyncResult.forMessage(notifyMessage);
    473             ar.result = this;
    474             ar.userObj = state;
    475 
    476             // arg1 is the character that was/is being processed
    477             notifyMessage.arg1 = c;
    478 
    479             //Rlog.v(LOG_TAG,
    480             //      "##### processNextPostDialChar: send msg to postDialHandler, arg1=" + c);
    481             notifyMessage.sendToTarget();
    482         }
    483     }
    484 
    485     /**
    486      * Set post dial state and acquire wake lock while switching to "started"
    487      * state, the wake lock will be released if state switches out of "started"
    488      * state or after WAKE_LOCK_TIMEOUT_MILLIS.
    489      * @param s new PostDialState
    490      */
    491     private void setPostDialState(PostDialState s) {
    492         if (mPostDialState != PostDialState.STARTED
    493                 && s == PostDialState.STARTED) {
    494             acquireWakeLock();
    495             Message msg = mHandler.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT);
    496             mHandler.sendMessageDelayed(msg, WAKE_LOCK_TIMEOUT_MILLIS);
    497         } else if (mPostDialState == PostDialState.STARTED
    498                 && s != PostDialState.STARTED) {
    499             mHandler.removeMessages(EVENT_WAKE_LOCK_TIMEOUT);
    500             releaseWakeLock();
    501         }
    502         mPostDialState = s;
    503         notifyPostDialListeners();
    504     }
    505 
    506     private void
    507     createWakeLock(Context context) {
    508         PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
    509         mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
    510     }
    511 
    512     private void
    513     acquireWakeLock() {
    514         Rlog.d(LOG_TAG, "acquireWakeLock");
    515         mPartialWakeLock.acquire();
    516     }
    517 
    518     void
    519     releaseWakeLock() {
    520         synchronized(mPartialWakeLock) {
    521             if (mPartialWakeLock.isHeld()) {
    522                 Rlog.d(LOG_TAG, "releaseWakeLock");
    523                 mPartialWakeLock.release();
    524             }
    525         }
    526     }
    527 
    528     @Override
    529     public int getNumberPresentation() {
    530         return mNumberPresentation;
    531     }
    532 
    533     @Override
    534     public UUSInfo getUUSInfo() {
    535         return mUusInfo;
    536     }
    537 
    538     @Override
    539     public Connection getOrigConnection() {
    540         return null;
    541     }
    542 
    543     @Override
    544     public boolean isMultiparty() {
    545         return mImsCall != null && mImsCall.isMultiparty();
    546     }
    547 
    548     /**
    549      * Where {@link #isMultiparty()} is {@code true}, determines if this {@link ImsCall} is the
    550      * origin of the conference call (i.e. {@code #isConferenceHost()} is {@code true}), or if this
    551      * {@link ImsCall} is a member of a conference hosted on another device.
    552      *
    553      * @return {@code true} if this call is the origin of the conference call it is a member of,
    554      *      {@code false} otherwise.
    555      */
    556     public boolean isConferenceHost() {
    557         if (mImsCall == null) {
    558             return false;
    559         }
    560         return mImsCall.isConferenceHost();
    561     }
    562 
    563     /*package*/ ImsCall getImsCall() {
    564         return mImsCall;
    565     }
    566 
    567     /*package*/ void setImsCall(ImsCall imsCall) {
    568         mImsCall = imsCall;
    569     }
    570 
    571     /*package*/ void changeParent(ImsPhoneCall parent) {
    572         mParent = parent;
    573     }
    574 
    575     /**
    576      * @return {@code true} if the {@link ImsPhoneConnection} or its media capabilities have been
    577      *     changed, and {@code false} otherwise.
    578      */
    579     /*package*/ boolean update(ImsCall imsCall, ImsPhoneCall.State state) {
    580         if (state == ImsPhoneCall.State.ACTIVE) {
    581             // If the state of the call is active, but there is a pending request to the RIL to hold
    582             // the call, we will skip this update.  This is really a signalling delay or failure
    583             // from the RIL, but we will prevent it from going through as we will end up erroneously
    584             // making this call active when really it should be on hold.
    585             if (imsCall.isPendingHold()) {
    586                 Rlog.w(LOG_TAG, "update : state is ACTIVE, but call is pending hold, skipping");
    587                 return false;
    588             }
    589 
    590             if (mParent.getState().isRinging() || mParent.getState().isDialing()) {
    591                 onConnectedInOrOut();
    592             }
    593 
    594             if (mParent.getState().isRinging() || mParent == mOwner.mBackgroundCall) {
    595                 //mForegroundCall should be IDLE
    596                 //when accepting WAITING call
    597                 //before accept WAITING call,
    598                 //the ACTIVE call should be held ahead
    599                 mParent.detach(this);
    600                 mParent = mOwner.mForegroundCall;
    601                 mParent.attach(this);
    602             }
    603         } else if (state == ImsPhoneCall.State.HOLDING) {
    604             onStartedHolding();
    605         }
    606 
    607         boolean updateParent = mParent.update(this, imsCall, state);
    608         boolean updateWifiState = updateWifiState();
    609         boolean updateAddressDisplay = updateAddressDisplay(imsCall);
    610 
    611         return updateParent || updateWifiState || updateAddressDisplay;
    612     }
    613 
    614     @Override
    615     public int getPreciseDisconnectCause() {
    616         return 0;
    617     }
    618 
    619     /**
    620      * Notifies this Connection of a request to disconnect a participant of the conference managed
    621      * by the connection.
    622      *
    623      * @param endpoint the {@link android.net.Uri} of the participant to disconnect.
    624      */
    625     @Override
    626     public void onDisconnectConferenceParticipant(Uri endpoint) {
    627         ImsCall imsCall = getImsCall();
    628         if (imsCall == null) {
    629             return;
    630         }
    631         try {
    632             imsCall.removeParticipants(new String[]{endpoint.toString()});
    633         } catch (ImsException e) {
    634             // No session in place -- no change
    635             Rlog.e(LOG_TAG, "onDisconnectConferenceParticipant: no session in place. "+
    636                     "Failed to disconnect endpoint = " + endpoint);
    637         }
    638     }
    639 
    640     /**
    641      * Sets the conference connect time.  Used when an {@code ImsConference} is created to out of
    642      * this phone connection.
    643      *
    644      * @param conferenceConnectTime The conference connect time.
    645      */
    646     public void setConferenceConnectTime(long conferenceConnectTime) {
    647         mConferenceConnectTime = conferenceConnectTime;
    648     }
    649 
    650     /**
    651      * @return The conference connect time.
    652      */
    653     public long getConferenceConnectTime() {
    654         return mConferenceConnectTime;
    655     }
    656 
    657     /**
    658      * Check for a change in the address display related fields for the {@link ImsCall}, and
    659      * update the {@link ImsPhoneConnection} with this information.
    660      *
    661      * @param imsCall The call to check for changes in address display fields.
    662      * @return Whether the address display fields have been changed.
    663      */
    664     private boolean updateAddressDisplay(ImsCall imsCall) {
    665         if (imsCall == null) {
    666             return false;
    667         }
    668 
    669         boolean changed = false;
    670         ImsCallProfile callProfile = imsCall.getCallProfile();
    671         if (callProfile != null) {
    672             String address = callProfile.getCallExtra(ImsCallProfile.EXTRA_OI);
    673             String name = callProfile.getCallExtra(ImsCallProfile.EXTRA_CNA);
    674             int nump = ImsCallProfile.OIRToPresentation(
    675                     callProfile.getCallExtraInt(ImsCallProfile.EXTRA_OIR));
    676             int namep = ImsCallProfile.OIRToPresentation(
    677                     callProfile.getCallExtraInt(ImsCallProfile.EXTRA_CNAP));
    678             if (Phone.DEBUG_PHONE) {
    679                 Rlog.d(LOG_TAG, "address = " +  address + " name = " + name +
    680                         " nump = " + nump + " namep = " + namep);
    681             }
    682             if(equalsHandlesNulls(mAddress, address)) {
    683                 mAddress = address;
    684                 changed = true;
    685             }
    686             if (TextUtils.isEmpty(name)) {
    687                 if (!TextUtils.isEmpty(mCnapName)) {
    688                     mCnapName = "";
    689                     changed = true;
    690                 }
    691             } else if (!name.equals(mCnapName)) {
    692                 mCnapName = name;
    693                 changed = true;
    694             }
    695             if (mNumberPresentation != nump) {
    696                 mNumberPresentation = nump;
    697                 changed = true;
    698             }
    699             if (mCnapNamePresentation != namep) {
    700                 mCnapNamePresentation = namep;
    701                 changed = true;
    702             }
    703         }
    704         return changed;
    705     }
    706 
    707     /**
    708      * Check for a change in the video capabilities and audio quality for the {@link ImsCall}, and
    709      * update the {@link ImsPhoneConnection} with this information.
    710      *
    711      * @param imsCall The call to check for changes in media capabilities.
    712      * @return Whether the media capabilities have been changed.
    713      */
    714     public boolean updateMediaCapabilities(ImsCall imsCall) {
    715         if (imsCall == null) {
    716             return false;
    717         }
    718 
    719         boolean changed = false;
    720 
    721         try {
    722             // The actual call profile (negotiated between local and peer).
    723             ImsCallProfile negotiatedCallProfile = imsCall.getCallProfile();
    724             // The capabilities of the local device.
    725             ImsCallProfile localCallProfile = imsCall.getLocalCallProfile();
    726             // The capabilities of the peer device.
    727             ImsCallProfile remoteCallProfile = imsCall.getRemoteCallProfile();
    728 
    729             if (negotiatedCallProfile != null) {
    730                 int oldVideoState = getVideoState();
    731                 int newVideoState = ImsCallProfile
    732                         .getVideoStateFromImsCallProfile(negotiatedCallProfile);
    733 
    734                 if (oldVideoState != newVideoState) {
    735                     setVideoState(newVideoState);
    736                     changed = true;
    737                 }
    738             }
    739 
    740             if (localCallProfile != null) {
    741                 int callType = localCallProfile.mCallType;
    742 
    743                 boolean newLocalVideoCapable = callType == ImsCallProfile.CALL_TYPE_VT;
    744                 if (isLocalVideoCapable() != newLocalVideoCapable) {
    745                     setLocalVideoCapable(newLocalVideoCapable);
    746                     changed = true;
    747                 }
    748             }
    749 
    750             if (remoteCallProfile != null) {
    751                     boolean newRemoteVideoCapable = remoteCallProfile.mCallType
    752                             == ImsCallProfile.CALL_TYPE_VT;
    753 
    754                     if (isRemoteVideoCapable() != newRemoteVideoCapable) {
    755                         setRemoteVideoCapable(newRemoteVideoCapable);
    756                         changed = true;
    757                     }
    758             }
    759 
    760             int newAudioQuality =
    761                     getAudioQualityFromCallProfile(localCallProfile, remoteCallProfile);
    762             if (getAudioQuality() != newAudioQuality) {
    763                 setAudioQuality(newAudioQuality);
    764                 changed = true;
    765             }
    766         } catch (ImsException e) {
    767             // No session in place -- no change
    768         }
    769 
    770         return changed;
    771     }
    772 
    773     /**
    774      * Check for a change in the wifi state of the ImsPhoneCallTracker and update the
    775      * {@link ImsPhoneConnection} with this information.
    776      *
    777      * @return Whether the ImsPhoneCallTracker's usage of wifi has been changed.
    778      */
    779     public boolean updateWifiState() {
    780         Rlog.d(LOG_TAG, "updateWifiState: " + mOwner.isVowifiEnabled());
    781         if (isWifi() != mOwner.isVowifiEnabled()) {
    782             setWifi(mOwner.isVowifiEnabled());
    783             return true;
    784         }
    785         return false;
    786     }
    787 
    788     /**
    789      * Determines the {@link ImsPhoneConnection} audio quality based on the local and remote
    790      * {@link ImsCallProfile}. If indicate a HQ audio call if the local stream profile
    791      * indicates AMR_WB or EVRC_WB and there is no remote restrict cause.
    792      *
    793      * @param localCallProfile The local call profile.
    794      * @param remoteCallProfile The remote call profile.
    795      * @return The audio quality.
    796      */
    797     private int getAudioQualityFromCallProfile(
    798             ImsCallProfile localCallProfile, ImsCallProfile remoteCallProfile) {
    799         if (localCallProfile == null || remoteCallProfile == null
    800                 || localCallProfile.mMediaProfile == null) {
    801             return AUDIO_QUALITY_STANDARD;
    802         }
    803 
    804         boolean isHighDef = (localCallProfile.mMediaProfile.mAudioQuality
    805                         == ImsStreamMediaProfile.AUDIO_QUALITY_AMR_WB
    806                 || localCallProfile.mMediaProfile.mAudioQuality
    807                         == ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_WB)
    808                 && remoteCallProfile.mRestrictCause == ImsCallProfile.CALL_RESTRICT_CAUSE_NONE;
    809         return isHighDef ? AUDIO_QUALITY_HIGH_DEFINITION : AUDIO_QUALITY_STANDARD;
    810     }
    811 
    812     /**
    813      * Provides a string representation of the {@link ImsPhoneConnection}.  Primarily intended for
    814      * use in log statements.
    815      *
    816      * @return String representation of call.
    817      */
    818     @Override
    819     public String toString() {
    820         StringBuilder sb = new StringBuilder();
    821         sb.append("[ImsPhoneConnection objId: ");
    822         sb.append(System.identityHashCode(this));
    823         sb.append(" address:");
    824         sb.append(Log.pii(getAddress()));
    825         sb.append(" ImsCall:");
    826         if (mImsCall == null) {
    827             sb.append("null");
    828         } else {
    829             sb.append(mImsCall);
    830         }
    831         sb.append("]");
    832         return sb.toString();
    833     }
    834 }
    835 
    836