IMS-VT: Update phone account's capabilities.
Updates phone account's video capabilities based on
the event received from Phone object.

IMS: Add support for video quality
- Add UI for adding IMS settings
- Add video call quality preference
  Enable user to control video call quality from UI

Change-Id: I308e6d106d853d8242b2e324467edc7ce739850f

IMS-VT: Do not allow changing the TTY mode during a Video call
When the device is already in a video call, the user SHOULD NOT
be allowed to change the TTY mode. If the user tries to change,
then an UI alert message will be displayed explaining it cannot
be changed during video calls.

Propagate the call substate changed message to the UI

Enhance log message in handleVideoCapabilitesChanged
- Add the video capability value to the log message

IMS-VT: Call Modify capability
-Add modify call capability only if call is in active or holding
 state

Change-Id: I8d75f65ce4ddb4b684478260995ec6cad74d46c0

IMS-VT: Enable Video conferencing.
Enable Video conferencing.

Ims: Update conferenceHostConnection state for a conference call
TelephonyConnection that backed the original connection is
cleaned up after making a clone this TelephonyConnection that
is eventually treated as conferenceHostConnection. Update
conferenceHostConnection state too

Change-Id: I962d11068d136a39527dd0720200c42fbd49681b
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index dcdae0c..a0802fd 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -505,6 +505,12 @@
                 android:uiOptions="splitActionBarWhenNarrow">
         </activity>
 
+        <activity android:name="ImsEditor"
+                android:theme="@style/DialerSettingsLight"
+                android:configChanges="orientation|screenSize|keyboardHidden"
+                android:uiOptions="splitActionBarWhenNarrow">
+        </activity>
+
         <!-- End SIP -->
 
         <activity android:name="ErrorDialogActivity"
diff --git a/res/values/array.xml b/res/values/array.xml
new file mode 100644
index 0000000..79b6034
--- /dev/null
+++ b/res/values/array.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Array resources for the Phone app. -->
+<resources>
+    <!-- Array resource for IMS VT call quality entries-->
+    <string-array translatable="true" name="ims_vt_call_quality_entries">
+        <item>@string/ims_vt_call_quality_low</item>
+        <item>@string/ims_vt_call_quality_high</item>
+    </string-array>
+
+    <!-- Array resource for IMS VT call quality values-->
+    <string-array translatable="false" name="ims_vt_call_quality_values">
+        <item>0</item>
+        <item>1</item>
+    </string-array>
+</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index abeeee0..4118e0a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -753,7 +753,8 @@
     <string name="tty_mode_option_summary">Set TTY mode</string>
     <string name="auto_retry_mode_title">Auto-retry</string>
     <string name="auto_retry_mode_summary">Enable Auto-retry mode</string>
-
+    <!-- TTY Mode change is NOT allowed during a video call -->
+    <string name="tty_mode_not_allowed_video_call">TTY Mode change is not allowed during a video call</string>
     <!-- FDN list screen: menu item label -->
     <string name="menu_add">Add contact</string>
     <!-- FDN list screen: menu item label -->
@@ -1298,4 +1299,17 @@
     <!-- DO NOT TRANSLATE. Internal key for a voicemail notification preference. -->
     <string name="wifi_calling_settings_key">button_wifi_calling_settings_key</string>
 
+    <!-- IMS settings related strings -->
+    <!-- Title of the ims editor screen. [CHAR LIMIT=NONE] -->
+    <string name="ims_edit_title">IMS account details</string>
+    <!-- Title displayed IMS account settings in the sip settings category. [CHAR LIMIT=NONE] -->
+    <string name="ims_accounts_title">IMS Account</string>
+
+    <!-- VT call quality settings. -->
+    <string translatable="false" name="ims_vt_call_quality">VtCallQuality</string>
+    <string name="ims_vt_call_quality_title">VT Call Quality</string>
+    <string name="ims_vt_call_quality_low">Low</string>
+    <string name="ims_vt_call_quality_high">High</string>
+    <string name="ims_vt_call_quality_unknown">Unknown</string>
+    <string name="ims_vt_call_quality_set_failed">Failed to set video quality</string>
 </resources>
diff --git a/res/xml/call_feature_setting.xml b/res/xml/call_feature_setting.xml
index 543fae5..4af9736 100644
--- a/res/xml/call_feature_setting.xml
+++ b/res/xml/call_feature_setting.xml
@@ -149,4 +149,14 @@
 
     </PreferenceScreen>
 
+    <PreferenceScreen
+        android:key="ims_account_settings_key"
+        android:title="@string/ims_accounts_title"
+        android:persistent="false">
+
+        <intent android:action="android.intent.action.MAIN"
+            android:targetPackage="com.android.phone"
+            android:targetClass="com.android.phone.ImsEditor" />
+    </PreferenceScreen>
+
 </PreferenceScreen>
diff --git a/res/xml/ims_edit.xml b/res/xml/ims_edit.xml
new file mode 100644
index 0000000..f16e2f5
--- /dev/null
+++ b/res/xml/ims_edit.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+        android:title="@string/ims_edit_title"
+        android:persistent="false">
+
+    <ListPreference
+        android:key="@string/ims_vt_call_quality"
+        android:title="@string/ims_vt_call_quality_title"
+        android:entries="@array/ims_vt_call_quality_entries"
+        android:entryValues="@array/ims_vt_call_quality_values"
+        android:summary="@string/ims_vt_call_quality_unknown"
+        android:persistent="false"
+        android:enabled="true"
+        android:dialogTitle="@string/ims_vt_call_quality_title"/>
+
+</PreferenceScreen>
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index c211323..4fa28ab 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -336,6 +336,10 @@
         } else if (preference == mButtonDTMF) {
             return true;
         } else if (preference == mButtonTTY) {
+            if(mPhone.isVideoCallPresent()) {
+                // TTY Mode change is not allowed during a VT call
+                showDialogIfForeground(VoicemailDialogUtil.TTY_SET_RESPONSE_ERROR);
+            }
             return true;
         } else if (preference == mButtonAutoRetry) {
             android.provider.Settings.Global.putInt(mPhone.getContext().getContentResolver(),
diff --git a/src/com/android/phone/ims/ImsEditor.java b/src/com/android/phone/ims/ImsEditor.java
new file mode 100644
index 0000000..bbd4526
--- /dev/null
+++ b/src/com/android/phone/ims/ImsEditor.java
@@ -0,0 +1,170 @@
+/* Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone;
+
+import android.os.Bundle;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceScreen;
+import android.telephony.SubscriptionManager;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.android.ims.ImsConfig;
+import com.android.ims.ImsConfigListener;
+import com.android.ims.ImsException;
+import com.android.ims.ImsManager;
+import com.android.phone.R;
+
+/**
+ * The activity class for editing a new or existing IMS profile.
+ */
+public class ImsEditor extends PreferenceActivity
+        implements Preference.OnPreferenceChangeListener {
+
+    private static final String TAG = ImsEditor.class.getSimpleName();
+
+    private ListPreference mVideoCallQuality;
+    private ImsConfig mImsConfig;
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        Log.d(TAG, "onResume");
+        getVideoQuality();
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        Log.v(TAG, "start profile editor");
+        super.onCreate(savedInstanceState);
+
+        addPreferencesFromResource(R.xml.ims_edit);
+        PreferenceScreen screen = getPreferenceScreen();
+
+        mVideoCallQuality = (ListPreference) screen
+                .findPreference(getString(R.string.ims_vt_call_quality));
+        mVideoCallQuality.setOnPreferenceChangeListener(this);
+
+        try {
+            ImsManager imsManager = ImsManager.getInstance(getBaseContext(),
+                    SubscriptionManager.getDefaultVoiceSubId());
+            mImsConfig = imsManager.getConfigInterface();
+        } catch (ImsException e) {
+            mImsConfig = null;
+            Log.e(TAG, "ImsService is not running");
+        }
+    }
+
+    private ImsConfigListener imsConfigListener = new ImsConfigListener.Stub() {
+        public void onGetVideoQuality(int status, int quality) {
+            if (hasRequestFailed(status)) {
+                quality = ImsConfig.OperationValuesConstants.VIDEO_QUALITY_UNKNOWN;
+                Log.e(TAG, "onGetVideoQuality: failed. errorCode = " + status);
+            }
+            Log.d(TAG, "onGetVideoQuality: value = " + quality);
+            loadVideoCallQualityPrefs(quality);
+        }
+
+        public void onSetVideoQuality(int status) {
+            if (hasRequestFailed(status)) {
+                Log.e(TAG, "onSetVideoQuality: set failed. errorCode = " + status);
+                Toast.makeText(getApplicationContext(), R.string.ims_vt_call_quality_set_failed,
+                        Toast.LENGTH_SHORT).show();
+                getVideoQuality(); // Set request failed, get current value.
+            } else {
+                Log.d(TAG, "onSetVideoQuality: set succeeded.");
+            }
+        }
+
+        public void onGetFeatureResponse(int feature, int network, int value, int status) {
+            //TODO not required as of now
+        }
+
+        public void onSetFeatureResponse(int feature, int network, int value, int status) {
+            //TODO not required as of now
+        }
+    };
+
+    @Override
+    public boolean onPreferenceChange(Preference pref, Object newValue) {
+        if (pref.equals(mVideoCallQuality)) {
+            if (newValue == null) {
+                Log.e(TAG, "onPreferenceChange invalid value received");
+            } else {
+                final int quality = Integer.parseInt(newValue.toString());
+                boolean result = setVideoQuality(quality);
+                if (result) {
+                    loadVideoCallQualityPrefs(quality);
+                }
+                return result;
+            }
+        }
+        return true;
+    }
+
+    private void loadVideoCallQualityPrefs(int vqValue) {
+        Log.d(TAG, "loadVideoCallQualityPrefs, vqValue = " + vqValue);
+        final String videoQuality = videoQualityToString(vqValue);
+        mVideoCallQuality.setValue(String.valueOf(vqValue));
+        mVideoCallQuality.setSummary(videoQuality);
+    }
+
+    private void getVideoQuality() {
+        try {
+            if (mImsConfig != null) {
+                mImsConfig.getVideoQuality(imsConfigListener);
+            } else {
+                loadVideoCallQualityPrefs(ImsConfig.OperationValuesConstants.VIDEO_QUALITY_UNKNOWN);
+                Log.e(TAG, "getVideoQuality failed. mImsConfig is null");
+            }
+        } catch (ImsException e) {
+            Log.e(TAG, "getVideoQuality failed. Exception = " + e);
+        }
+    }
+
+    private boolean setVideoQuality(int quality) {
+        try {
+            if (mImsConfig != null) {
+                mImsConfig.setVideoQuality(quality, imsConfigListener);
+            } else {
+                Log.e(TAG, "setVideoQuality failed. mImsConfig is null");
+                return false;
+            }
+        } catch (ImsException e) {
+            Log.e(TAG, "setVideoQuality failed. Exception = " + e);
+            return false;
+        }
+        return true;
+    }
+
+    private boolean hasRequestFailed(int result) {
+        return (result != ImsConfig.OperationStatusConstants.SUCCESS);
+    }
+
+    private String videoQualityToString(int quality) {
+        switch (quality) {
+            case ImsConfig.OperationValuesConstants.VIDEO_QUALITY_HIGH:
+                return getString(R.string.ims_vt_call_quality_high);
+            case ImsConfig.OperationValuesConstants.VIDEO_QUALITY_LOW:
+                return getString(R.string.ims_vt_call_quality_low);
+            case ImsConfig.OperationValuesConstants.VIDEO_QUALITY_UNKNOWN:
+            default:
+                return getString(R.string.ims_vt_call_quality_unknown);
+        }
+    }
+}
diff --git a/src/com/android/phone/settings/VoicemailDialogUtil.java b/src/com/android/phone/settings/VoicemailDialogUtil.java
index 0a9e4bc..f1e7fa7 100644
--- a/src/com/android/phone/settings/VoicemailDialogUtil.java
+++ b/src/com/android/phone/settings/VoicemailDialogUtil.java
@@ -35,11 +35,12 @@
     public static final int VM_FWD_SAVING_DIALOG = 601;
     public static final int VM_FWD_READING_DIALOG = 602;
     public static final int VM_REVERTING_DIALOG = 603;
+    public static final int TTY_SET_RESPONSE_ERROR = 800;
 
     public static Dialog getDialog(CallFeaturesSetting parent, int id) {
         if ((id == VM_RESPONSE_ERROR_DIALOG) || (id == VM_NOCHANGE_ERROR_DIALOG) ||
             (id == FWD_SET_RESPONSE_ERROR_DIALOG) || (id == FWD_GET_RESPONSE_ERROR_DIALOG) ||
-                (id == VM_CONFIRM_DIALOG)) {
+                (id == VM_CONFIRM_DIALOG) || (id == TTY_SET_RESPONSE_ERROR)) {
 
             AlertDialog.Builder b = new AlertDialog.Builder(parent);
 
@@ -75,6 +76,12 @@
                     b.setPositiveButton(R.string.alert_dialog_yes, parent);
                     b.setNegativeButton(R.string.alert_dialog_no, parent);
                     break;
+                case TTY_SET_RESPONSE_ERROR:
+                    titleId = R.string.tty_mode_option_title;
+                    msgId = R.string.tty_mode_not_allowed_video_call;
+                    b.setIconAttribute(android.R.attr.alertDialogIcon);
+                    b.setPositiveButton(R.string.ok, parent);
+                    break;
                 default:
                     msgId = R.string.exception_error;
                     // Set Button 3, tells the activity that the error is
diff --git a/src/com/android/services/telephony/CdmaConnection.java b/src/com/android/services/telephony/CdmaConnection.java
index 0baa3cc..3016039 100644
--- a/src/com/android/services/telephony/CdmaConnection.java
+++ b/src/com/android/services/telephony/CdmaConnection.java
@@ -155,13 +155,16 @@
         mIsCallWaiting = originalConnection != null &&
                 originalConnection.getState() == Call.State.WAITING;
 
-        if (state == android.telecom.Connection.STATE_DIALING) {
-            if (isEmergency()) {
-                mEmergencyTonePlayer.start();
+        if (mEmergencyTonePlayer != null) {
+            if (state == android.telecom.Connection.STATE_DIALING) {
+                if (isEmergency()) {
+                    mEmergencyTonePlayer.start();
+                }
+            } else {
+                // No need to check if it is an emergency call, since it is a no-op if it
+                // isn't started.
+                mEmergencyTonePlayer.stop();
             }
-        } else {
-            // No need to check if it is an emergency call, since it is a no-op if it isn't started.
-            mEmergencyTonePlayer.stop();
         }
 
         super.onStateChanged(state);
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index e692a30..9afb4ad 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -21,7 +21,11 @@
 import android.telecom.ConferenceParticipant;
 import android.telecom.Connection;
 import android.telecom.DisconnectCause;
+import android.telecom.Log;
 import android.telecom.PhoneAccountHandle;
+import android.telecom.VideoProfile;
+import android.telecom.Conference.Listener;
+import android.telecom.Connection.VideoProvider;
 
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.CallStateException;
@@ -145,6 +149,28 @@
             TelephonyConnection telephonyConnection = (TelephonyConnection) c;
             handleConferenceParticipantsUpdate(telephonyConnection, participants);
         }
+
+        @Override
+        public void onVideoStateChanged(android.telecom.Connection c, int videoState) {
+            Log.d(this, "onVideoStateChanged video state %d", videoState);
+            setVideoState(c, videoState);
+        }
+
+        @Override
+        public void onVideoProviderChanged(android.telecom.Connection c,
+                Connection.VideoProvider videoProvider) {
+            Log.d(this, "onVideoProviderChanged: Connection: %s, VideoProvider: %s", c,
+                    videoProvider);
+            setVideoProvider(c, videoProvider);
+        }
+
+        @Override
+        public void onConnectionCapabilitiesChanged(Connection c, int connectionCapabilities) {
+            Log.d(this, "onCallCapabilitiesChanged: Connection: %s, callCapabilities: %s", c,
+                    connectionCapabilities);
+            int capabilites = ImsConference.this.getCapabilities();
+            setCapabilities(applyVideoCapabilities(capabilites, connectionCapabilities));
+        }
     };
 
     /**
@@ -191,11 +217,39 @@
             Log.v(this, "set phacc to " + mPhoneAccount);
         }
 
-        setConnectionCapabilities(
-                Connection.CAPABILITY_SUPPORT_HOLD |
-                Connection.CAPABILITY_HOLD |
-                Connection.CAPABILITY_MUTE
-        );
+        int capabilities = Connection.CAPABILITY_SUPPORT_HOLD | Connection.CAPABILITY_HOLD |
+                Connection.CAPABILITY_MUTE;
+
+        capabilities = applyVideoCapabilities(capabilities, mConferenceHost.getCallCapabilities());
+        setConnectionCapabilities(capabilities);
+
+    }
+
+    private int applyVideoCapabilities(int conferenceCapabilities, int capabilities) {
+        if (can(capabilities, Connection.CAPABILITY_SUPPORTS_VT_LOCAL)) {
+            conferenceCapabilities = applyCapability(conferenceCapabilities,
+                    Connection.CAPABILITY_SUPPORTS_VT_LOCAL);
+        } else {
+            conferenceCapabilities = removeCapability(conferenceCapabilities,
+                    Connection.CAPABILITY_SUPPORTS_VT_LOCAL);
+        }
+
+        if (can(capabilities, Connection.CAPABILITY_SUPPORTS_VT_REMOTE)) {
+            conferenceCapabilities = applyCapability(conferenceCapabilities,
+                    Connection.CAPABILITY_SUPPORTS_VT_REMOTE);
+        } else {
+            conferenceCapabilities = removeCapability(conferenceCapabilities,
+                    Connection.CAPABILITY_SUPPORTS_VT_REMOTE);
+        }
+
+        if (can(capabilities, Connection.CAPABILITY_CAN_UPGRADE_TO_VIDEO)) {
+            conferenceCapabilities = applyCapability(conferenceCapabilities,
+                    Connection.CAPABILITY_CAN_UPGRADE_TO_VIDEO);
+        } else {
+            conferenceCapabilities = removeCapability(conferenceCapabilities,
+                    Connection.CAPABILITY_CAN_UPGRADE_TO_VIDEO);
+        }
+        return conferenceCapabilities;
     }
 
     /**
@@ -209,6 +263,32 @@
     }
 
     /**
+     * Returns VideoProvider of the conference. This can be null.
+     *
+     * @hide
+     */
+    @Override
+    public VideoProvider getVideoProvider() {
+        if (mConferenceHost != null) {
+            return mConferenceHost.getVideoProvider();
+        }
+        return null;
+    }
+
+    /**
+     * Returns video state of conference
+     *
+     * @hide
+     */
+    @Override
+    public int getVideoState() {
+        if (mConferenceHost != null) {
+            return mConferenceHost.getVideoState();
+        }
+        return VideoProfile.VideoState.AUDIO_ONLY;
+    }
+
+    /**
      * Invoked when the Conference and all its {@link Connection}s should be disconnected.
      * <p>
      * Hangs up the call via the conference host connection.  When the host connection has been
@@ -321,6 +401,16 @@
         // No-op
     }
 
+    private int applyCapability(int capabilities, int capability) {
+        int newCapabilities = capabilities | capability;
+        return newCapabilities;
+    }
+
+    private int removeCapability(int capabilities, int capability) {
+        int newCapabilities = capabilities & ~capability;
+        return newCapabilities;
+    }
+
     /**
      * Updates the manage conference capability of the conference.  Where there are one or more
      * conference event package participants, the conference management is permitted.  Where there
@@ -356,6 +446,7 @@
         mConferenceHost = conferenceHost;
         mConferenceHost.addConnectionListener(mConferenceHostListener);
         mConferenceHost.addTelephonyConnectionListener(mTelephonyConnectionListener);
+        setState(mConferenceHost.getState());
     }
 
     /**
diff --git a/src/com/android/services/telephony/PstnPhoneCapabilitiesNotifier.java b/src/com/android/services/telephony/PstnPhoneCapabilitiesNotifier.java
new file mode 100644
index 0000000..48568d3
--- /dev/null
+++ b/src/com/android/services/telephony/PstnPhoneCapabilitiesNotifier.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.services.telephony;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.PhoneProxy;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.util.Preconditions;
+import com.android.phone.PhoneUtils;
+
+/**
+ * Listens to phone's capabilities changed event and notifies Telecomm. One instance of these exists
+ * for each of the telephony-based call services.
+ */
+final class PstnPhoneCapabilitiesNotifier {
+    private static final int EVENT_VIDEO_CAPABILITIES_CHANGED = 1;
+
+    private final PhoneProxy mPhoneProxy;
+    private Phone mPhoneBase;
+
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case EVENT_VIDEO_CAPABILITIES_CHANGED:
+                    handleVideoCapabilitesChanged((AsyncResult) msg.obj);
+                    break;
+                default:
+                    break;
+            }
+        }
+    };
+
+    private final BroadcastReceiver mRatReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED.equals(action)) {
+                String newPhone = intent.getStringExtra(PhoneConstants.PHONE_NAME_KEY);
+                Log.d(this, "Radio technology switched. Now %s is active.", newPhone);
+
+                registerForNotifications();
+            }
+        }
+    };
+
+    /*package*/
+    PstnPhoneCapabilitiesNotifier(PhoneProxy phoneProxy) {
+        Preconditions.checkNotNull(phoneProxy);
+
+        mPhoneProxy = phoneProxy;
+
+        registerForNotifications();
+
+        IntentFilter intentFilter =
+                new IntentFilter(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED);
+        mPhoneProxy.getContext().registerReceiver(mRatReceiver, intentFilter);
+    }
+
+    /*package*/
+    void teardown() {
+        unregisterForNotifications();
+        mPhoneProxy.getContext().unregisterReceiver(mRatReceiver);
+    }
+
+    private void registerForNotifications() {
+        Phone newPhone = mPhoneProxy.getActivePhone();
+        if (newPhone != mPhoneBase) {
+            unregisterForNotifications();
+
+            if (newPhone != null) {
+                Log.d(this, "Registering: " + newPhone);
+                mPhoneBase = newPhone;
+                mPhoneBase.registerForVideoCapabilityChanged(
+                        mHandler, EVENT_VIDEO_CAPABILITIES_CHANGED, null);
+            }
+        }
+    }
+
+    private void unregisterForNotifications() {
+        if (mPhoneBase != null) {
+            Log.d(this, "Unregistering: " + mPhoneBase);
+            mPhoneBase.unregisterForVideoCapabilityChanged(mHandler);
+        }
+    }
+
+    private void handleVideoCapabilitesChanged(AsyncResult ar) {
+        try {
+            boolean isVideoCapable = (Boolean) ar.result;
+            Log.d(this, "handleVideoCapabilitesChanged. Video capability - " + isVideoCapable);
+            PhoneAccountHandle accountHandle =
+                    PhoneUtils.makePstnPhoneAccountHandle(mPhoneProxy);
+            TelecomManager telecomMgr = TelecomManager.from(mPhoneProxy.getContext());
+            PhoneAccount oldPhoneAccount = telecomMgr.getPhoneAccount(accountHandle);
+            PhoneAccount.Builder builder = new PhoneAccount.Builder(oldPhoneAccount);
+
+            int capabilites = newCapabilities(oldPhoneAccount.getCapabilities(),
+                    PhoneAccount.CAPABILITY_VIDEO_CALLING, isVideoCapable);
+            builder.setCapabilities(capabilites);
+            telecomMgr.registerPhoneAccount(builder.build());
+        } catch (Exception e) {
+            Log.d(this, "handleVideoCapabilitesChanged. Exception=" + e);
+        }
+    }
+
+    private int newCapabilities(int capabilities, int capability, boolean set) {
+        if (set) {
+            capabilities |= capability;
+        } else {
+            capabilities &= ~capability;
+        }
+        return capabilities;
+    }
+}
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 799d844..866118c 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -57,6 +57,7 @@
         private final Phone mPhone;
         private final PhoneAccount mAccount;
         private final PstnIncomingCallNotifier mIncomingCallNotifier;
+        private final PstnPhoneCapabilitiesNotifier mPhoneCapabilitiesNotifier;
 
         AccountEntry(Phone phone, boolean isEmergency, boolean isDummy) {
             mPhone = phone;
@@ -64,10 +65,12 @@
             Log.d(this, "Registered phoneAccount: %s with handle: %s",
                     mAccount, mAccount.getAccountHandle());
             mIncomingCallNotifier = new PstnIncomingCallNotifier((PhoneProxy) mPhone);
+            mPhoneCapabilitiesNotifier = new PstnPhoneCapabilitiesNotifier((PhoneProxy) mPhone);
         }
 
         void teardown() {
             mIncomingCallNotifier.teardown();
+            mPhoneCapabilitiesNotifier.teardown();
         }
 
         /**
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 71bd810..6786d12 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -192,6 +192,17 @@
         public void onConferenceParticipantsChanged(List<ConferenceParticipant> participants) {
             updateConferenceParticipants(participants);
         }
+
+        /**
+         * Used by the {@link com.android.internal.telephony.Connection} to report a change in the
+         * substate of the current call
+         *
+         * @param callSubstate The call substate.
+         */
+        @Override
+        public void onCallSubstateChanged(int callSubstate) {
+            setCallSubstate(callSubstate);
+        }
     };
 
     private com.android.internal.telephony.Connection mOriginalConnection;
@@ -519,11 +530,13 @@
 
         // Set video state and capabilities
         setVideoState(mOriginalConnection.getVideoState());
+        updateState();
         setLocalVideoCapable(mOriginalConnection.isLocalVideoCapable());
         setRemoteVideoCapable(mOriginalConnection.isRemoteVideoCapable());
         setWifi(mOriginalConnection.isWifi());
         setVideoProvider(mOriginalConnection.getVideoProvider());
         setAudioQuality(mOriginalConnection.getAudioQuality());
+        setCallSubstate(mOriginalConnection.getCallSubstate());
 
         if (isImsConnection()) {
             mWasImsConnection = true;