Merge "Add support for downgrade to audio when data limit is reached." into nyc-mr1-dev
diff --git a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
index 2cc4336..993ac6d 100755
--- a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
@@ -298,9 +298,10 @@
             //we should have failed in !canDial() above before we get here
             throw new CallStateException("cannot dial in current state");
         }
-
+        boolean isEmergencyCall = PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(),
+                dialString);
         mPendingMO = new GsmCdmaConnection(mPhone, checkForTestEmergencyNumber(dialString),
-                this, mForegroundCall);
+                this, mForegroundCall, isEmergencyCall);
         mHangupPendingMO = false;
 
         if ( mPendingMO.getAddress() == null || mPendingMO.getAddress().length() == 0
@@ -411,7 +412,7 @@
         }
 
         mPendingMO = new GsmCdmaConnection(mPhone, checkForTestEmergencyNumber(dialString),
-                this, mForegroundCall);
+                this, mForegroundCall, isEmergencyCall);
         mHangupPendingMO = false;
 
         if ( mPendingMO.getAddress() == null || mPendingMO.getAddress().length() == 0
@@ -454,12 +455,13 @@
     //CDMA
     private Connection dialThreeWay(String dialString) {
         if (!mForegroundCall.isIdle()) {
-            // Check data call
+            // Check data call and possibly set mIsInEmergencyCall
             disableDataCallInEmergencyCall(dialString);
 
             // Attach the new connection to foregroundCall
             mPendingMO = new GsmCdmaConnection(mPhone,
-                    checkForTestEmergencyNumber(dialString), this, mForegroundCall);
+                    checkForTestEmergencyNumber(dialString), this, mForegroundCall,
+                    mIsInEmergencyCall);
             // Some network need a empty flash before sending the normal one
             m3WayCallFlashDelay = mPhone.getContext().getResources()
                     .getInteger(com.android.internal.R.integer.config_cdma_3waycall_flash_delay);
diff --git a/src/java/com/android/internal/telephony/GsmCdmaConnection.java b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
index a195bd1..1530ada 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaConnection.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
@@ -71,6 +71,8 @@
 
     private PowerManager.WakeLock mPartialWakeLock;
 
+    private boolean mIsEmergencyCall = false;
+
     // The cached delay to be used between DTMF tones fetched from carrier config.
     private int mDtmfToneDelay = 0;
 
@@ -116,7 +118,7 @@
 
     //***** Constructors
 
-    /** This is probably an MT call that we first saw in a CLCC response */
+    /** This is probably an MT call that we first saw in a CLCC response or a hand over. */
     public GsmCdmaConnection (GsmCdmaPhone phone, DriverCall dc, GsmCdmaCallTracker ct, int index) {
         super(phone.getPhoneType());
         createWakeLock(phone.getContext());
@@ -126,7 +128,7 @@
         mHandler = new MyHandler(mOwner.getLooper());
 
         mAddress = dc.number;
-
+        mIsEmergencyCall = PhoneNumberUtils.isLocalEmergencyNumber(phone.getContext(), mAddress);
         mIsIncoming = dc.isMT;
         mCreateTime = System.currentTimeMillis();
         mCnapName = dc.name;
@@ -144,7 +146,7 @@
 
     /** This is an MO call, created when dialing */
     public GsmCdmaConnection (GsmCdmaPhone phone, String dialString, GsmCdmaCallTracker ct,
-                              GsmCdmaCall parent) {
+                              GsmCdmaCall parent, boolean isEmergencyCall) {
         super(phone.getPhoneType());
         createWakeLock(phone.getContext());
         acquireWakeLock();
@@ -155,13 +157,16 @@
         if (isPhoneTypeGsm()) {
             mDialString = dialString;
         } else {
-            Rlog.d(LOG_TAG, "[GsmCdmaConn] GsmCdmaConnection: dialString=" + maskDialString(dialString));
+            Rlog.d(LOG_TAG, "[GsmCdmaConn] GsmCdmaConnection: dialString=" +
+                    maskDialString(dialString));
             dialString = formatDialString(dialString);
             Rlog.d(LOG_TAG,
-                    "[GsmCdmaConn] GsmCdmaConnection:formated dialString=" + maskDialString(dialString));
+                    "[GsmCdmaConn] GsmCdmaConnection:formated dialString=" +
+                            maskDialString(dialString));
         }
 
         mAddress = PhoneNumberUtils.extractNetworkPortionAlt(dialString);
+        mIsEmergencyCall = isEmergencyCall;
         mPostDialString = PhoneNumberUtils.extractPostDialPortion(dialString);
 
         mIndex = -1;
@@ -491,45 +496,49 @@
                 int serviceState = phone.getServiceState().getState();
                 UiccCardApplication cardApp = phone.getUiccCardApplication();
                 AppState uiccAppState = (cardApp != null) ? cardApp.getState() :
-                                                            AppState.APPSTATE_UNKNOWN;
+                        AppState.APPSTATE_UNKNOWN;
                 if (serviceState == ServiceState.STATE_POWER_OFF) {
                     return DisconnectCause.POWER_OFF;
-                } else if (serviceState == ServiceState.STATE_OUT_OF_SERVICE
-                        || serviceState == ServiceState.STATE_EMERGENCY_ONLY ) {
-                    return DisconnectCause.OUT_OF_SERVICE;
-                } else {
-                    if (isPhoneTypeGsm()) {
-                        if (uiccAppState != AppState.APPSTATE_READY) {
+                }
+                if (!mIsEmergencyCall) {
+                    // Only send OUT_OF_SERVICE if it is not an emergency call. We can still
+                    // technically be in STATE_OUT_OF_SERVICE or STATE_EMERGENCY_ONLY during
+                    // an emergency call and when it ends, we do not want to mistakenly generate
+                    // an OUT_OF_SERVICE disconnect cause during normal call ending.
+                    if ((serviceState == ServiceState.STATE_OUT_OF_SERVICE
+                            || serviceState == ServiceState.STATE_EMERGENCY_ONLY)) {
+                        return DisconnectCause.OUT_OF_SERVICE;
+                    }
+                    // If we are placing an emergency call and the SIM is currently PIN/PUK
+                    // locked the AppState will always not be equal to APPSTATE_READY.
+                    if (uiccAppState != AppState.APPSTATE_READY) {
+                        if (isPhoneTypeGsm()) {
                             return DisconnectCause.ICC_ERROR;
-                        } else if (causeCode == CallFailCause.ERROR_UNSPECIFIED) {
-                            if (phone.mSST.mRestrictedState.isCsRestricted()) {
-                                return DisconnectCause.CS_RESTRICTED;
-                            } else if (phone.mSST.mRestrictedState.isCsEmergencyRestricted()) {
-                                return DisconnectCause.CS_RESTRICTED_EMERGENCY;
-                            } else if (phone.mSST.mRestrictedState.isCsNormalRestricted()) {
-                                return DisconnectCause.CS_RESTRICTED_NORMAL;
-                            } else {
-                                return DisconnectCause.ERROR_UNSPECIFIED;
+                        } else { // CDMA
+                            if (phone.mCdmaSubscriptionSource ==
+                                    CdmaSubscriptionSourceManager.SUBSCRIPTION_FROM_RUIM) {
+                                return DisconnectCause.ICC_ERROR;
                             }
-                        } else if (causeCode == CallFailCause.NORMAL_CLEARING) {
-                            return DisconnectCause.NORMAL;
-                        } else {
-                            // If nothing else matches, report unknown call drop reason
-                            // to app, not NORMAL call end.
-                            return DisconnectCause.ERROR_UNSPECIFIED;
-                        }
-                    } else {
-                        if (phone.mCdmaSubscriptionSource ==
-                                CdmaSubscriptionSourceManager.SUBSCRIPTION_FROM_RUIM
-                                && uiccAppState != AppState.APPSTATE_READY) {
-                            return DisconnectCause.ICC_ERROR;
-                        } else if (causeCode==CallFailCause.NORMAL_CLEARING) {
-                            return DisconnectCause.NORMAL;
-                        } else {
-                            return DisconnectCause.ERROR_UNSPECIFIED;
                         }
                     }
                 }
+                if (isPhoneTypeGsm()) {
+                    if (causeCode == CallFailCause.ERROR_UNSPECIFIED) {
+                        if (phone.mSST.mRestrictedState.isCsRestricted()) {
+                            return DisconnectCause.CS_RESTRICTED;
+                        } else if (phone.mSST.mRestrictedState.isCsEmergencyRestricted()) {
+                            return DisconnectCause.CS_RESTRICTED_EMERGENCY;
+                        } else if (phone.mSST.mRestrictedState.isCsNormalRestricted()) {
+                            return DisconnectCause.CS_RESTRICTED_NORMAL;
+                        }
+                    }
+                }
+                if (causeCode == CallFailCause.NORMAL_CLEARING) {
+                    return DisconnectCause.NORMAL;
+                }
+                // If nothing else matches, report unknown call drop reason
+                // to app, not NORMAL call end.
+                return DisconnectCause.ERROR_UNSPECIFIED;
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
index 0919af1..834548b 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
@@ -409,6 +409,9 @@
                 networkType, NETWORK_TYPE, TelephonyManager.getNetworkTypeName(networkType));
         mNetworkInfo.setRoaming(ss.getDataRoaming());
         mNetworkInfo.setIsAvailable(true);
+        // The network should be by default metered until we find it has NET_CAPABILITY_NOT_METERED
+        // capability.
+        mNetworkInfo.setMetered(true);
 
         addState(mDefaultState);
             addState(mInactiveState, mDefaultState);
@@ -947,6 +950,10 @@
             if (!mApnSetting.isMetered(mPhone.getContext(), mPhone.getSubId(),
                     mPhone.getServiceState().getDataRoaming())) {
                 result.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+                mNetworkInfo.setMetered(false);
+            } else {
+                result.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+                mNetworkInfo.setMetered(true);
             }
 
             result.maybeMarkCapabilitiesRestricted();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaConnectionTest.java
index 90d2687..df9a7ab 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaConnectionTest.java
@@ -57,7 +57,8 @@
     @Test @SmallTest
     public void testFormatDialString(){
         connection = new GsmCdmaConnection(mPhone, String.format(
-                "+1 (700).555-41NN%c1234", PhoneNumberUtils.PAUSE), mCT, null);
+                "+1 (700).555-41NN%c1234", PhoneNumberUtils.PAUSE), mCT, null,
+                false /*isEmergencyCall*/);
        /* case 1: If PAUSE/WAIT sequence at the end, ignore them */
         String formattedDialStr = connection.formatDialString(
                 String.format("+1 (700).555-41NN1234%c", PhoneNumberUtils.PAUSE));
@@ -72,7 +73,8 @@
     @Test @SmallTest
     public void testSanityGSM() {
         connection = new GsmCdmaConnection(mPhone, String.format(
-                "+1 (700).555-41NN%c1234", PhoneNumberUtils.PAUSE), mCT, null);
+                "+1 (700).555-41NN%c1234", PhoneNumberUtils.PAUSE), mCT, null,
+                false /*isEmergencyCall*/);
         logd("Testing initial state of GsmCdmaConnection");
         assertEquals(GsmCdmaCall.State.IDLE, connection.getState());
         assertEquals(Connection.PostDialState.NOT_STARTED, connection.getPostDialState());
@@ -89,7 +91,8 @@
     public void testSanityCDMA() {
         doReturn(PhoneConstants.PHONE_TYPE_CDMA).when(mPhone).getPhoneType();
         connection = new GsmCdmaConnection(mPhone, String.format(
-                "+1 (700).555-41NN%c1234", PhoneNumberUtils.PAUSE), mCT, null);
+                "+1 (700).555-41NN%c1234", PhoneNumberUtils.PAUSE), mCT, null,
+                false /*isEmergencyCall*/);
         logd("Testing initial state of GsmCdmaConnection");
         assertEquals(GsmCdmaCall.State.IDLE, connection.getState());
         assertEquals(Connection.PostDialState.NOT_STARTED, connection.getPostDialState());
@@ -106,7 +109,8 @@
     @Test @SmallTest
     public void testConnectionStateUpdate() {
         connection = new GsmCdmaConnection(mPhone, String.format(
-                "+1 (700).555-41NN%c1234", PhoneNumberUtils.PAUSE), mCT, null);
+                "+1 (700).555-41NN%c1234", PhoneNumberUtils.PAUSE), mCT, null,
+                false /*isEmergencyCall*/);
         logd("Update the connection state from idle to active");
         mDC.state = DriverCall.State.ACTIVE;
         connection.update(mDC);
@@ -123,7 +127,8 @@
     public void testCDMAPostDialPause() {
         doReturn(PhoneConstants.PHONE_TYPE_CDMA).when(mPhone).getPhoneType();
         connection = new GsmCdmaConnection(mPhone, String.format(
-                "+1 (700).555-41NN%c1234", PhoneNumberUtils.PAUSE), mCT, null);
+                "+1 (700).555-41NN%c1234", PhoneNumberUtils.PAUSE), mCT, null,
+                false /*isEmergencyCall*/);
         logd("Mock connection state from alerting to active ");
         mDC.state = DriverCall.State.ALERTING;
         connection.update(mDC);
@@ -139,7 +144,8 @@
     @Test @MediumTest
     public void testGSMPostDialPause() {
         connection = new GsmCdmaConnection(mPhone, String.format(
-                "+1 (700).555-41NN%c1234", PhoneNumberUtils.PAUSE), mCT, null);
+                "+1 (700).555-41NN%c1234", PhoneNumberUtils.PAUSE), mCT, null,
+                false /*isEmergencyCall*/);
         logd("Mock connection state from alerting to active ");
         mDC.state = DriverCall.State.ALERTING;
         connection.update(mDC);
@@ -157,7 +163,8 @@
     public void testPostDialWait() {
         doReturn(PhoneConstants.PHONE_TYPE_CDMA).when(mPhone).getPhoneType();
         connection = new GsmCdmaConnection(mPhone,
-                String.format("+1 (700).555-41NN%c1234", PhoneNumberUtils.WAIT),mCT,null);
+                String.format("+1 (700).555-41NN%c1234", PhoneNumberUtils.WAIT),mCT,null,
+                false /*isEmergencyCall*/);
         logd("Mock connection state transition from alerting to active ");
         mDC.state = DriverCall.State.ALERTING;
         connection.update(mDC);
@@ -173,7 +180,8 @@
     @Test @SmallTest
     public void testHangUpConnection() {
         connection = new GsmCdmaConnection(mPhone, String.format(
-                "+1 (700).555-41NN%c1234", PhoneNumberUtils.PAUSE), mCT, null);
+                "+1 (700).555-41NN%c1234", PhoneNumberUtils.PAUSE), mCT, null,
+                false /*isEmergencyCall*/);
         mDC.state = DriverCall.State.ACTIVE;
         connection.update(mDC);
         logd("Hangup the connection locally");
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
index 4bccdd9..0f2192b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
@@ -16,10 +16,13 @@
 
 package com.android.internal.telephony.dataconnection;
 
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Message;
+import android.telephony.CarrierConfigManager;
 import android.telephony.ServiceState;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -32,15 +35,20 @@
 import com.android.internal.util.IState;
 import com.android.internal.util.StateMachine;
 
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
 
+import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 
 import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doReturn;
@@ -246,4 +254,47 @@
         AsyncResult ar = new AsyncResult(null, response, null);
         assertEquals(RetryManager.NO_RETRY, getSuggestedRetryDelay(ar));
     }
+
+    private NetworkInfo getNetworkInfo() throws Exception {
+        Field f = DataConnection.class.getDeclaredField("mNetworkInfo");
+        f.setAccessible(true);
+        return (NetworkInfo) f.get(mDc);
+    }
+
+    private NetworkCapabilities getCopyNetworkCapabilities() throws Exception {
+        Method method = DataConnection.class.getDeclaredMethod("getCopyNetworkCapabilities");
+        method.setAccessible(true);
+        return (NetworkCapabilities) method.invoke(mDc);
+    }
+
+    @Test
+    @SmallTest
+    public void testMeteredCapability() throws Exception {
+
+        mContextFixture.getCarrierConfigBundle().
+                putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+                new String[] {"default"});
+
+        testConnectEvent();
+
+        assertTrue(getNetworkInfo().isMetered());
+        assertFalse(getCopyNetworkCapabilities().
+                hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED));
+    }
+
+    @Test
+    @SmallTest
+    public void testNonMeteredCapability() throws Exception {
+
+        doReturn(1).when(mPhone).getSubId();
+        mContextFixture.getCarrierConfigBundle().
+                putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+                        new String[] {"mms"});
+
+        testConnectEvent();
+
+        assertFalse(getNetworkInfo().isMetered());
+        assertTrue(getCopyNetworkCapabilities().
+                hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED));
+    }
 }
\ No newline at end of file