ImsPhoneConnection.java revision 4611e65ec532915b3a6968459d68e5a2dc3a08c8
1a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville/*
2a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville * Copyright (C) 2013 The Android Open Source Project
3a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville *
4a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville * Licensed under the Apache License, Version 2.0 (the "License");
5a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville * you may not use this file except in compliance with the License.
6a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville * You may obtain a copy of the License at
7a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville *
8a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville *      http://www.apache.org/licenses/LICENSE-2.0
9a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville *
10a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville * Unless required by applicable law or agreed to in writing, software
11a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville * distributed under the License is distributed on an "AS IS" BASIS,
12a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville * See the License for the specific language governing permissions and
14a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville * limitations under the License.
15a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville */
16a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
17a8467dd0c524787104b1ccdddc5e8af10ba729edWink Savillepackage com.android.internal.telephony.imsphone;
18a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
19a8467dd0c524787104b1ccdddc5e8af10ba729edWink Savilleimport android.content.Context;
20d7f4ed8ac190763dce7e57b737caa44654b4592aTyler Gunnimport android.net.Uri;
21a8467dd0c524787104b1ccdddc5e8af10ba729edWink Savilleimport android.os.AsyncResult;
2208e9c4b483dac2cbf1b7fe4cbad22f003ff92bcbShriram Ganeshimport android.os.Bundle;
23a8467dd0c524787104b1ccdddc5e8af10ba729edWink Savilleimport android.os.Handler;
24a8467dd0c524787104b1ccdddc5e8af10ba729edWink Savilleimport android.os.Looper;
25a8467dd0c524787104b1ccdddc5e8af10ba729edWink Savilleimport android.os.Message;
266bb557b243f3fc9984b82319026519608ada2c9cRoshan Piusimport android.os.PersistableBundle;
27a8467dd0c524787104b1ccdddc5e8af10ba729edWink Savilleimport android.os.PowerManager;
28a8467dd0c524787104b1ccdddc5e8af10ba729edWink Savilleimport android.os.Registrant;
29a8467dd0c524787104b1ccdddc5e8af10ba729edWink Savilleimport android.os.SystemClock;
30db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunnimport android.telecom.VideoProfile;
316bb557b243f3fc9984b82319026519608ada2c9cRoshan Piusimport android.telephony.CarrierConfigManager;
32a8467dd0c524787104b1ccdddc5e8af10ba729edWink Savilleimport android.telephony.DisconnectCause;
33a8467dd0c524787104b1ccdddc5e8af10ba729edWink Savilleimport android.telephony.PhoneNumberUtils;
34a8467dd0c524787104b1ccdddc5e8af10ba729edWink Savilleimport android.telephony.Rlog;
352e66c6325d321e41b8851da9a4596f7a745c7b1eTyler Gunnimport android.telephony.ServiceState;
36eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganeshimport android.text.TextUtils;
37a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
38286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunnimport com.android.ims.ImsCall;
391846615c7cb3d995efc8788b60aa1f5240de8b8fBrad Ebingerimport android.telephony.ims.ImsCallProfile;
402999afbcfeab69bf7473e1b9bcabb1c9c6935b19Tyler Gunnimport com.android.ims.ImsException;
411846615c7cb3d995efc8788b60aa1f5240de8b8fBrad Ebingerimport android.telephony.ims.ImsStreamMediaProfile;
42db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunnimport com.android.ims.internal.ImsVideoCallProviderWrapper;
43a8467dd0c524787104b1ccdddc5e8af10ba729edWink Savilleimport com.android.internal.telephony.CallStateException;
44a8467dd0c524787104b1ccdddc5e8af10ba729edWink Savilleimport com.android.internal.telephony.Connection;
45a8467dd0c524787104b1ccdddc5e8af10ba729edWink Savilleimport com.android.internal.telephony.Phone;
46a8467dd0c524787104b1ccdddc5e8af10ba729edWink Savilleimport com.android.internal.telephony.PhoneConstants;
47a8467dd0c524787104b1ccdddc5e8af10ba729edWink Savilleimport com.android.internal.telephony.UUSInfo;
48a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
4921048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkarimport java.util.Objects;
5021048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar
51a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville/**
52a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville * {@hide}
53a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville */
54db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunnpublic class ImsPhoneConnection extends Connection implements
55db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn        ImsVideoCallProviderWrapper.ImsVideoProviderWrapperCallback {
56db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn
57a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    private static final String LOG_TAG = "ImsPhoneConnection";
58a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    private static final boolean DBG = true;
59a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
60a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    //***** Instance Variables
61a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
62a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    private ImsPhoneCallTracker mOwner;
63a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    private ImsPhoneCall mParent;
64a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    private ImsCall mImsCall;
6521048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar    private Bundle mExtras = new Bundle();
66a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
67a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    private boolean mDisconnected;
68a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
69a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    /*
70a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    int mIndex;          // index in ImsPhoneCallTracker.connections[], -1 if unassigned
71a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                        // The GSM index is 1 + this
72a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    */
73a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
74a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    /*
75a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville     * These time/timespan values are based on System.currentTimeMillis(),
76a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville     * i.e., "wall clock" time.
77a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville     */
78a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    private long mDisconnectTime;
79a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
80a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    private UUSInfo mUusInfo;
81a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    private Handler mHandler;
82a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
83a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    private PowerManager.WakeLock mPartialWakeLock;
84a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
85fd2305639e3761c2bf3213463c4f17cc8310f4d0Tyler Gunn    // The cached connect time of the connection when it turns into a conference.
86fd2305639e3761c2bf3213463c4f17cc8310f4d0Tyler Gunn    private long mConferenceConnectTime = 0;
87fd2305639e3761c2bf3213463c4f17cc8310f4d0Tyler Gunn
886bb557b243f3fc9984b82319026519608ada2c9cRoshan Pius    // The cached delay to be used between DTMF tones fetched from carrier config.
896bb557b243f3fc9984b82319026519608ada2c9cRoshan Pius    private int mDtmfToneDelay = 0;
906bb557b243f3fc9984b82319026519608ada2c9cRoshan Pius
918bfa1b42f0ffebf33239546ceaf5d6f5ca813191Amit Mahajan    private boolean mIsEmergency = false;
928bfa1b42f0ffebf33239546ceaf5d6f5ca813191Amit Mahajan
932e66c6325d321e41b8851da9a4596f7a745c7b1eTyler Gunn    /**
94db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn     * Used to indicate that video state changes detected by
95db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn     * {@link #updateMediaCapabilities(ImsCall)} should be ignored.  When a video state change from
96db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn     * unpaused to paused occurs, we set this flag and then update the existing video state when
97db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn     * new {@link #onReceiveSessionModifyResponse(int, VideoProfile, VideoProfile)} callbacks come
98db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn     * in.  When the video un-pauses we continue receiving the video state updates.
99db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn     */
100db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn    private boolean mShouldIgnoreVideoStateChanges = false;
101db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn
102359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn    private ImsVideoCallProviderWrapper mImsVideoCallProviderWrapper;
103359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn
104222bd9d64068a23f5470561655ca4dbd2359eeceJayachandran C    private int mPreciseDisconnectCause = 0;
105222bd9d64068a23f5470561655ca4dbd2359eeceJayachandran C
106fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu    private ImsRttTextHandler mRttTextHandler;
107fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu    private android.telecom.Connection.RttTextStream mRttTextStream;
108fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu
109286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn    /**
110286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn     * Used to indicate that this call is in the midst of being merged into a conference.
111286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn     */
112286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn    private boolean mIsMergeInProcess = false;
113286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn
11463fa599fef0383b41f85e1fc3a38d33c4434e24aTyler Gunn    /**
1157a411d4797fb60914f5f6230bde24fc25b12f920Tyler Gunn     * Used as an override to determine whether video is locally available for this call.
1167a411d4797fb60914f5f6230bde24fc25b12f920Tyler Gunn     * This allows video availability to be overridden in the case that the modem says video is
1177a411d4797fb60914f5f6230bde24fc25b12f920Tyler Gunn     * currently available, but mobile data is off and the carrier is metering data for video
1187a411d4797fb60914f5f6230bde24fc25b12f920Tyler Gunn     * calls.
1197a411d4797fb60914f5f6230bde24fc25b12f920Tyler Gunn     */
1207a411d4797fb60914f5f6230bde24fc25b12f920Tyler Gunn    private boolean mIsVideoEnabled = true;
1217a411d4797fb60914f5f6230bde24fc25b12f920Tyler Gunn
122a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    //***** Event Constants
123a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    private static final int EVENT_DTMF_DONE = 1;
124a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    private static final int EVENT_PAUSE_DONE = 2;
125a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    private static final int EVENT_NEXT_POST_DIAL = 3;
126a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    private static final int EVENT_WAKE_LOCK_TIMEOUT = 4;
1276bb557b243f3fc9984b82319026519608ada2c9cRoshan Pius    private static final int EVENT_DTMF_DELAY_DONE = 5;
128a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
129a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    //***** Constants
130a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    private static final int PAUSE_DELAY_MILLIS = 3 * 1000;
131a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    private static final int WAKE_LOCK_TIMEOUT_MILLIS = 60*1000;
132a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
133a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    //***** Inner Classes
134a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
135a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    class MyHandler extends Handler {
136a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        MyHandler(Looper l) {super(l);}
137a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
138a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        @Override
139a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        public void
140a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        handleMessage(Message msg) {
141a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
142a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            switch (msg.what) {
143a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                case EVENT_NEXT_POST_DIAL:
1446bb557b243f3fc9984b82319026519608ada2c9cRoshan Pius                case EVENT_DTMF_DELAY_DONE:
145a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                case EVENT_PAUSE_DONE:
146a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                    processNextPostDialChar();
147a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                    break;
148a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                case EVENT_WAKE_LOCK_TIMEOUT:
149a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                    releaseWakeLock();
150a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                    break;
1516bb557b243f3fc9984b82319026519608ada2c9cRoshan Pius                case EVENT_DTMF_DONE:
1526bb557b243f3fc9984b82319026519608ada2c9cRoshan Pius                    // We may need to add a delay specified by carrier between DTMF tones that are
1536bb557b243f3fc9984b82319026519608ada2c9cRoshan Pius                    // sent out.
1546bb557b243f3fc9984b82319026519608ada2c9cRoshan Pius                    mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_DTMF_DELAY_DONE),
1556bb557b243f3fc9984b82319026519608ada2c9cRoshan Pius                            mDtmfToneDelay);
1566bb557b243f3fc9984b82319026519608ada2c9cRoshan Pius                    break;
157a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            }
158a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        }
159a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
160a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
161a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    //***** Constructors
162a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
163a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    /** This is probably an MT call */
164f9b150b221db15e88b97a0b9c0971ca3c71c0313fionaxu    public ImsPhoneConnection(Phone phone, ImsCall imsCall, ImsPhoneCallTracker ct,
16508e9c4b483dac2cbf1b7fe4cbad22f003ff92bcbShriram Ganesh           ImsPhoneCall parent, boolean isUnknown) {
166c2b66d57e16623dffc2fe6d02ded4f24c014b6c8Amit Mahajan        super(PhoneConstants.PHONE_TYPE_IMS);
1676bb557b243f3fc9984b82319026519608ada2c9cRoshan Pius        createWakeLock(phone.getContext());
168a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        acquireWakeLock();
169a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
170a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mOwner = ct;
171a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mHandler = new MyHandler(mOwner.getLooper());
172a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mImsCall = imsCall;
173a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
174a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        if ((imsCall != null) && (imsCall.getCallProfile() != null)) {
175a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            mAddress = imsCall.getCallProfile().getCallExtra(ImsCallProfile.EXTRA_OI);
176a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            mCnapName = imsCall.getCallProfile().getCallExtra(ImsCallProfile.EXTRA_CNA);
177d325833e9248c05305b1edabb1d8efc827803f75Jing Zhao            mNumberPresentation = ImsCallProfile.OIRToPresentation(
178a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                    imsCall.getCallProfile().getCallExtraInt(ImsCallProfile.EXTRA_OIR));
179d325833e9248c05305b1edabb1d8efc827803f75Jing Zhao            mCnapNamePresentation = ImsCallProfile.OIRToPresentation(
180a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                    imsCall.getCallProfile().getCallExtraInt(ImsCallProfile.EXTRA_CNAP));
1816a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee            updateMediaCapabilities(imsCall);
182a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        } else {
183a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            mNumberPresentation = PhoneConstants.PRESENTATION_UNKNOWN;
184a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            mCnapNamePresentation = PhoneConstants.PRESENTATION_UNKNOWN;
185a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        }
186a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
18708e9c4b483dac2cbf1b7fe4cbad22f003ff92bcbShriram Ganesh        mIsIncoming = !isUnknown;
188a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mCreateTime = System.currentTimeMillis();
189a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mUusInfo = null;
190a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
191553db651cd5a51eedde0259d5a25e91700ef96c5Tyler Gunn        // Ensure any extras set on the ImsCallProfile at the start of the call are cached locally
192553db651cd5a51eedde0259d5a25e91700ef96c5Tyler Gunn        // in the ImsPhoneConnection.  This isn't going to inform any listeners (since the original
193553db651cd5a51eedde0259d5a25e91700ef96c5Tyler Gunn        // connection is not likely to be associated with a TelephonyConnection yet).
194553db651cd5a51eedde0259d5a25e91700ef96c5Tyler Gunn        updateExtras(imsCall);
195553db651cd5a51eedde0259d5a25e91700ef96c5Tyler Gunn
196a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mParent = parent;
19708e9c4b483dac2cbf1b7fe4cbad22f003ff92bcbShriram Ganesh        mParent.attach(this,
19808e9c4b483dac2cbf1b7fe4cbad22f003ff92bcbShriram Ganesh                (mIsIncoming? ImsPhoneCall.State.INCOMING: ImsPhoneCall.State.DIALING));
1996bb557b243f3fc9984b82319026519608ada2c9cRoshan Pius
2006bb557b243f3fc9984b82319026519608ada2c9cRoshan Pius        fetchDtmfToneDelay(phone);
201c6e763fa8aa1121f9c5aef16368115b06e31c6a2Jimmy Wu
202c6e763fa8aa1121f9c5aef16368115b06e31c6a2Jimmy Wu        if (phone.getContext().getResources().getBoolean(
203c6e763fa8aa1121f9c5aef16368115b06e31c6a2Jimmy Wu                com.android.internal.R.bool.config_use_voip_mode_for_ims)) {
204c6e763fa8aa1121f9c5aef16368115b06e31c6a2Jimmy Wu            setAudioModeIsVoip(true);
205c6e763fa8aa1121f9c5aef16368115b06e31c6a2Jimmy Wu        }
206a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
207a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
208a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    /** This is an MO call, created when dialing */
209f9b150b221db15e88b97a0b9c0971ca3c71c0313fionaxu    public ImsPhoneConnection(Phone phone, String dialString, ImsPhoneCallTracker ct,
2108bfa1b42f0ffebf33239546ceaf5d6f5ca813191Amit Mahajan            ImsPhoneCall parent, boolean isEmergency) {
211c2b66d57e16623dffc2fe6d02ded4f24c014b6c8Amit Mahajan        super(PhoneConstants.PHONE_TYPE_IMS);
2126bb557b243f3fc9984b82319026519608ada2c9cRoshan Pius        createWakeLock(phone.getContext());
213a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        acquireWakeLock();
214a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
215a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mOwner = ct;
216a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mHandler = new MyHandler(mOwner.getLooper());
217a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
218a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mDialString = dialString;
219a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
220a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mAddress = PhoneNumberUtils.extractNetworkPortionAlt(dialString);
221a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mPostDialString = PhoneNumberUtils.extractPostDialPortion(dialString);
222a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
223a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        //mIndex = -1;
224a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
225a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mIsIncoming = false;
226a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mCnapName = null;
227a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mCnapNamePresentation = PhoneConstants.PRESENTATION_ALLOWED;
228a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mNumberPresentation = PhoneConstants.PRESENTATION_ALLOWED;
229a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mCreateTime = System.currentTimeMillis();
230a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
231a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mParent = parent;
232a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        parent.attachFake(this, ImsPhoneCall.State.DIALING);
2336bb557b243f3fc9984b82319026519608ada2c9cRoshan Pius
2348bfa1b42f0ffebf33239546ceaf5d6f5ca813191Amit Mahajan        mIsEmergency = isEmergency;
2358bfa1b42f0ffebf33239546ceaf5d6f5ca813191Amit Mahajan
2366bb557b243f3fc9984b82319026519608ada2c9cRoshan Pius        fetchDtmfToneDelay(phone);
237c6e763fa8aa1121f9c5aef16368115b06e31c6a2Jimmy Wu
238c6e763fa8aa1121f9c5aef16368115b06e31c6a2Jimmy Wu        if (phone.getContext().getResources().getBoolean(
239c6e763fa8aa1121f9c5aef16368115b06e31c6a2Jimmy Wu                com.android.internal.R.bool.config_use_voip_mode_for_ims)) {
240c6e763fa8aa1121f9c5aef16368115b06e31c6a2Jimmy Wu            setAudioModeIsVoip(true);
241c6e763fa8aa1121f9c5aef16368115b06e31c6a2Jimmy Wu        }
242a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
243a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
244a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    public void dispose() {
245a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
246a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
247a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    static boolean
248a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    equalsHandlesNulls (Object a, Object b) {
249a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        return (a == null) ? (b == null) : a.equals (b);
250a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
251a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
25274c8509aede6fe748904e75c156049b4f1ba5e28Hanada Masafumi    static boolean
25374c8509aede6fe748904e75c156049b4f1ba5e28Hanada Masafumi    equalsBaseDialString (String a, String b) {
25474c8509aede6fe748904e75c156049b4f1ba5e28Hanada Masafumi        return (a == null) ? (b == null) : (b != null && a.startsWith (b));
25574c8509aede6fe748904e75c156049b4f1ba5e28Hanada Masafumi    }
25674c8509aede6fe748904e75c156049b4f1ba5e28Hanada Masafumi
2577a411d4797fb60914f5f6230bde24fc25b12f920Tyler Gunn    private int applyLocalCallCapabilities(ImsCallProfile localProfile, int capabilities) {
2587a411d4797fb60914f5f6230bde24fc25b12f920Tyler Gunn        Rlog.i(LOG_TAG, "applyLocalCallCapabilities - localProfile = " + localProfile);
259f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn        capabilities = removeCapability(capabilities,
2602e7e09e79c04f34034bebea8f5d86606bb584577Tyler Gunn                Connection.Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL);
261f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn
2627a411d4797fb60914f5f6230bde24fc25b12f920Tyler Gunn        if (!mIsVideoEnabled) {
2637a411d4797fb60914f5f6230bde24fc25b12f920Tyler Gunn            Rlog.i(LOG_TAG, "applyLocalCallCapabilities - disabling video (overidden)");
2647a411d4797fb60914f5f6230bde24fc25b12f920Tyler Gunn            return capabilities;
2657a411d4797fb60914f5f6230bde24fc25b12f920Tyler Gunn        }
266f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn        switch (localProfile.mCallType) {
267f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn            case ImsCallProfile.CALL_TYPE_VT:
2682e7e09e79c04f34034bebea8f5d86606bb584577Tyler Gunn                // Fall-through
269f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn            case ImsCallProfile.CALL_TYPE_VIDEO_N_VOICE:
270f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn                capabilities = addCapability(capabilities,
2712e7e09e79c04f34034bebea8f5d86606bb584577Tyler Gunn                        Connection.Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL);
272f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn                break;
273f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn        }
274f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn        return capabilities;
275f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn    }
276f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn
277f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn    private static int applyRemoteCallCapabilities(ImsCallProfile remoteProfile, int capabilities) {
2782e7e09e79c04f34034bebea8f5d86606bb584577Tyler Gunn        Rlog.w(LOG_TAG, "applyRemoteCallCapabilities - remoteProfile = "+remoteProfile);
279f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn        capabilities = removeCapability(capabilities,
2802e7e09e79c04f34034bebea8f5d86606bb584577Tyler Gunn                Connection.Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
281f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn
282f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn        switch (remoteProfile.mCallType) {
283f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn            case ImsCallProfile.CALL_TYPE_VT:
2842e7e09e79c04f34034bebea8f5d86606bb584577Tyler Gunn                // fall-through
285f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn            case ImsCallProfile.CALL_TYPE_VIDEO_N_VOICE:
286f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn                capabilities = addCapability(capabilities,
2872e7e09e79c04f34034bebea8f5d86606bb584577Tyler Gunn                        Connection.Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
288f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn                break;
289f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn        }
290f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn        return capabilities;
291f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn    }
292f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn
293a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    @Override
294a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    public String getOrigDialString(){
295a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        return mDialString;
296a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
297a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
298a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    @Override
299a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    public ImsPhoneCall getCall() {
300a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        return mParent;
301a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
302a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
303a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    @Override
304a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    public long getDisconnectTime() {
305a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        return mDisconnectTime;
306a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
307a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
308a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    @Override
309a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    public long getHoldingStartTime() {
310a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        return mHoldingStartTime;
311a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
312a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
313a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    @Override
314a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    public long getHoldDurationMillis() {
315a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        if (getState() != ImsPhoneCall.State.HOLDING) {
316a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            // If not holding, return 0
317a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            return 0;
318a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        } else {
319a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            return SystemClock.elapsedRealtime() - mHoldingStartTime;
320a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        }
321a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
322a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
323a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    public void setDisconnectCause(int cause) {
324a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mCause = cause;
325a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
326a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
32733cfb500a7cf192adfc5ca06792fe3847073b6c1Chao Liu    @Override
32833cfb500a7cf192adfc5ca06792fe3847073b6c1Chao Liu    public String getVendorDisconnectCause() {
32933cfb500a7cf192adfc5ca06792fe3847073b6c1Chao Liu      return null;
33033cfb500a7cf192adfc5ca06792fe3847073b6c1Chao Liu    }
33133cfb500a7cf192adfc5ca06792fe3847073b6c1Chao Liu
332a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    public ImsPhoneCallTracker getOwner () {
333a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        return mOwner;
334a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
335a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
336a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    @Override
337a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    public ImsPhoneCall.State getState() {
338a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        if (mDisconnected) {
339a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            return ImsPhoneCall.State.DISCONNECTED;
340a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        } else {
341a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            return super.getState();
342a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        }
343a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
344a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
345a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    @Override
3464611e65ec532915b3a6968459d68e5a2dc3a08c8Pooja Jain    public void deflect(String number) throws CallStateException {
3474611e65ec532915b3a6968459d68e5a2dc3a08c8Pooja Jain        if (mParent.getState().isRinging()) {
3484611e65ec532915b3a6968459d68e5a2dc3a08c8Pooja Jain            try {
3494611e65ec532915b3a6968459d68e5a2dc3a08c8Pooja Jain                if (mImsCall != null) {
3504611e65ec532915b3a6968459d68e5a2dc3a08c8Pooja Jain                    mImsCall.deflect(number);
3514611e65ec532915b3a6968459d68e5a2dc3a08c8Pooja Jain                } else {
3524611e65ec532915b3a6968459d68e5a2dc3a08c8Pooja Jain                    throw new CallStateException("no valid ims call to deflect");
3534611e65ec532915b3a6968459d68e5a2dc3a08c8Pooja Jain                }
3544611e65ec532915b3a6968459d68e5a2dc3a08c8Pooja Jain            } catch (ImsException e) {
3554611e65ec532915b3a6968459d68e5a2dc3a08c8Pooja Jain                throw new CallStateException("cannot deflect call");
3564611e65ec532915b3a6968459d68e5a2dc3a08c8Pooja Jain            }
3574611e65ec532915b3a6968459d68e5a2dc3a08c8Pooja Jain        } else {
3584611e65ec532915b3a6968459d68e5a2dc3a08c8Pooja Jain            throw new CallStateException("phone not ringing");
3594611e65ec532915b3a6968459d68e5a2dc3a08c8Pooja Jain        }
3604611e65ec532915b3a6968459d68e5a2dc3a08c8Pooja Jain    }
3614611e65ec532915b3a6968459d68e5a2dc3a08c8Pooja Jain
3624611e65ec532915b3a6968459d68e5a2dc3a08c8Pooja Jain    @Override
363a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    public void hangup() throws CallStateException {
364a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        if (!mDisconnected) {
365a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            mOwner.hangup(this);
366a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        } else {
367a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            throw new CallStateException ("disconnected");
368a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        }
369a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
370a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
371a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    @Override
372a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    public void separate() throws CallStateException {
373a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        throw new CallStateException ("not supported");
374a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
375a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
376a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    @Override
377a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    public void proceedAfterWaitChar() {
378a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        if (mPostDialState != PostDialState.WAIT) {
379a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            Rlog.w(LOG_TAG, "ImsPhoneConnection.proceedAfterWaitChar(): Expected "
380a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                    + "getPostDialState() to be WAIT but was " + mPostDialState);
381a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            return;
382a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        }
383a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
384a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        setPostDialState(PostDialState.STARTED);
385a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
386a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        processNextPostDialChar();
387a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
388a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
389a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    @Override
390a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    public void proceedAfterWildChar(String str) {
391a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        if (mPostDialState != PostDialState.WILD) {
392a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            Rlog.w(LOG_TAG, "ImsPhoneConnection.proceedAfterWaitChar(): Expected "
393a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                    + "getPostDialState() to be WILD but was " + mPostDialState);
394a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            return;
395a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        }
396a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
397a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        setPostDialState(PostDialState.STARTED);
398a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
399a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        // make a new postDialString, with the wild char replacement string
400a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        // at the beginning, followed by the remaining postDialString.
401a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
402a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        StringBuilder buf = new StringBuilder(str);
403a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        buf.append(mPostDialString.substring(mNextPostDialChar));
404a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mPostDialString = buf.toString();
405a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mNextPostDialChar = 0;
406a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        if (Phone.DEBUG_PHONE) {
407a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            Rlog.d(LOG_TAG, "proceedAfterWildChar: new postDialString is " +
408a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                    mPostDialString);
409a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        }
410a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
411a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        processNextPostDialChar();
412a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
413a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
414a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    @Override
415a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    public void cancelPostDial() {
416a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        setPostDialState(PostDialState.CANCELLED);
417a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
418a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
419a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    /**
420a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville     * Called when this Connection is being hung up locally (eg, user pressed "end")
421a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville     */
422a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    void
423a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    onHangupLocal() {
424a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mCause = DisconnectCause.LOCAL;
425a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
426a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
427a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    /** Called when the connection has been disconnected */
428c2b66d57e16623dffc2fe6d02ded4f24c014b6c8Amit Mahajan    @Override
429c2b66d57e16623dffc2fe6d02ded4f24c014b6c8Amit Mahajan    public boolean onDisconnect(int cause) {
430a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        Rlog.d(LOG_TAG, "onDisconnect: cause=" + cause);
4311999b9772eb9b3a230bd2520ffebb152544eaba9Tyler Gunn        if (mCause != DisconnectCause.LOCAL || cause == DisconnectCause.INCOMING_REJECTED) {
4321999b9772eb9b3a230bd2520ffebb152544eaba9Tyler Gunn            mCause = cause;
4331999b9772eb9b3a230bd2520ffebb152544eaba9Tyler Gunn        }
434a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        return onDisconnect();
435a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
436a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
437f9b150b221db15e88b97a0b9c0971ca3c71c0313fionaxu    public boolean onDisconnect() {
438a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        boolean changed = false;
439a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
440a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        if (!mDisconnected) {
441a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            //mIndex = -1;
442a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
443a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            mDisconnectTime = System.currentTimeMillis();
444a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            mDuration = SystemClock.elapsedRealtime() - mConnectTimeReal;
445a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            mDisconnected = true;
446a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
447a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            mOwner.mPhone.notifyDisconnect(this);
44847b078c27bfb57477a4ff1ce12abc4e9632c1dd5Tyler Gunn            notifyDisconnect(mCause);
449a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
450a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            if (mParent != null) {
451a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                changed = mParent.connectionDisconnected(this);
452a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            } else {
453a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                Rlog.d(LOG_TAG, "onDisconnect: no parent");
454a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            }
45534310e958f52099ce3a2e74a8e3c4f631a241529Hall Liu            synchronized (this) {
45634310e958f52099ce3a2e74a8e3c4f631a241529Hall Liu                if (mImsCall != null) mImsCall.close();
45734310e958f52099ce3a2e74a8e3c4f631a241529Hall Liu                mImsCall = null;
45834310e958f52099ce3a2e74a8e3c4f631a241529Hall Liu            }
459a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        }
460a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        releaseWakeLock();
461a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        return changed;
462a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
463a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
464a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    /**
465a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville     * An incoming or outgoing call has connected
466a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville     */
467a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    void
468a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    onConnectedInOrOut() {
469a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mConnectTime = System.currentTimeMillis();
470a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mConnectTimeReal = SystemClock.elapsedRealtime();
471a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mDuration = 0;
472a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
473a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        if (Phone.DEBUG_PHONE) {
474a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            Rlog.d(LOG_TAG, "onConnectedInOrOut: connectTime=" + mConnectTime);
475a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        }
476a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
477a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        if (!mIsIncoming) {
478a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            // outgoing calls only
479a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            processNextPostDialChar();
480a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        }
481a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        releaseWakeLock();
482a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
483a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
484a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    /*package*/ void
485a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    onStartedHolding() {
486a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mHoldingStartTime = SystemClock.elapsedRealtime();
487a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
488a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    /**
489a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville     * Performs the appropriate action for a post-dial char, but does not
490a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville     * notify application. returns false if the character is invalid and
491a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville     * should be ignored
492a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville     */
493a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    private boolean
494a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    processPostDialChar(char c) {
495a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        if (PhoneNumberUtils.is12Key(c)) {
496d9aa1a75304b1c04c352198b9269f40a2a059f74Andrew Lee            mOwner.sendDtmf(c, mHandler.obtainMessage(EVENT_DTMF_DONE));
497a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        } else if (c == PhoneNumberUtils.PAUSE) {
498a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            // From TS 22.101:
499a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            // It continues...
500a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            // Upon the called party answering the UE shall send the DTMF digits
501a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            // automatically to the network after a delay of 3 seconds( 20 ).
502a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            // The digits shall be sent according to the procedures and timing
503a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            // specified in 3GPP TS 24.008 [13]. The first occurrence of the
504a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            // "DTMF Control Digits Separator" shall be used by the ME to
505a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            // distinguish between the addressing digits (i.e. the phone number)
506a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            // and the DTMF digits. Upon subsequent occurrences of the
507a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            // separator,
508a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            // the UE shall pause again for 3 seconds ( 20 ) before sending
509a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            // any further DTMF digits.
510a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_PAUSE_DONE),
511a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                    PAUSE_DELAY_MILLIS);
512a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        } else if (c == PhoneNumberUtils.WAIT) {
513a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            setPostDialState(PostDialState.WAIT);
514a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        } else if (c == PhoneNumberUtils.WILD) {
515a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            setPostDialState(PostDialState.WILD);
516a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        } else {
517a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            return false;
518a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        }
519a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
520a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        return true;
521a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
522a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
523a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    @Override
524032d73c15281387a97cdb91c6f0c7c3a1b1b230eAmit Mahajan    protected void finalize() {
525a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        releaseWakeLock();
526a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
527a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
528a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    private void
529a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    processNextPostDialChar() {
530a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        char c = 0;
531a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        Registrant postDialHandler;
532a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
533a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        if (mPostDialState == PostDialState.CANCELLED) {
534a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            //Rlog.d(LOG_TAG, "##### processNextPostDialChar: postDialState == CANCELLED, bail");
535a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            return;
536a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        }
537a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
538d9aa1a75304b1c04c352198b9269f40a2a059f74Andrew Lee        if (mPostDialString == null || mPostDialString.length() <= mNextPostDialChar) {
539a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            setPostDialState(PostDialState.COMPLETE);
540a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
541a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            // notifyMessage.arg1 is 0 on complete
542a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            c = 0;
543a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        } else {
544a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            boolean isValid;
545a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
546a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            setPostDialState(PostDialState.STARTED);
547a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
548a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            c = mPostDialString.charAt(mNextPostDialChar++);
549a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
550a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            isValid = processPostDialChar(c);
551a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
552a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            if (!isValid) {
553a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                // Will call processNextPostDialChar
554a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                mHandler.obtainMessage(EVENT_NEXT_POST_DIAL).sendToTarget();
555a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                // Don't notify application
556a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                Rlog.e(LOG_TAG, "processNextPostDialChar: c=" + c + " isn't valid!");
557a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                return;
558a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            }
559a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        }
560a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
5614567a0789e9966929c71af9a2c3866582c85c9e0Nancy Chen        notifyPostDialListenersNextChar(c);
5624567a0789e9966929c71af9a2c3866582c85c9e0Nancy Chen
5634567a0789e9966929c71af9a2c3866582c85c9e0Nancy Chen        // TODO: remove the following code since the handler no longer executes anything.
56493c62c8a71821f46194e16ca3e84f95e101edb90Amit Mahajan        postDialHandler = mOwner.mPhone.getPostDialHandler();
565a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
566a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        Message notifyMessage;
567a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
568a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        if (postDialHandler != null
569a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                && (notifyMessage = postDialHandler.messageForRegistrant()) != null) {
570a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            // The AsyncResult.result is the Connection object
571a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            PostDialState state = mPostDialState;
572a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            AsyncResult ar = AsyncResult.forMessage(notifyMessage);
573a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            ar.result = this;
574a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            ar.userObj = state;
575a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
576a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            // arg1 is the character that was/is being processed
577a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            notifyMessage.arg1 = c;
578a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
579d9c3a08d5958e5ced0c0c9f7aabf376a5fe59bb2Anju Mathapati            //Rlog.v(LOG_TAG,
580d9c3a08d5958e5ced0c0c9f7aabf376a5fe59bb2Anju Mathapati            //      "##### processNextPostDialChar: send msg to postDialHandler, arg1=" + c);
581a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            notifyMessage.sendToTarget();
582a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        }
583a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
584a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
585a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    /**
586a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville     * Set post dial state and acquire wake lock while switching to "started"
587a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville     * state, the wake lock will be released if state switches out of "started"
588a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville     * state or after WAKE_LOCK_TIMEOUT_MILLIS.
589a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville     * @param s new PostDialState
590a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville     */
591a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    private void setPostDialState(PostDialState s) {
592a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        if (mPostDialState != PostDialState.STARTED
593a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                && s == PostDialState.STARTED) {
594a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            acquireWakeLock();
595a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            Message msg = mHandler.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT);
596a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            mHandler.sendMessageDelayed(msg, WAKE_LOCK_TIMEOUT_MILLIS);
597a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        } else if (mPostDialState == PostDialState.STARTED
598a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                && s != PostDialState.STARTED) {
599a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            mHandler.removeMessages(EVENT_WAKE_LOCK_TIMEOUT);
600a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            releaseWakeLock();
601a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        }
602a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mPostDialState = s;
603d9aa1a75304b1c04c352198b9269f40a2a059f74Andrew Lee        notifyPostDialListeners();
604a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
605a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
606a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    private void
607a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    createWakeLock(Context context) {
608a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
609a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
610a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
611a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
612a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    private void
613a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    acquireWakeLock() {
614a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        Rlog.d(LOG_TAG, "acquireWakeLock");
615a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mPartialWakeLock.acquire();
616a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
617a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
61897fba207643a87dd718395010a98ded3e809a3d7Libin.Tang@motorola.com    void
619a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    releaseWakeLock() {
62037800d3cdb300fca83462d95221a05bbef21a951Amit Mahajan        if (mPartialWakeLock != null) {
62137800d3cdb300fca83462d95221a05bbef21a951Amit Mahajan            synchronized (mPartialWakeLock) {
62237800d3cdb300fca83462d95221a05bbef21a951Amit Mahajan                if (mPartialWakeLock.isHeld()) {
62337800d3cdb300fca83462d95221a05bbef21a951Amit Mahajan                    Rlog.d(LOG_TAG, "releaseWakeLock");
62437800d3cdb300fca83462d95221a05bbef21a951Amit Mahajan                    mPartialWakeLock.release();
62537800d3cdb300fca83462d95221a05bbef21a951Amit Mahajan                }
626a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            }
627a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        }
628a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
629a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
630c2b66d57e16623dffc2fe6d02ded4f24c014b6c8Amit Mahajan    private void fetchDtmfToneDelay(Phone phone) {
6316bb557b243f3fc9984b82319026519608ada2c9cRoshan Pius        CarrierConfigManager configMgr = (CarrierConfigManager)
6326bb557b243f3fc9984b82319026519608ada2c9cRoshan Pius                phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
6336bb557b243f3fc9984b82319026519608ada2c9cRoshan Pius        PersistableBundle b = configMgr.getConfigForSubId(phone.getSubId());
6346bb557b243f3fc9984b82319026519608ada2c9cRoshan Pius        if (b != null) {
6356bb557b243f3fc9984b82319026519608ada2c9cRoshan Pius            mDtmfToneDelay = b.getInt(CarrierConfigManager.KEY_IMS_DTMF_TONE_DELAY_INT);
6366bb557b243f3fc9984b82319026519608ada2c9cRoshan Pius        }
6376bb557b243f3fc9984b82319026519608ada2c9cRoshan Pius    }
6386bb557b243f3fc9984b82319026519608ada2c9cRoshan Pius
639a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    @Override
640a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    public int getNumberPresentation() {
641a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        return mNumberPresentation;
642a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
643a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
644a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    @Override
645a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    public UUSInfo getUUSInfo() {
646a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        return mUusInfo;
647a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
648a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
649a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    @Override
650a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    public Connection getOrigConnection() {
651a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        return null;
652a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
653a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
654a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    @Override
65534310e958f52099ce3a2e74a8e3c4f631a241529Hall Liu    public synchronized boolean isMultiparty() {
656541accbd8e08fac77faa1c60d14533b1a5a36347Tyler Gunn        return mImsCall != null && mImsCall.isMultiparty();
657a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
658a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
659ce67e30bf7a91a3ff63f86020c2d91c248c3a71bTyler Gunn    /**
660ce67e30bf7a91a3ff63f86020c2d91c248c3a71bTyler Gunn     * Where {@link #isMultiparty()} is {@code true}, determines if this {@link ImsCall} is the
661ce67e30bf7a91a3ff63f86020c2d91c248c3a71bTyler Gunn     * origin of the conference call (i.e. {@code #isConferenceHost()} is {@code true}), or if this
662ce67e30bf7a91a3ff63f86020c2d91c248c3a71bTyler Gunn     * {@link ImsCall} is a member of a conference hosted on another device.
663ce67e30bf7a91a3ff63f86020c2d91c248c3a71bTyler Gunn     *
664ce67e30bf7a91a3ff63f86020c2d91c248c3a71bTyler Gunn     * @return {@code true} if this call is the origin of the conference call it is a member of,
665ce67e30bf7a91a3ff63f86020c2d91c248c3a71bTyler Gunn     *      {@code false} otherwise.
666ce67e30bf7a91a3ff63f86020c2d91c248c3a71bTyler Gunn     */
667c2b66d57e16623dffc2fe6d02ded4f24c014b6c8Amit Mahajan    @Override
66834310e958f52099ce3a2e74a8e3c4f631a241529Hall Liu    public synchronized boolean isConferenceHost() {
66934310e958f52099ce3a2e74a8e3c4f631a241529Hall Liu        return mImsCall != null && mImsCall.isConferenceHost();
670ce67e30bf7a91a3ff63f86020c2d91c248c3a71bTyler Gunn    }
671ce67e30bf7a91a3ff63f86020c2d91c248c3a71bTyler Gunn
672c2b66d57e16623dffc2fe6d02ded4f24c014b6c8Amit Mahajan    @Override
673c2b66d57e16623dffc2fe6d02ded4f24c014b6c8Amit Mahajan    public boolean isMemberOfPeerConference() {
674c2b66d57e16623dffc2fe6d02ded4f24c014b6c8Amit Mahajan        return !isConferenceHost();
675c2b66d57e16623dffc2fe6d02ded4f24c014b6c8Amit Mahajan    }
676c2b66d57e16623dffc2fe6d02ded4f24c014b6c8Amit Mahajan
67734310e958f52099ce3a2e74a8e3c4f631a241529Hall Liu    public synchronized ImsCall getImsCall() {
678a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        return mImsCall;
679a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
680a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
68134310e958f52099ce3a2e74a8e3c4f631a241529Hall Liu    public synchronized void setImsCall(ImsCall imsCall) {
682a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mImsCall = imsCall;
683a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
684a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
685f9b150b221db15e88b97a0b9c0971ca3c71c0313fionaxu    public void changeParent(ImsPhoneCall parent) {
686a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mParent = parent;
687a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
688a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
6896a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee    /**
6906a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee     * @return {@code true} if the {@link ImsPhoneConnection} or its media capabilities have been
6916a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee     *     changed, and {@code false} otherwise.
6926a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee     */
693f9b150b221db15e88b97a0b9c0971ca3c71c0313fionaxu    public boolean update(ImsCall imsCall, ImsPhoneCall.State state) {
694a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        if (state == ImsPhoneCall.State.ACTIVE) {
695707644d8250cc5c3c2287068a9df210d7eb3e863Tyler Gunn            // If the state of the call is active, but there is a pending request to the RIL to hold
696707644d8250cc5c3c2287068a9df210d7eb3e863Tyler Gunn            // the call, we will skip this update.  This is really a signalling delay or failure
697707644d8250cc5c3c2287068a9df210d7eb3e863Tyler Gunn            // from the RIL, but we will prevent it from going through as we will end up erroneously
698707644d8250cc5c3c2287068a9df210d7eb3e863Tyler Gunn            // making this call active when really it should be on hold.
699707644d8250cc5c3c2287068a9df210d7eb3e863Tyler Gunn            if (imsCall.isPendingHold()) {
700707644d8250cc5c3c2287068a9df210d7eb3e863Tyler Gunn                Rlog.w(LOG_TAG, "update : state is ACTIVE, but call is pending hold, skipping");
701707644d8250cc5c3c2287068a9df210d7eb3e863Tyler Gunn                return false;
702707644d8250cc5c3c2287068a9df210d7eb3e863Tyler Gunn            }
703707644d8250cc5c3c2287068a9df210d7eb3e863Tyler Gunn
7046a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee            if (mParent.getState().isRinging() || mParent.getState().isDialing()) {
705a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                onConnectedInOrOut();
706a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            }
707a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
7086a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee            if (mParent.getState().isRinging() || mParent == mOwner.mBackgroundCall) {
709a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                //mForegroundCall should be IDLE
710a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                //when accepting WAITING call
711a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                //before accept WAITING call,
712a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                //the ACTIVE call should be held ahead
713a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                mParent.detach(this);
714a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                mParent = mOwner.mForegroundCall;
715a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                mParent.attach(this);
716a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            }
717a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        } else if (state == ImsPhoneCall.State.HOLDING) {
718a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            onStartedHolding();
719a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        }
720a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
721549148fb25d8b38c2bd1207c1a21027c6621932bEtan Cohen        boolean updateParent = mParent.update(this, imsCall, state);
722eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganesh        boolean updateAddressDisplay = updateAddressDisplay(imsCall);
72321048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar        boolean updateMediaCapabilities = updateMediaCapabilities(imsCall);
72421048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar        boolean updateExtras = updateExtras(imsCall);
725ce67e30bf7a91a3ff63f86020c2d91c248c3a71bTyler Gunn
72675f96a40e73bbf262287b64f7ba79f058adac472Brad Ebinger        return updateParent || updateAddressDisplay || updateMediaCapabilities || updateExtras;
727a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
728a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
729a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    @Override
730a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    public int getPreciseDisconnectCause() {
731222bd9d64068a23f5470561655ca4dbd2359eeceJayachandran C        return mPreciseDisconnectCause;
732222bd9d64068a23f5470561655ca4dbd2359eeceJayachandran C    }
733222bd9d64068a23f5470561655ca4dbd2359eeceJayachandran C
734222bd9d64068a23f5470561655ca4dbd2359eeceJayachandran C    public void setPreciseDisconnectCause(int cause) {
735222bd9d64068a23f5470561655ca4dbd2359eeceJayachandran C        mPreciseDisconnectCause = cause;
736a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    }
737d7f4ed8ac190763dce7e57b737caa44654b4592aTyler Gunn
738d7f4ed8ac190763dce7e57b737caa44654b4592aTyler Gunn    /**
739d7f4ed8ac190763dce7e57b737caa44654b4592aTyler Gunn     * Notifies this Connection of a request to disconnect a participant of the conference managed
740d7f4ed8ac190763dce7e57b737caa44654b4592aTyler Gunn     * by the connection.
741d7f4ed8ac190763dce7e57b737caa44654b4592aTyler Gunn     *
742d7f4ed8ac190763dce7e57b737caa44654b4592aTyler Gunn     * @param endpoint the {@link android.net.Uri} of the participant to disconnect.
743d7f4ed8ac190763dce7e57b737caa44654b4592aTyler Gunn     */
744d7f4ed8ac190763dce7e57b737caa44654b4592aTyler Gunn    @Override
745d7f4ed8ac190763dce7e57b737caa44654b4592aTyler Gunn    public void onDisconnectConferenceParticipant(Uri endpoint) {
746d7f4ed8ac190763dce7e57b737caa44654b4592aTyler Gunn        ImsCall imsCall = getImsCall();
747d7f4ed8ac190763dce7e57b737caa44654b4592aTyler Gunn        if (imsCall == null) {
748d7f4ed8ac190763dce7e57b737caa44654b4592aTyler Gunn            return;
749d7f4ed8ac190763dce7e57b737caa44654b4592aTyler Gunn        }
750d7f4ed8ac190763dce7e57b737caa44654b4592aTyler Gunn        try {
751d7f4ed8ac190763dce7e57b737caa44654b4592aTyler Gunn            imsCall.removeParticipants(new String[]{endpoint.toString()});
752d7f4ed8ac190763dce7e57b737caa44654b4592aTyler Gunn        } catch (ImsException e) {
753d7f4ed8ac190763dce7e57b737caa44654b4592aTyler Gunn            // No session in place -- no change
754d7f4ed8ac190763dce7e57b737caa44654b4592aTyler Gunn            Rlog.e(LOG_TAG, "onDisconnectConferenceParticipant: no session in place. "+
755d7f4ed8ac190763dce7e57b737caa44654b4592aTyler Gunn                    "Failed to disconnect endpoint = " + endpoint);
756d7f4ed8ac190763dce7e57b737caa44654b4592aTyler Gunn        }
757d7f4ed8ac190763dce7e57b737caa44654b4592aTyler Gunn    }
758541accbd8e08fac77faa1c60d14533b1a5a36347Tyler Gunn
759541accbd8e08fac77faa1c60d14533b1a5a36347Tyler Gunn    /**
760fd2305639e3761c2bf3213463c4f17cc8310f4d0Tyler Gunn     * Sets the conference connect time.  Used when an {@code ImsConference} is created to out of
761fd2305639e3761c2bf3213463c4f17cc8310f4d0Tyler Gunn     * this phone connection.
762fd2305639e3761c2bf3213463c4f17cc8310f4d0Tyler Gunn     *
763fd2305639e3761c2bf3213463c4f17cc8310f4d0Tyler Gunn     * @param conferenceConnectTime The conference connect time.
764fd2305639e3761c2bf3213463c4f17cc8310f4d0Tyler Gunn     */
765fd2305639e3761c2bf3213463c4f17cc8310f4d0Tyler Gunn    public void setConferenceConnectTime(long conferenceConnectTime) {
766fd2305639e3761c2bf3213463c4f17cc8310f4d0Tyler Gunn        mConferenceConnectTime = conferenceConnectTime;
767fd2305639e3761c2bf3213463c4f17cc8310f4d0Tyler Gunn    }
768fd2305639e3761c2bf3213463c4f17cc8310f4d0Tyler Gunn
769fd2305639e3761c2bf3213463c4f17cc8310f4d0Tyler Gunn    /**
770fd2305639e3761c2bf3213463c4f17cc8310f4d0Tyler Gunn     * @return The conference connect time.
771fd2305639e3761c2bf3213463c4f17cc8310f4d0Tyler Gunn     */
772fd2305639e3761c2bf3213463c4f17cc8310f4d0Tyler Gunn    public long getConferenceConnectTime() {
773fd2305639e3761c2bf3213463c4f17cc8310f4d0Tyler Gunn        return mConferenceConnectTime;
774fd2305639e3761c2bf3213463c4f17cc8310f4d0Tyler Gunn    }
775fd2305639e3761c2bf3213463c4f17cc8310f4d0Tyler Gunn
776fd2305639e3761c2bf3213463c4f17cc8310f4d0Tyler Gunn    /**
777eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganesh     * Check for a change in the address display related fields for the {@link ImsCall}, and
778eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganesh     * update the {@link ImsPhoneConnection} with this information.
779eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganesh     *
780eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganesh     * @param imsCall The call to check for changes in address display fields.
781eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganesh     * @return Whether the address display fields have been changed.
782eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganesh     */
78327e0378194d55fdcb23f4a3a85dc620a234b5855Anju Mathapati    public boolean updateAddressDisplay(ImsCall imsCall) {
784eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganesh        if (imsCall == null) {
785eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganesh            return false;
786eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganesh        }
787eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganesh
788eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganesh        boolean changed = false;
789eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganesh        ImsCallProfile callProfile = imsCall.getCallProfile();
790eb7465409290618ca5d7b5f8ba2ffabba2d936d9Tyler Gunn        if (callProfile != null && isIncoming()) {
791eb7465409290618ca5d7b5f8ba2ffabba2d936d9Tyler Gunn            // Only look for changes to the address for incoming calls.  The originating identity
792eb7465409290618ca5d7b5f8ba2ffabba2d936d9Tyler Gunn            // can change for outgoing calls due to, for example, a call being forwarded to
793eb7465409290618ca5d7b5f8ba2ffabba2d936d9Tyler Gunn            // voicemail.  This address change does not need to be presented to the user.
794eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganesh            String address = callProfile.getCallExtra(ImsCallProfile.EXTRA_OI);
795eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganesh            String name = callProfile.getCallExtra(ImsCallProfile.EXTRA_CNA);
796eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganesh            int nump = ImsCallProfile.OIRToPresentation(
797eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganesh                    callProfile.getCallExtraInt(ImsCallProfile.EXTRA_OIR));
798eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganesh            int namep = ImsCallProfile.OIRToPresentation(
799eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganesh                    callProfile.getCallExtraInt(ImsCallProfile.EXTRA_CNAP));
800eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganesh            if (Phone.DEBUG_PHONE) {
801eb7465409290618ca5d7b5f8ba2ffabba2d936d9Tyler Gunn                Rlog.d(LOG_TAG, "updateAddressDisplay: callId = " + getTelecomCallId()
802c58281f18e760c39c2fedba61453eae7bfdda349Xinying.Deng                        + " address = " + Rlog.pii(LOG_TAG, address) + " name = "
803c58281f18e760c39c2fedba61453eae7bfdda349Xinying.Deng                        + Rlog.pii(LOG_TAG, name) + " nump = " + nump + " namep = " + namep);
804eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganesh            }
805286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn            if (!mIsMergeInProcess) {
806286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn                // Only process changes to the name and address when a merge is not in process.
807286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn                // When call A initiated a merge with call B to form a conference C, there is a
808286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn                // point in time when the ImsCall transfers the conference call session into A,
809286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn                // at which point the ImsConferenceController creates the conference in Telecom.
810286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn                // For some carriers C will have a unique conference URI address.  Swapping the
811286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn                // conference session into A, which is about to be disconnected, to be logged to
812286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn                // the call log using the conference address.  To prevent this we suppress updates
813286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn                // to the call address while a merge is in process.
814286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn                if (!equalsBaseDialString(mAddress, address)) {
815286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn                    mAddress = address;
816286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn                    changed = true;
817286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn                }
818286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn                if (TextUtils.isEmpty(name)) {
819286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn                    if (!TextUtils.isEmpty(mCnapName)) {
820286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn                        mCnapName = "";
821286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn                        changed = true;
822286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn                    }
823286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn                } else if (!name.equals(mCnapName)) {
824286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn                    mCnapName = name;
825286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn                    changed = true;
826286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn                }
827286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn                if (mNumberPresentation != nump) {
828286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn                    mNumberPresentation = nump;
829286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn                    changed = true;
830286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn                }
831286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn                if (mCnapNamePresentation != namep) {
832286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn                    mCnapNamePresentation = namep;
833eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganesh                    changed = true;
834eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganesh                }
835eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganesh            }
836eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganesh        }
837eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganesh        return changed;
838eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganesh    }
839eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganesh
840eeeb1cb65e41778fa78abcaba93a9e4ebe351ec6Shriram Ganesh    /**
8416a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee     * Check for a change in the video capabilities and audio quality for the {@link ImsCall}, and
8426a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee     * update the {@link ImsPhoneConnection} with this information.
8436a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee     *
8446a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee     * @param imsCall The call to check for changes in media capabilities.
8456a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee     * @return Whether the media capabilities have been changed.
8466a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee     */
847d9c3a08d5958e5ced0c0c9f7aabf376a5fe59bb2Anju Mathapati    public boolean updateMediaCapabilities(ImsCall imsCall) {
8486a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee        if (imsCall == null) {
8496a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee            return false;
8506a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee        }
8516a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee
8526a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee        boolean changed = false;
8536a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee
8546a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee        try {
855e70972cf6a3b48df1f7d036877eb28529e606ca9Tyler Gunn            // The actual call profile (negotiated between local and peer).
856e70972cf6a3b48df1f7d036877eb28529e606ca9Tyler Gunn            ImsCallProfile negotiatedCallProfile = imsCall.getCallProfile();
8576a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee
858e70972cf6a3b48df1f7d036877eb28529e606ca9Tyler Gunn            if (negotiatedCallProfile != null) {
859288268d5528e0df03f348e303954813cb188c55bRekha Kumar                int oldVideoState = getVideoState();
860288268d5528e0df03f348e303954813cb188c55bRekha Kumar                int newVideoState = ImsCallProfile
861288268d5528e0df03f348e303954813cb188c55bRekha Kumar                        .getVideoStateFromImsCallProfile(negotiatedCallProfile);
862e70972cf6a3b48df1f7d036877eb28529e606ca9Tyler Gunn
863288268d5528e0df03f348e303954813cb188c55bRekha Kumar                if (oldVideoState != newVideoState) {
864db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                    // The video state has changed.  See also code in onReceiveSessionModifyResponse
865db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                    // below.  When the video enters a paused state, subsequent changes to the video
866db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                    // state will not be reported by the modem.  In onReceiveSessionModifyResponse
867db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                    // we will be updating the current video state while paused to include any
868db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                    // changes the modem reports via the video provider.  When the video enters an
869db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                    // unpaused state, we will resume passing the video states from the modem as is.
870db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                    if (VideoProfile.isPaused(oldVideoState) &&
871db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                            !VideoProfile.isPaused(newVideoState)) {
872db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                        // Video entered un-paused state; recognize updates from now on; we want to
873db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                        // ensure that the new un-paused state is propagated to Telecom, so change
874db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                        // this now.
875db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                        mShouldIgnoreVideoStateChanges = false;
876db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                    }
877db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn
878db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                    if (!mShouldIgnoreVideoStateChanges) {
87944b58c8044c5fc0b271432429fc97707c4c773e1Tyler Gunn                        updateVideoState(newVideoState);
880db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                        changed = true;
881db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                    } else {
882db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                        Rlog.d(LOG_TAG, "updateMediaCapabilities - ignoring video state change " +
883db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                                "due to paused state.");
884db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                    }
885db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn
886db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                    if (!VideoProfile.isPaused(oldVideoState) &&
887db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                            VideoProfile.isPaused(newVideoState)) {
888db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                        // Video entered pause state; ignore updates until un-paused.  We do this
889db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                        // after setVideoState is called above to ensure Telecom is notified that
890db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                        // the device has entered paused state.
891db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                        mShouldIgnoreVideoStateChanges = true;
892db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                    }
893e70972cf6a3b48df1f7d036877eb28529e606ca9Tyler Gunn                }
894e70972cf6a3b48df1f7d036877eb28529e606ca9Tyler Gunn            }
895e70972cf6a3b48df1f7d036877eb28529e606ca9Tyler Gunn
896f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn            // Check for a change in the capabilities for the call and update
897f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn            // {@link ImsPhoneConnection} with this information.
898f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn            int capabilities = getConnectionCapabilities();
8992e7e09e79c04f34034bebea8f5d86606bb584577Tyler Gunn
9002e7e09e79c04f34034bebea8f5d86606bb584577Tyler Gunn            // Use carrier config to determine if downgrading directly to audio-only is supported.
9012e7e09e79c04f34034bebea8f5d86606bb584577Tyler Gunn            if (mOwner.isCarrierDowngradeOfVtCallSupported()) {
9022e7e09e79c04f34034bebea8f5d86606bb584577Tyler Gunn                capabilities = addCapability(capabilities,
9032e7e09e79c04f34034bebea8f5d86606bb584577Tyler Gunn                        Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE |
9042e7e09e79c04f34034bebea8f5d86606bb584577Tyler Gunn                                Capability.SUPPORTS_DOWNGRADE_TO_VOICE_LOCAL);
9052e7e09e79c04f34034bebea8f5d86606bb584577Tyler Gunn            } else {
9062e7e09e79c04f34034bebea8f5d86606bb584577Tyler Gunn                capabilities = removeCapability(capabilities,
9072e7e09e79c04f34034bebea8f5d86606bb584577Tyler Gunn                        Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE |
9082e7e09e79c04f34034bebea8f5d86606bb584577Tyler Gunn                                Capability.SUPPORTS_DOWNGRADE_TO_VOICE_LOCAL);
9092e7e09e79c04f34034bebea8f5d86606bb584577Tyler Gunn            }
9102e7e09e79c04f34034bebea8f5d86606bb584577Tyler Gunn
911f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn            // Get the current local call capabilities which might be voice or video or both.
912f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn            ImsCallProfile localCallProfile = imsCall.getLocalCallProfile();
913f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn            Rlog.v(LOG_TAG, "update localCallProfile=" + localCallProfile);
9146a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee            if (localCallProfile != null) {
915f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn                capabilities = applyLocalCallCapabilities(localCallProfile, capabilities);
9166a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee            }
9176a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee
918f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn            // Get the current remote call capabilities which might be voice or video or both.
919f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn            ImsCallProfile remoteCallProfile = imsCall.getRemoteCallProfile();
920f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn            Rlog.v(LOG_TAG, "update remoteCallProfile=" + remoteCallProfile);
921288268d5528e0df03f348e303954813cb188c55bRekha Kumar            if (remoteCallProfile != null) {
922f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn                capabilities = applyRemoteCallCapabilities(remoteCallProfile, capabilities);
923f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn            }
924f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn            if (getConnectionCapabilities() != capabilities) {
925f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn                setConnectionCapabilities(capabilities);
926f83df11772fcf7f2aae0d9179b4d36eba92ad74eTyler Gunn                changed = true;
927288268d5528e0df03f348e303954813cb188c55bRekha Kumar            }
928288268d5528e0df03f348e303954813cb188c55bRekha Kumar
9296a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee            int newAudioQuality =
9306a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee                    getAudioQualityFromCallProfile(localCallProfile, remoteCallProfile);
9316a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee            if (getAudioQuality() != newAudioQuality) {
9326a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee                setAudioQuality(newAudioQuality);
9336a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee                changed = true;
9346a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee            }
9356a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee        } catch (ImsException e) {
9366a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee            // No session in place -- no change
9376a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee        }
9386a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee
9396a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee        return changed;
9406a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee    }
9416a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee
94244b58c8044c5fc0b271432429fc97707c4c773e1Tyler Gunn    private void updateVideoState(int newVideoState) {
94344b58c8044c5fc0b271432429fc97707c4c773e1Tyler Gunn        if (mImsVideoCallProviderWrapper != null) {
94444b58c8044c5fc0b271432429fc97707c4c773e1Tyler Gunn            mImsVideoCallProviderWrapper.onVideoStateChanged(newVideoState);
94544b58c8044c5fc0b271432429fc97707c4c773e1Tyler Gunn        }
94644b58c8044c5fc0b271432429fc97707c4c773e1Tyler Gunn        setVideoState(newVideoState);
94744b58c8044c5fc0b271432429fc97707c4c773e1Tyler Gunn    }
94844b58c8044c5fc0b271432429fc97707c4c773e1Tyler Gunn
949fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu    public void sendRttModifyRequest(android.telecom.Connection.RttTextStream textStream) {
950fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu        getImsCall().sendRttModifyRequest();
951fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu        setCurrentRttTextStream(textStream);
952fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu    }
953fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu
954fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu    /**
955fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu     * Sends the user's response to a remotely-issued RTT upgrade request
956fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu     *
957fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu     * @param textStream A valid {@link android.telecom.Connection.RttTextStream} if the user
958fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu     *                   accepts, {@code null} if not.
959fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu     */
960fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu    public void sendRttModifyResponse(android.telecom.Connection.RttTextStream textStream) {
961fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu        boolean accept = textStream != null;
962fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu        ImsCall imsCall = getImsCall();
963fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu
964fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu        imsCall.sendRttModifyResponse(accept);
965fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu        if (accept) {
966fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu            setCurrentRttTextStream(textStream);
967fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu            startRttTextProcessing();
968fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu        } else {
969fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu            Rlog.e(LOG_TAG, "sendRttModifyResponse: foreground call has no connections");
970fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu        }
971fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu    }
972fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu
973fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu    public void onRttMessageReceived(String message) {
974fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu        getOrCreateRttTextHandler().sendToInCall(message);
975fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu    }
976fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu
977fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu    public void setCurrentRttTextStream(android.telecom.Connection.RttTextStream rttTextStream) {
978fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu        mRttTextStream = rttTextStream;
979fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu    }
980fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu
981fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu    public void startRttTextProcessing() {
982fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu        getOrCreateRttTextHandler().initialize(mRttTextStream);
983fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu    }
984fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu
985fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu    private ImsRttTextHandler getOrCreateRttTextHandler() {
986fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu        if (mRttTextHandler != null) {
987fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu            return mRttTextHandler;
988fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu        }
989fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu        mRttTextHandler = new ImsRttTextHandler(Looper.getMainLooper(),
990fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu                (message) -> getImsCall().sendRttMessage(message));
991fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu        return mRttTextHandler;
992fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu    }
993fa88fc1de20ddb819975d78c98e4c0d6b27e225aHall Liu
9946a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee    /**
9952e66c6325d321e41b8851da9a4596f7a745c7b1eTyler Gunn     * Updates the wifi state based on the {@link ImsCallProfile#EXTRA_CALL_RAT_TYPE}.
9962e66c6325d321e41b8851da9a4596f7a745c7b1eTyler Gunn     * The call is considered to be a WIFI call if the extra value is
9972e66c6325d321e41b8851da9a4596f7a745c7b1eTyler Gunn     * {@link ServiceState#RIL_RADIO_TECHNOLOGY_IWLAN}.
9982e66c6325d321e41b8851da9a4596f7a745c7b1eTyler Gunn     *
9992e66c6325d321e41b8851da9a4596f7a745c7b1eTyler Gunn     * @param extras The ImsCallProfile extras.
10002e66c6325d321e41b8851da9a4596f7a745c7b1eTyler Gunn     */
10012e66c6325d321e41b8851da9a4596f7a745c7b1eTyler Gunn    private void updateWifiStateFromExtras(Bundle extras) {
1002d0a85e9d7e52ecd5de9f7063361d9b01d2493627Brad Ebinger        if (extras.containsKey(ImsCallProfile.EXTRA_CALL_RAT_TYPE) ||
1003d0a85e9d7e52ecd5de9f7063361d9b01d2493627Brad Ebinger                extras.containsKey(ImsCallProfile.EXTRA_CALL_RAT_TYPE_ALT)) {
10042e66c6325d321e41b8851da9a4596f7a745c7b1eTyler Gunn
1005d0a85e9d7e52ecd5de9f7063361d9b01d2493627Brad Ebinger            ImsCall call = getImsCall();
1006d0a85e9d7e52ecd5de9f7063361d9b01d2493627Brad Ebinger            boolean isWifi = false;
1007d0a85e9d7e52ecd5de9f7063361d9b01d2493627Brad Ebinger            if (call != null) {
1008d0a85e9d7e52ecd5de9f7063361d9b01d2493627Brad Ebinger                isWifi = call.isWifiCall();
1009d0a85e9d7e52ecd5de9f7063361d9b01d2493627Brad Ebinger            }
10102e66c6325d321e41b8851da9a4596f7a745c7b1eTyler Gunn
10112e66c6325d321e41b8851da9a4596f7a745c7b1eTyler Gunn            // Report any changes
10122e66c6325d321e41b8851da9a4596f7a745c7b1eTyler Gunn            if (isWifi() != isWifi) {
10132e66c6325d321e41b8851da9a4596f7a745c7b1eTyler Gunn                setWifi(isWifi);
10142e66c6325d321e41b8851da9a4596f7a745c7b1eTyler Gunn            }
10152e66c6325d321e41b8851da9a4596f7a745c7b1eTyler Gunn        }
10162e66c6325d321e41b8851da9a4596f7a745c7b1eTyler Gunn    }
10172e66c6325d321e41b8851da9a4596f7a745c7b1eTyler Gunn
10182e66c6325d321e41b8851da9a4596f7a745c7b1eTyler Gunn    /**
101921048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar     * Check for a change in call extras of {@link ImsCall}, and
102021048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar     * update the {@link ImsPhoneConnection} accordingly.
102121048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar     *
102221048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar     * @param imsCall The call to check for changes in extras.
102321048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar     * @return Whether the extras fields have been changed.
102421048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar     */
102521048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar     boolean updateExtras(ImsCall imsCall) {
102621048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar        if (imsCall == null) {
102721048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar            return false;
102821048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar        }
102921048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar
103021048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar        final ImsCallProfile callProfile = imsCall.getCallProfile();
103121048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar        final Bundle extras = callProfile != null ? callProfile.mCallExtras : null;
103221048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar        if (extras == null && DBG) {
103321048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar            Rlog.d(LOG_TAG, "Call profile extras are null.");
103421048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar        }
103521048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar
103621048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar        final boolean changed = !areBundlesEqual(extras, mExtras);
103721048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar        if (changed) {
10382e66c6325d321e41b8851da9a4596f7a745c7b1eTyler Gunn            updateWifiStateFromExtras(extras);
10392e66c6325d321e41b8851da9a4596f7a745c7b1eTyler Gunn
104021048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar            mExtras.clear();
104121048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar            mExtras.putAll(extras);
104221048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar            setConnectionExtras(mExtras);
104321048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar        }
104421048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar        return changed;
104521048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar    }
104621048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar
104721048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar    private static boolean areBundlesEqual(Bundle extras, Bundle newExtras) {
104821048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar        if (extras == null || newExtras == null) {
104921048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar            return extras == newExtras;
105021048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar        }
105121048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar
105221048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar        if (extras.size() != newExtras.size()) {
105321048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar            return false;
105421048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar        }
105521048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar
105621048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar        for(String key : extras.keySet()) {
105721048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar            if (key != null) {
105821048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar                final Object value = extras.get(key);
105921048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar                final Object newValue = newExtras.get(key);
106021048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar                if (!Objects.equals(value, newValue)) {
106121048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar                    return false;
106221048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar                }
106321048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar            }
106421048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar        }
106521048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar        return true;
106621048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar    }
106721048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar
106821048a2bc97d932a3ddecdfd79003a03f34263ecNivedita Sarkar    /**
10696a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee     * Determines the {@link ImsPhoneConnection} audio quality based on the local and remote
1070363911ade99b8c798fd1c6ef545d253b8c35a001Dheeraj Shetty     * {@link ImsCallProfile}. Indicate a HD audio call if the local stream profile
1071363911ade99b8c798fd1c6ef545d253b8c35a001Dheeraj Shetty     * is AMR_WB, EVRC_WB, EVS_WB, EVS_SWB, EVS_FB and
1072363911ade99b8c798fd1c6ef545d253b8c35a001Dheeraj Shetty     * there is no remote restrict cause.
10736a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee     *
10746a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee     * @param localCallProfile The local call profile.
10756a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee     * @param remoteCallProfile The remote call profile.
10766a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee     * @return The audio quality.
10776a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee     */
10786a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee    private int getAudioQualityFromCallProfile(
10796a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee            ImsCallProfile localCallProfile, ImsCallProfile remoteCallProfile) {
10806a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee        if (localCallProfile == null || remoteCallProfile == null
10816a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee                || localCallProfile.mMediaProfile == null) {
10826a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee            return AUDIO_QUALITY_STANDARD;
10836a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee        }
10846a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee
1085363911ade99b8c798fd1c6ef545d253b8c35a001Dheeraj Shetty        final boolean isEvsCodecHighDef = (localCallProfile.mMediaProfile.mAudioQuality
1086363911ade99b8c798fd1c6ef545d253b8c35a001Dheeraj Shetty                        == ImsStreamMediaProfile.AUDIO_QUALITY_EVS_WB
1087363911ade99b8c798fd1c6ef545d253b8c35a001Dheeraj Shetty                || localCallProfile.mMediaProfile.mAudioQuality
1088363911ade99b8c798fd1c6ef545d253b8c35a001Dheeraj Shetty                        == ImsStreamMediaProfile.AUDIO_QUALITY_EVS_SWB
1089363911ade99b8c798fd1c6ef545d253b8c35a001Dheeraj Shetty                || localCallProfile.mMediaProfile.mAudioQuality
1090363911ade99b8c798fd1c6ef545d253b8c35a001Dheeraj Shetty                        == ImsStreamMediaProfile.AUDIO_QUALITY_EVS_FB);
1091363911ade99b8c798fd1c6ef545d253b8c35a001Dheeraj Shetty
1092363911ade99b8c798fd1c6ef545d253b8c35a001Dheeraj Shetty        final boolean isHighDef = (localCallProfile.mMediaProfile.mAudioQuality
10936a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee                        == ImsStreamMediaProfile.AUDIO_QUALITY_AMR_WB
10946a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee                || localCallProfile.mMediaProfile.mAudioQuality
1095363911ade99b8c798fd1c6ef545d253b8c35a001Dheeraj Shetty                        == ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_WB
1096363911ade99b8c798fd1c6ef545d253b8c35a001Dheeraj Shetty                || isEvsCodecHighDef)
10976a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee                && remoteCallProfile.mRestrictCause == ImsCallProfile.CALL_RESTRICT_CAUSE_NONE;
10986a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee        return isHighDef ? AUDIO_QUALITY_HIGH_DEFINITION : AUDIO_QUALITY_STANDARD;
10996a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee    }
11006a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee
11016a0fa4105fa8104a09b33a3403df5ae2711e0083Andrew Lee    /**
1102541accbd8e08fac77faa1c60d14533b1a5a36347Tyler Gunn     * Provides a string representation of the {@link ImsPhoneConnection}.  Primarily intended for
1103541accbd8e08fac77faa1c60d14533b1a5a36347Tyler Gunn     * use in log statements.
1104541accbd8e08fac77faa1c60d14533b1a5a36347Tyler Gunn     *
1105541accbd8e08fac77faa1c60d14533b1a5a36347Tyler Gunn     * @return String representation of call.
1106541accbd8e08fac77faa1c60d14533b1a5a36347Tyler Gunn     */
1107541accbd8e08fac77faa1c60d14533b1a5a36347Tyler Gunn    @Override
1108541accbd8e08fac77faa1c60d14533b1a5a36347Tyler Gunn    public String toString() {
1109541accbd8e08fac77faa1c60d14533b1a5a36347Tyler Gunn        StringBuilder sb = new StringBuilder();
1110541accbd8e08fac77faa1c60d14533b1a5a36347Tyler Gunn        sb.append("[ImsPhoneConnection objId: ");
1111541accbd8e08fac77faa1c60d14533b1a5a36347Tyler Gunn        sb.append(System.identityHashCode(this));
11121a87ab3d7170d618f048c4f5af8c7504a587aaa5Jack Yu        sb.append(" telecomCallID: ");
11131a87ab3d7170d618f048c4f5af8c7504a587aaa5Jack Yu        sb.append(getTelecomCallId());
11141a87ab3d7170d618f048c4f5af8c7504a587aaa5Jack Yu        sb.append(" address: ");
111577a1f167b962ceaf7972d246f4c23e17772d1c69fionaxu        sb.append(Rlog.pii(LOG_TAG, getAddress()));
11161a87ab3d7170d618f048c4f5af8c7504a587aaa5Jack Yu        sb.append(" ImsCall: ");
111734310e958f52099ce3a2e74a8e3c4f631a241529Hall Liu        synchronized (this) {
111834310e958f52099ce3a2e74a8e3c4f631a241529Hall Liu            if (mImsCall == null) {
111934310e958f52099ce3a2e74a8e3c4f631a241529Hall Liu                sb.append("null");
112034310e958f52099ce3a2e74a8e3c4f631a241529Hall Liu            } else {
112134310e958f52099ce3a2e74a8e3c4f631a241529Hall Liu                sb.append(mImsCall);
112234310e958f52099ce3a2e74a8e3c4f631a241529Hall Liu            }
1123541accbd8e08fac77faa1c60d14533b1a5a36347Tyler Gunn        }
1124541accbd8e08fac77faa1c60d14533b1a5a36347Tyler Gunn        sb.append("]");
1125541accbd8e08fac77faa1c60d14533b1a5a36347Tyler Gunn        return sb.toString();
1126541accbd8e08fac77faa1c60d14533b1a5a36347Tyler Gunn    }
11278bfa1b42f0ffebf33239546ceaf5d6f5ca813191Amit Mahajan
1128359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn    @Override
1129359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn    public void setVideoProvider(android.telecom.Connection.VideoProvider videoProvider) {
1130359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn        super.setVideoProvider(videoProvider);
1131359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn
1132359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn        if (videoProvider instanceof ImsVideoCallProviderWrapper) {
1133359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn            mImsVideoCallProviderWrapper = (ImsVideoCallProviderWrapper) videoProvider;
1134359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn        }
1135359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn    }
1136359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn
11378bfa1b42f0ffebf33239546ceaf5d6f5ca813191Amit Mahajan    /**
11388bfa1b42f0ffebf33239546ceaf5d6f5ca813191Amit Mahajan     * Indicates whether current phone connection is emergency or not
11398bfa1b42f0ffebf33239546ceaf5d6f5ca813191Amit Mahajan     * @return boolean: true if emergency, false otherwise
11408bfa1b42f0ffebf33239546ceaf5d6f5ca813191Amit Mahajan     */
11418bfa1b42f0ffebf33239546ceaf5d6f5ca813191Amit Mahajan    protected boolean isEmergency() {
11428bfa1b42f0ffebf33239546ceaf5d6f5ca813191Amit Mahajan        return mIsEmergency;
11438bfa1b42f0ffebf33239546ceaf5d6f5ca813191Amit Mahajan    }
1144db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn
1145db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn    /**
1146db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn     * Handles notifications from the {@link ImsVideoCallProviderWrapper} of session modification
1147db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn     * responses received.
1148db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn     *
1149db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn     * @param status The status of the original request.
1150db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn     * @param requestProfile The requested video profile.
1151db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn     * @param responseProfile The response upon video profile.
1152db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn     */
1153db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn    @Override
1154db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn    public void onReceiveSessionModifyResponse(int status, VideoProfile requestProfile,
1155db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn            VideoProfile responseProfile) {
1156db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn        if (status == android.telecom.Connection.VideoProvider.SESSION_MODIFY_REQUEST_SUCCESS &&
1157db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                mShouldIgnoreVideoStateChanges) {
1158db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn            int currentVideoState = getVideoState();
1159db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn            int newVideoState = responseProfile.getVideoState();
1160db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn
1161db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn            // If the current video state is paused, the modem will not send us any changes to
1162db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn            // the TX and RX bits of the video state.  Until the video is un-paused we will
1163db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn            // "fake out" the video state by applying the changes that the modem reports via a
1164db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn            // response.
1165db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn
1166db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn            // First, find out whether there was a change to the TX or RX bits:
1167db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn            int changedBits = currentVideoState ^ newVideoState;
1168db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn            changedBits &= VideoProfile.STATE_BIDIRECTIONAL;
1169db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn            if (changedBits == 0) {
1170db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                // No applicable change, bail out.
1171db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                return;
1172db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn            }
1173db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn
1174db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn            // Turn off any existing bits that changed.
1175db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn            currentVideoState &= ~(changedBits & currentVideoState);
1176db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn            // Turn on any new bits that turned on.
1177db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn            currentVideoState |= changedBits & newVideoState;
1178db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn
1179db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn            Rlog.d(LOG_TAG, "onReceiveSessionModifyResponse : received " +
1180db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                    VideoProfile.videoStateToString(requestProfile.getVideoState()) +
1181db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                    " / " +
1182db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                    VideoProfile.videoStateToString(responseProfile.getVideoState()) +
1183db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                    " while paused ; sending new videoState = " +
1184db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn                    VideoProfile.videoStateToString(currentVideoState));
1185db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn            setVideoState(currentVideoState);
1186db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn        }
1187db5b6a50186bc3fc8d42c50f4d23c91b05412ec6Tyler Gunn    }
1188359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn
1189359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn    /**
1190359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn     * Issues a request to pause the video using {@link VideoProfile#STATE_PAUSED} from a source
1191359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn     * other than the InCall UI.
1192359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn     *
1193359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn     * @param source The source of the pause request.
1194359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn     */
1195359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn    public void pauseVideo(int source) {
1196359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn        if (mImsVideoCallProviderWrapper == null) {
1197359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn            return;
1198359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn        }
1199359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn
1200359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn        mImsVideoCallProviderWrapper.pauseVideo(getVideoState(), source);
1201359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn    }
1202359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn
1203359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn    /**
1204359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn     * Issues a request to resume the video using {@link VideoProfile#STATE_PAUSED} from a source
1205359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn     * other than the InCall UI.
1206359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn     *
1207359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn     * @param source The source of the resume request.
1208359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn     */
1209359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn    public void resumeVideo(int source) {
1210359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn        if (mImsVideoCallProviderWrapper == null) {
1211359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn            return;
1212359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn        }
1213359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn
1214359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn        mImsVideoCallProviderWrapper.resumeVideo(getVideoState(), source);
1215359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn    }
1216359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn
1217359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn    /**
1218359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn     * Determines if a specified source has issued a pause request.
1219359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn     *
1220359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn     * @param source The source.
1221359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn     * @return {@code true} if the source issued a pause request, {@code false} otherwise.
1222359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn     */
1223359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn    public boolean wasVideoPausedFromSource(int source) {
1224359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn        if (mImsVideoCallProviderWrapper == null) {
1225359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn            return false;
1226359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn        }
1227359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn
1228359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn        return mImsVideoCallProviderWrapper.wasVideoPausedFromSource(source);
1229359c182111879b821c0b7eba6d5fa52194293212Tyler Gunn    }
1230286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn
1231286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn    /**
1232286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn     * Mark the call as in the process of being merged and inform the UI of the merge start.
1233286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn     */
1234286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn    public void handleMergeStart() {
1235286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn        mIsMergeInProcess = true;
1236286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn        onConnectionEvent(android.telecom.Connection.EVENT_MERGE_START, null);
1237286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn    }
1238286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn
1239286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn    /**
1240286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn     * Mark the call as done merging and inform the UI of the merge start.
1241286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn     */
1242286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn    public void handleMergeComplete() {
1243286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn        mIsMergeInProcess = false;
1244286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn        onConnectionEvent(android.telecom.Connection.EVENT_MERGE_COMPLETE, null);
1245286e95aa9cd740c6d8e9fd32aeefeceaf0c81695Tyler Gunn    }
124644b58c8044c5fc0b271432429fc97707c4c773e1Tyler Gunn
124744b58c8044c5fc0b271432429fc97707c4c773e1Tyler Gunn    public void changeToPausedState() {
124844b58c8044c5fc0b271432429fc97707c4c773e1Tyler Gunn        int newVideoState = getVideoState() | VideoProfile.STATE_PAUSED;
124944b58c8044c5fc0b271432429fc97707c4c773e1Tyler Gunn        Rlog.i(LOG_TAG, "ImsPhoneConnection: changeToPausedState - setting paused bit; "
125044b58c8044c5fc0b271432429fc97707c4c773e1Tyler Gunn                + "newVideoState=" + VideoProfile.videoStateToString(newVideoState));
125144b58c8044c5fc0b271432429fc97707c4c773e1Tyler Gunn        updateVideoState(newVideoState);
12526dee0e40c77ac373820e7e266ff372a481e30befTyler Gunn        mShouldIgnoreVideoStateChanges = true;
125344b58c8044c5fc0b271432429fc97707c4c773e1Tyler Gunn    }
125444b58c8044c5fc0b271432429fc97707c4c773e1Tyler Gunn
125544b58c8044c5fc0b271432429fc97707c4c773e1Tyler Gunn    public void changeToUnPausedState() {
125644b58c8044c5fc0b271432429fc97707c4c773e1Tyler Gunn        int newVideoState = getVideoState() & ~VideoProfile.STATE_PAUSED;
125744b58c8044c5fc0b271432429fc97707c4c773e1Tyler Gunn        Rlog.i(LOG_TAG, "ImsPhoneConnection: changeToUnPausedState - unsetting paused bit; "
125844b58c8044c5fc0b271432429fc97707c4c773e1Tyler Gunn                + "newVideoState=" + VideoProfile.videoStateToString(newVideoState));
125944b58c8044c5fc0b271432429fc97707c4c773e1Tyler Gunn        updateVideoState(newVideoState);
12606dee0e40c77ac373820e7e266ff372a481e30befTyler Gunn        mShouldIgnoreVideoStateChanges = false;
126144b58c8044c5fc0b271432429fc97707c4c773e1Tyler Gunn    }
12627a411d4797fb60914f5f6230bde24fc25b12f920Tyler Gunn
12637a411d4797fb60914f5f6230bde24fc25b12f920Tyler Gunn    public void handleDataEnabledChange(boolean isDataEnabled) {
12647a411d4797fb60914f5f6230bde24fc25b12f920Tyler Gunn        mIsVideoEnabled = isDataEnabled;
12657a411d4797fb60914f5f6230bde24fc25b12f920Tyler Gunn        Rlog.i(LOG_TAG, "handleDataEnabledChange: isDataEnabled=" + isDataEnabled
12667a411d4797fb60914f5f6230bde24fc25b12f920Tyler Gunn                + "; updating local video availability.");
12677a411d4797fb60914f5f6230bde24fc25b12f920Tyler Gunn        updateMediaCapabilities(getImsCall());
12687a411d4797fb60914f5f6230bde24fc25b12f920Tyler Gunn        if (mImsVideoCallProviderWrapper != null) {
12697a411d4797fb60914f5f6230bde24fc25b12f920Tyler Gunn            mImsVideoCallProviderWrapper.setIsVideoEnabled(
12707a411d4797fb60914f5f6230bde24fc25b12f920Tyler Gunn                    hasCapabilities(Connection.Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL));
12717a411d4797fb60914f5f6230bde24fc25b12f920Tyler Gunn        }
127265cd15597c5e611184a3f4c8ead5f42644fc00d6Tyler Gunn    }
1273a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville}
1274