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