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