Merge "Update the waitUntilReady/setReady logic to use java blocking"
diff --git a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
index e4d696b..504bf1d 100644
--- a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -22,12 +22,12 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.telephony.CellInfo;
+import android.telephony.PreciseCallState;
 import android.telephony.Rlog;
-import android.telephony.VoLteServiceState;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
-import android.telephony.PreciseCallState;
+import android.telephony.VoLteServiceState;
 
 import com.android.internal.telephony.PhoneConstantConversions;
 
@@ -179,14 +179,13 @@
             if (mRegistry != null) {
                 mRegistry.notifyDataConnectionForSubscriber(subId,
                     PhoneConstantConversions.convertDataState(state),
-                    sender.isDataConnectivityPossible(apnType), reason,
-                    sender.getActiveApnHost(apnType),
-                    apnType,
-                    linkProperties,
-                    networkCapabilities,
-                    ((telephony!=null) ? telephony.getDataNetworkType(subId) :
-                    TelephonyManager.NETWORK_TYPE_UNKNOWN),
-                    roaming);
+                        sender.isDataAllowed(), reason,
+                        sender.getActiveApnHost(apnType),
+                        apnType,
+                        linkProperties,
+                        networkCapabilities,
+                        ((telephony != null) ? telephony.getDataNetworkType(subId) :
+                                TelephonyManager.NETWORK_TYPE_UNKNOWN), roaming);
             }
         } catch (RemoteException ex) {
             // system process is dead
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 303c921..8f2f048 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -2732,20 +2732,11 @@
     /**
      * Report on whether data connectivity is allowed.
      */
-    public boolean isDataConnectivityPossible() {
-        return isDataConnectivityPossible(PhoneConstants.APN_TYPE_DEFAULT);
+    public boolean isDataAllowed() {
+        return ((mDcTracker != null) && (mDcTracker.isDataAllowed(null)));
     }
 
     /**
-     * Report on whether data connectivity is allowed for an APN.
-     */
-    public boolean isDataConnectivityPossible(String apnType) {
-        return ((mDcTracker != null) &&
-                (mDcTracker.isDataPossible(apnType)));
-    }
-
-
-    /**
      * Action set from carrier signalling broadcast receivers to enable/disable metered apns.
      */
     public void carrierActionSetMeteredApnsEnabled(boolean enabled) {
@@ -3488,7 +3479,6 @@
         pw.println(" getPhoneType()=" + getPhoneType());
         pw.println(" getVoiceMessageCount()=" + getVoiceMessageCount());
         pw.println(" getActiveApnTypes()=" + getActiveApnTypes());
-        pw.println(" isDataConnectivityPossible()=" + isDataConnectivityPossible());
         pw.println(" needsOtaServiceProvisioning=" + needsOtaServiceProvisioning());
         pw.flush();
         pw.println("++++++++++++++++++++++++++++++++");
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index 7db8210..65b4322 100644
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -602,6 +602,23 @@
 
     @VisibleForTesting
     public void updatePhoneType() {
+        // If we are previously voice roaming, we need to notify that roaming status changed before
+        // we change back to non-roaming.
+        if (mSS != null && mSS.getVoiceRoaming()) {
+            mVoiceRoamingOffRegistrants.notifyRegistrants();
+        }
+
+        // If we are previously data roaming, we need to notify that roaming status changed before
+        // we change back to non-roaming.
+        if (mSS != null && mSS.getDataRoaming()) {
+            mDataRoamingOffRegistrants.notifyRegistrants();
+        }
+
+        // If we are previously in service, we need to notify that we are out of service now.
+        if (mSS != null && mSS.getDataRegState() == ServiceState.STATE_IN_SERVICE) {
+            mDetachedRegistrants.notifyRegistrants();
+        }
+
         mSS = new ServiceState();
         mNewSS = new ServiceState();
         mLastCellInfoListTime = 0;
@@ -673,11 +690,7 @@
 
         logPhoneTypeChange();
 
-        // Tell everybody that we've thrown away state and are starting over with
-        // empty, detached ServiceStates.
-        mVoiceRoamingOffRegistrants.notifyRegistrants();
-        mDataRoamingOffRegistrants.notifyRegistrants();
-        mDetachedRegistrants.notifyRegistrants();
+        // Tell everybody that the registration state and RAT have changed.
         notifyDataRegStateRilRadioTechnologyChanged();
     }
 
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java b/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java
index 00c77bc..0ce1991 100644
--- a/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java
+++ b/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java
@@ -24,6 +24,7 @@
 import android.telephony.ServiceState;
 import android.text.TextUtils;
 
+import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.uicc.IccRecords;
@@ -404,15 +405,41 @@
         return false;
     }
 
-    public static boolean isMeteredApnType(String type, Context context, int subId,
-                                           boolean isRoaming) {
+    /**
+     * Check if this APN type is metered.
+     *
+     * @param type The APN type
+     * @param phone The phone object
+     * @return True if the APN type is metered, otherwise false.
+     */
+    public static boolean isMeteredApnType(String type, Phone phone) {
+        if (phone == null) {
+            return true;
+        }
 
-        String carrierConfig = (isRoaming) ?
-                CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS :
-                CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS;
+        boolean isRoaming = phone.getServiceState().getDataRoaming();
+        boolean isIwlan = phone.getServiceState().getRilDataRadioTechnology()
+                == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
+        int subId = phone.getSubId();
+
+        String carrierConfig;
+        // First check if the device is in IWLAN mode. If yes, use the IWLAN metered APN list. Then
+        // check if the device is roaming. If yes, use the roaming metered APN list. Otherwise, use
+        // the normal metered APN list.
+        if (isIwlan) {
+            carrierConfig = CarrierConfigManager.KEY_CARRIER_METERED_IWLAN_APN_TYPES_STRINGS;
+        } else if (isRoaming) {
+            carrierConfig = CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS;
+        } else {
+            carrierConfig = CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS;
+        }
+
+        if (DBG) {
+            Rlog.d(LOG_TAG, "isMeteredApnType: isRoaming=" + isRoaming + ", isIwlan=" + isIwlan);
+        }
 
         CarrierConfigManager configManager = (CarrierConfigManager)
-                context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+                phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
         if (configManager == null) {
             Rlog.e(LOG_TAG, "Carrier config service is not available");
             return true;
@@ -432,44 +459,49 @@
 
         HashSet<String> meteredApnSet = new HashSet<>(Arrays.asList(meteredApnTypes));
         if (DBG) {
-            Rlog.d(LOG_TAG, "For subId = " + subId + ", metered APN types are " +
-                    Arrays.toString(meteredApnSet.toArray()) +
-                    " isRoaming: " + isRoaming);
+            Rlog.d(LOG_TAG, "For subId = " + subId + ", metered APN types are "
+                    + Arrays.toString(meteredApnSet.toArray()));
         }
 
         // If all types of APN are metered, then this APN setting must be metered.
         if (meteredApnSet.contains(PhoneConstants.APN_TYPE_ALL)) {
-            if (DBG) Rlog.d(LOG_TAG, "All APN types are metered. isRoaming: " + isRoaming);
+            if (DBG) Rlog.d(LOG_TAG, "All APN types are metered.");
             return true;
         }
 
         if (meteredApnSet.contains(type)) {
-            if (DBG) Rlog.d(LOG_TAG, type + " is metered. isRoaming: " + isRoaming);
+            if (DBG) Rlog.d(LOG_TAG, type + " is metered.");
             return true;
         } else if (type.equals(PhoneConstants.APN_TYPE_ALL)) {
             // Assuming no configuration error, if at least one APN type is
             // metered, then this APN setting is metered.
             if (meteredApnSet.size() > 0) {
-                if (DBG) Rlog.d(LOG_TAG, "APN_TYPE_ALL APN is metered. isRoaming: " +
-                        isRoaming);
+                if (DBG) Rlog.d(LOG_TAG, "APN_TYPE_ALL APN is metered.");
                 return true;
             }
         }
 
-        if (DBG) Rlog.d(LOG_TAG, type + " is not metered. isRoaming: " + isRoaming);
+        if (DBG) Rlog.d(LOG_TAG, type + " is not metered.");
         return false;
     }
 
-    public boolean isMetered(Context context, int subId, boolean isRoaming ) {
+    /**
+     * Check if this APN setting is metered.
+     *
+     * @param phone The phone object
+     * @return True if this APN setting is metered, otherwise false.
+     */
+    public boolean isMetered(Phone phone) {
+        if (phone == null) {
+            return true;
+        }
+
         for (String type : types) {
             // If one of the APN type is metered, then this APN setting is metered.
-            if (isMeteredApnType(type, context, subId, isRoaming)) {
-                if (DBG) Rlog.d(LOG_TAG, "Metered. APN = " + toString() +
-                        "isRoaming: " + isRoaming);
+            if (isMeteredApnType(type, phone)) {
                 return true;
             }
         }
-        if (DBG) Rlog.d(LOG_TAG, "Not metered. APN = " + toString() + "isRoaming: " + isRoaming);
         return false;
     }
 
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
index ffc5b6f..8662bd3 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
@@ -259,10 +259,6 @@
 
     /* Getter functions */
 
-    NetworkCapabilities getCopyNetworkCapabilities() {
-        return makeNetworkCapabilities();
-    }
-
     LinkProperties getCopyLinkProperties() {
         return new LinkProperties(mLinkProperties);
     }
@@ -856,8 +852,7 @@
 
         // Do we need a restricted network to satisfy the request?
         // Is this network metered?  If not, then don't add restricted
-        if (!mApnSetting.isMetered(mPhone.getContext(), mPhone.getSubId(),
-                mPhone.getServiceState().getDataRoaming())) {
+        if (!mApnSetting.isMetered(mPhone)) {
             return;
         }
 
@@ -865,7 +860,7 @@
         mRestrictedNetworkOverride = !mDct.isDataEnabled();
     }
 
-    private NetworkCapabilities makeNetworkCapabilities() {
+    NetworkCapabilities getNetworkCapabilities() {
         NetworkCapabilities result = new NetworkCapabilities();
         result.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
 
@@ -873,9 +868,7 @@
             for (String type : mApnSetting.types) {
                 if (!mRestrictedNetworkOverride
                         && (mConnectionParams != null && mConnectionParams.mUnmeteredUseOnly)
-                        && ApnSetting.isMeteredApnType(type,
-                        mPhone.getContext(), mPhone.getSubId(),
-                        mPhone.getServiceState().getDataRoaming())) {
+                        && ApnSetting.isMeteredApnType(type, mPhone)) {
                     log("Dropped the metered " + type + " for the unmetered data call.");
                     continue;
                 }
@@ -938,8 +931,7 @@
             // 2. The non-restricted data and is intended for unmetered use only.
             if (((mConnectionParams != null && mConnectionParams.mUnmeteredUseOnly)
                     && !mRestrictedNetworkOverride)
-                    || !mApnSetting.isMetered(mPhone.getContext(), mPhone.getSubId(),
-                    mPhone.getServiceState().getDataRoaming())) {
+                    || !mApnSetting.isMetered(mPhone)) {
                 result.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
                 mNetworkInfo.setMetered(false);
             } else {
@@ -1158,7 +1150,7 @@
                     break;
                 }
                 case DcAsyncChannel.REQ_GET_NETWORK_CAPABILITIES: {
-                    NetworkCapabilities nc = getCopyNetworkCapabilities();
+                    NetworkCapabilities nc = getNetworkCapabilities();
                     if (VDBG) log("REQ_GET_NETWORK_CAPABILITIES networkCapabilities" + nc);
                     mAc.replyToMessage(msg, DcAsyncChannel.RSP_GET_NETWORK_CAPABILITIES, nc);
                     break;
@@ -1220,7 +1212,7 @@
                             TelephonyManager.getNetworkTypeName(networkType));
                     if (mNetworkAgent != null) {
                         updateNetworkInfoSuspendState();
-                        mNetworkAgent.sendNetworkCapabilities(makeNetworkCapabilities());
+                        mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities());
                         mNetworkAgent.sendNetworkInfo(mNetworkInfo);
                         mNetworkAgent.sendLinkProperties(mLinkProperties);
                     }
@@ -1545,34 +1537,23 @@
 
             // verify and get updated information in case these things
             // are obsolete
-            {
-                ServiceState ss = mPhone.getServiceState();
-                final int networkType = ss.getDataNetworkType();
-                if (mNetworkInfo.getSubtype() != networkType) {
-                    log("DcActiveState with incorrect subtype (" + mNetworkInfo.getSubtype() +
-                            ", " + networkType + "), updating.");
-                }
-                mNetworkInfo.setSubtype(networkType, TelephonyManager.getNetworkTypeName(networkType));
-                final boolean roaming = ss.getDataRoaming();
-                if (roaming != mNetworkInfo.isRoaming()) {
-                    log("DcActiveState with incorrect roaming (" + mNetworkInfo.isRoaming() +
-                            ", " + roaming +"), updating.");
-                }
-                mNetworkInfo.setRoaming(roaming);
+            ServiceState ss = mPhone.getServiceState();
+            final int networkType = ss.getDataNetworkType();
+            if (mNetworkInfo.getSubtype() != networkType) {
+                log("DcActiveState with incorrect subtype (" + mNetworkInfo.getSubtype()
+                        + ", " + networkType + "), updating.");
+            }
+            mNetworkInfo.setSubtype(networkType, TelephonyManager.getNetworkTypeName(networkType));
+            final boolean roaming = ss.getDataRoaming();
+            if (roaming != mNetworkInfo.isRoaming()) {
+                log("DcActiveState with incorrect roaming (" + mNetworkInfo.isRoaming()
+                        + ", " + roaming + "), updating.");
             }
 
-            boolean createNetworkAgent = true;
-            // If a disconnect is already pending, avoid notifying all of connected
-            if (hasMessages(EVENT_DISCONNECT) ||
-                    hasMessages(EVENT_DISCONNECT_ALL) ||
-                    hasDeferredMessages(EVENT_DISCONNECT) ||
-                    hasDeferredMessages(EVENT_DISCONNECT_ALL)) {
-                log("DcActiveState: skipping notifyAllOfConnected()");
-                createNetworkAgent = false;
-            } else {
-                // If we were retrying there maybe more than one, otherwise they'll only be one.
-                notifyAllOfConnected(Phone.REASON_CONNECTED);
-            }
+            mNetworkInfo.setRoaming(roaming);
+
+            // If we were retrying there maybe more than one, otherwise they'll only be one.
+            notifyAllOfConnected(Phone.REASON_CONNECTED);
 
             mPhone.getCallTracker().registerForVoiceCallStarted(getHandler(),
                     DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_STARTED, null);
@@ -1597,12 +1578,10 @@
             }
             misc.subscriberId = mPhone.getSubscriberId();
 
-            if (createNetworkAgent) {
-                setNetworkRestriction();
-                mNetworkAgent = new DcNetworkAgent(getHandler().getLooper(), mPhone.getContext(),
-                        "DcNetworkAgent", mNetworkInfo, makeNetworkCapabilities(), mLinkProperties,
-                        50, misc);
-            }
+            setNetworkRestriction();
+            mNetworkAgent = new DcNetworkAgent(getHandler().getLooper(), mPhone.getContext(),
+                    "DcNetworkAgent", mNetworkInfo, getNetworkCapabilities(), mLinkProperties,
+                    50, misc);
         }
 
         @Override
@@ -1722,7 +1701,7 @@
                     } else {
                         final ArrayList<Integer> capInfo = (ArrayList<Integer>)ar.result;
                         final int lceBwDownKbps = capInfo.get(0);
-                        NetworkCapabilities nc = makeNetworkCapabilities();
+                        NetworkCapabilities nc = getNetworkCapabilities();
                         if (mPhone.getLceStatus() == RILConstants.LCE_ACTIVE) {
                             nc.setLinkDownstreamBandwidthKbps(lceBwDownKbps);
                             if (mNetworkAgent != null) {
@@ -2075,7 +2054,7 @@
                 + " mLastFailCause=" + mLastFailCause
                 + " mTag=" + mTag
                 + " mLinkProperties=" + mLinkProperties
-                + " linkCapabilities=" + makeNetworkCapabilities()
+                + " linkCapabilities=" + getNetworkCapabilities()
                 + " mRestrictedNetworkOverride=" + mRestrictedNetworkOverride;
     }
 
@@ -2125,7 +2104,7 @@
         pw.flush();
         pw.println(" mDataRegState=" + mDataRegState);
         pw.println(" mRilRat=" + mRilRat);
-        pw.println(" mNetworkCapabilities=" + makeNetworkCapabilities());
+        pw.println(" mNetworkCapabilities=" + getNetworkCapabilities());
         pw.println(" mCreateTime=" + TimeUtils.logTimeOfDay(mCreateTime));
         pw.println(" mLastFailTime=" + TimeUtils.logTimeOfDay(mLastFailTime));
         pw.println(" mLastFailCause=" + mLastFailCause);
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java b/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java
new file mode 100644
index 0000000..e949acf
--- /dev/null
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2017 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.internal.telephony.dataconnection;
+
+import java.util.HashSet;
+
+/**
+ * The class to describe the reasons of allowing or disallowing to establish a data connection.
+ */
+public class DataConnectionReasons {
+    private HashSet<DataDisallowedReasonType> mDataDisallowedReasonSet = new HashSet<>();
+    private DataAllowedReasonType mDataAllowedReason = DataAllowedReasonType.NONE;
+
+    public DataConnectionReasons() {}
+
+    void add(DataDisallowedReasonType reason) {
+        // Adding a disallowed reason will clean up the allowed reason because they are
+        // mutual exclusive.
+        mDataAllowedReason = DataAllowedReasonType.NONE;
+        mDataDisallowedReasonSet.add(reason);
+    }
+
+    void add(DataAllowedReasonType reason) {
+        // Adding an allowed reason will clean up the disallowed reasons because they are
+        // mutual exclusive.
+        mDataDisallowedReasonSet.clear();
+
+        // Only higher priority allowed reason can overwrite the old one. See
+        // DataAllowedReasonType for the oder.
+        if (reason.ordinal() > mDataAllowedReason.ordinal()) {
+            mDataAllowedReason = reason;
+        }
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder reasonStr = new StringBuilder();
+        if (mDataDisallowedReasonSet.size() > 0) {
+            reasonStr.append("Data disallowed, reasons:");
+            for (DataDisallowedReasonType reason : mDataDisallowedReasonSet) {
+                reasonStr.append(" ").append(reason);
+            }
+        } else {
+            reasonStr.append("Data allowed, reason:");
+            reasonStr.append(" ").append(mDataAllowedReason);
+        }
+        return reasonStr.toString();
+    }
+
+    void copyFrom(DataConnectionReasons reasons) {
+        this.mDataDisallowedReasonSet = reasons.mDataDisallowedReasonSet;
+        this.mDataAllowedReason = reasons.mDataAllowedReason;
+    }
+
+    boolean allowed() {
+        return mDataDisallowedReasonSet.size() == 0;
+    }
+
+    boolean contains(DataDisallowedReasonType reason) {
+        return mDataDisallowedReasonSet.contains(reason);
+    }
+
+    boolean contains(DataAllowedReasonType reason) {
+        return reason == mDataAllowedReason;
+    }
+
+    boolean containsHardDisallowedReasons() {
+        for (DataDisallowedReasonType reason : mDataDisallowedReasonSet) {
+            if (reason.isHardReason()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    // Disallowed reasons. There could be multiple reasons if data connection is not allowed.
+    enum DataDisallowedReasonType {
+        // Soft failure reasons. Normally the reasons from users or policy settings.
+        DATA_DISABLED(false),                   // Data is disabled by the user or policy.
+        ROAMING_DISABLED(false),                // Data roaming is disabled by the user.
+
+        // Belows are all hard failure reasons.
+        NOT_ATTACHED(true),
+        RECORD_NOT_LOADED(true),
+        INVALID_PHONE_STATE(true),
+        CONCURRENT_VOICE_DATA_NOT_ALLOWED(true),
+        PS_RESTRICTED(true),
+        UNDESIRED_POWER_STATE(true),
+        INTERNAL_DATA_DISABLED(true),
+        DEFAULT_DATA_UNSELECTED(true),
+        RADIO_DISABLED_BY_CARRIER(true),
+        APN_NOT_CONNECTABLE(true),
+        ON_IWLAN(true),
+        IN_ECBM(true);
+
+        private boolean mIsHardReason;
+
+        boolean isHardReason() {
+            return mIsHardReason;
+        }
+
+        DataDisallowedReasonType(boolean isHardReason) {
+            mIsHardReason = isHardReason;
+        }
+    }
+
+    // Data allowed reasons. There will be only one reason if data is allowed.
+    enum DataAllowedReasonType {
+        // Note that unlike disallowed reasons, we only have one allowed reason every time
+        // when we check data is allowed or not. The order of these allowed reasons is very
+        // important. The lower ones take precedence over the upper ones.
+        NONE,
+        NORMAL,
+        UNMETERED_APN,
+        RESTRICTED_REQUEST,
+        EMERGENCY_APN,
+    }
+}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java b/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java
index ea54538..a8bffd3 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java
@@ -63,6 +63,13 @@
 
     private final RegistrantList mDataEnabledChangedRegistrants = new RegistrantList();
 
+    @Override
+    public String toString() {
+        return "[mInternalDataEnabled=" + mInternalDataEnabled + ", mUserDataEnabled="
+                + mUserDataEnabled + ", mPolicyDataEnabled=" + mPolicyDataEnabled
+                + ", mCarrierDataEnabled=" + mCarrierDataEnabled + "]";
+    }
+
     public synchronized void setInternalDataEnabled(boolean enabled) {
         boolean prevDataEnabled = isDataEnabled();
         mInternalDataEnabled = enabled;
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java b/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java
index 77cd6b5..67d91d3 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java
@@ -342,7 +342,7 @@
                 value = null;
             }
         } else {
-            value = mDc.getCopyNetworkCapabilities();
+            value = mDc.getNetworkCapabilities();
         }
         return value;
     }
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
index 9f962bb..f00bfe6 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -84,8 +84,9 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.RILConstants;
-import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.dataconnection.DataConnectionReasons.DataAllowedReasonType;
+import com.android.internal.telephony.dataconnection.DataConnectionReasons.DataDisallowedReasonType;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.telephony.uicc.IccRecords;
 import com.android.internal.telephony.uicc.UiccController;
@@ -98,7 +99,6 @@
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.PriorityQueue;
@@ -160,57 +160,6 @@
     private static final String INTENT_DATA_STALL_ALARM =
             "com.android.internal.telephony.data-stall";
 
-    @VisibleForTesting
-    public static class DataAllowFailReason {
-        private HashSet<DataAllowFailReasonType> mDataAllowFailReasonSet = new HashSet<>();
-
-        public void addDataAllowFailReason(DataAllowFailReasonType type) {
-            mDataAllowFailReasonSet.add(type);
-        }
-
-        public String getDataAllowFailReason() {
-            StringBuilder failureReason = new StringBuilder();
-            failureReason.append("isDataAllowed: No");
-            for(DataAllowFailReasonType reason : mDataAllowFailReasonSet) {
-                failureReason.append(reason.mFailReasonStr);
-            }
-            return failureReason.toString();
-        }
-
-        public boolean isFailForSingleReason(DataAllowFailReasonType failReasonType) {
-            return (mDataAllowFailReasonSet.size() == 1) &&
-                    (mDataAllowFailReasonSet.contains(failReasonType));
-        }
-
-        public void clearAllReasons() {
-            mDataAllowFailReasonSet.clear();
-        }
-
-        public boolean isFailed() {
-            return mDataAllowFailReasonSet.size() > 0;
-        }
-    }
-
-    @VisibleForTesting
-    public enum DataAllowFailReasonType {
-        NOT_ATTACHED(" - Not attached"),
-        RECORD_NOT_LOADED(" - SIM not loaded"),
-        ROAMING_DISABLED(" - Roaming and data roaming not enabled"),
-        INVALID_PHONE_STATE(" - PhoneState is not idle"),
-        CONCURRENT_VOICE_DATA_NOT_ALLOWED(" - Concurrent voice and data not allowed"),
-        PS_RESTRICTED(" - mIsPsRestricted= true"),
-        UNDESIRED_POWER_STATE(" - desiredPowerState= false"),
-        INTERNAL_DATA_DISABLED(" - mInternalDataEnabled= false"),
-        DEFAULT_DATA_UNSELECTED(" - defaultDataSelected= false"),
-        RADIO_DISABLED_BY_CARRIER(" - powerStateFromCarrier= false");
-
-        public String mFailReasonStr;
-
-        DataAllowFailReasonType(String reason) {
-            mFailReasonStr = reason;
-        }
-    }
-
     private DcTesterFailBringUpAll mDcTesterFailBringUpAll;
     private DcController mDcc;
 
@@ -955,8 +904,7 @@
                             // data connection.
                             apnContext.setReason(Phone.REASON_DATA_ENABLED);
                             cleanUpConnection(true, apnContext);
-                        } else if (apnContext.getApnSetting().isMetered(mPhone.getContext(),
-                                mPhone.getSubId(), mPhone.getServiceState().getDataRoaming())
+                        } else if (apnContext.getApnSetting().isMetered(mPhone)
                                 && (netCaps != null && netCaps.hasCapability(
                                         NetworkCapabilities.NET_CAPABILITY_NOT_METERED))) {
                             if (DBG) {
@@ -1098,38 +1046,6 @@
         }
     }
 
-    public boolean isDataPossible(String apnType) {
-        ApnContext apnContext = mApnContexts.get(apnType);
-        if (apnContext == null) {
-            return false;
-        }
-        boolean apnContextIsEnabled = apnContext.isEnabled();
-        DctConstants.State apnContextState = apnContext.getState();
-        boolean apnTypePossible = !(apnContextIsEnabled &&
-                (apnContextState == DctConstants.State.FAILED));
-        boolean isEmergencyApn = apnContext.getApnType().equals(PhoneConstants.APN_TYPE_EMERGENCY);
-        // Set the emergency APN availability status as TRUE irrespective of conditions checked in
-        // isDataAllowed() like IN_SERVICE, MOBILE DATA status etc.
-        boolean dataAllowed = isEmergencyApn || isDataAllowed(null);
-        boolean possible = dataAllowed && apnTypePossible;
-
-        if ((apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DEFAULT)
-                    || apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IA))
-                && (mPhone.getServiceState().getRilDataRadioTechnology()
-                == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN)) {
-            log("Default data call activation not possible in iwlan.");
-            possible = false;
-        }
-
-        if (VDBG) {
-            log(String.format("isDataPossible(%s): possible=%b isDataAllowed=%b " +
-                            "apnTypePossible=%b apnContextisEnabled=%b apnContextState()=%s",
-                    apnType, possible, dataAllowed, apnTypePossible,
-                    apnContextIsEnabled, apnContextState));
-        }
-        return possible;
-    }
-
     @Override
     protected void finalize() {
         if(DBG && mPhone != null) log("finalize");
@@ -1313,47 +1229,11 @@
         }
     }
 
-    /**
-     * Report on whether data connectivity is enabled for any APN.
-     * @return {@code false} if data connectivity has been explicitly disabled,
-     * {@code true} otherwise.
-     */
-    public boolean getAnyDataEnabled() {
-        if (!mDataEnabledSettings.isDataEnabled()) return false;
-        DataAllowFailReason failureReason = new DataAllowFailReason();
-        if (!isDataAllowed(failureReason)) {
-            if (DBG) log(failureReason.getDataAllowFailReason());
-            return false;
-        }
-        for (ApnContext apnContext : mApnContexts.values()) {
-            // Make sure we don't have a context that is going down
-            // and is explicitly disabled.
-            if (isDataAllowedForApn(apnContext)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     @VisibleForTesting
     public boolean isDataEnabled() {
         return mDataEnabledSettings.isDataEnabled();
     }
 
-    private boolean isDataAllowedForApn(ApnContext apnContext) {
-        //If RAT is iwlan then dont allow default/IA PDP at all.
-        //Rest of APN types can be evaluated for remaining conditions.
-        if ((apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DEFAULT)
-                    || apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IA))
-                && (mPhone.getServiceState().getRilDataRadioTechnology()
-                == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN)) {
-            log("Default data call activation not allowed in iwlan.");
-            return false;
-        }
-
-        return apnContext.isReady();
-    }
-
     //****** Called from ServiceStateTracker
     /**
      * Invoked when ServiceStateTracker observes a transition from GPRS
@@ -1389,30 +1269,58 @@
         setupDataOnConnectableApns(Phone.REASON_DATA_ATTACHED);
     }
 
-    private boolean isDataAllowed(DataAllowFailReason failureReason) {
-        final boolean internalDataEnabled;
-        internalDataEnabled = mDataEnabledSettings.isInternalDataEnabled();
+    /**
+     * Check if it is allowed to make a data connection (without checking APN context specific
+     * conditions).
+     *
+     * @param dataConnectionReasons Data connection allowed or disallowed reasons as the output
+     *                              param. It's okay to pass null here and no reasons will be
+     *                              provided.
+     * @return True if data connection is allowed, otherwise false.
+     */
+    public boolean isDataAllowed(DataConnectionReasons dataConnectionReasons) {
+        return isDataAllowed(null, dataConnectionReasons);
+    }
 
+    /**
+     * Check if it is allowed to make a data connection for a given APN type.
+     *
+     * @param apnContext APN context. If passing null, then will only check general but not APN
+     *                   specific conditions (e.g. APN state, metered/unmetered APN).
+     * @param dataConnectionReasons Data connection allowed or disallowed reasons as the output
+     *                              param. It's okay to pass null here and no reasons will be
+     *                              provided.
+     * @return True if data connection is allowed, otherwise false.
+     */
+    boolean isDataAllowed(ApnContext apnContext, DataConnectionReasons dataConnectionReasons) {
+        // Step 1: Get all environment conditions.
+        // Step 2: Special handling for emergency APN.
+        // Step 3. Build disallowed reasons.
+        // Step 4: Determine if data should be allowed in some special conditions.
+
+        DataConnectionReasons reasons = new DataConnectionReasons();
+
+        // Step 1: Get all environment conditions.
+        final boolean internalDataEnabled = mDataEnabledSettings.isInternalDataEnabled();
         boolean attachedState = mAttached.get();
         boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
         boolean radioStateFromCarrier = mPhone.getServiceStateTracker().getPowerStateFromCarrier();
+        // TODO: Remove this hack added by ag/641832.
         int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
         if (radioTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN) {
             desiredPowerState = true;
             radioStateFromCarrier = true;
         }
 
-        IccRecords r = mIccRecords.get();
-        boolean recordsLoaded = false;
-        if (r != null) {
-            recordsLoaded = r.getRecordsLoaded();
-            if (DBG && !recordsLoaded) log("isDataAllowed getRecordsLoaded=" + recordsLoaded);
-        }
+        boolean recordsLoaded = mIccRecords.get() != null && mIccRecords.get().getRecordsLoaded();
 
-        int dataSub = SubscriptionManager.getDefaultDataSubscriptionId();
-        boolean defaultDataSelected = SubscriptionManager.isValidSubscriptionId(dataSub);
+        boolean defaultDataSelected = SubscriptionManager.isValidSubscriptionId(
+                SubscriptionManager.getDefaultDataSubscriptionId());
 
-        PhoneConstants.State state = PhoneConstants.State.IDLE;
+        boolean isMeteredApnType = apnContext == null
+                || ApnSetting.isMeteredApnType(apnContext.getApnType(), mPhone);
+
+        PhoneConstants.State phoneState = PhoneConstants.State.IDLE;
         // Note this is explicitly not using mPhone.getState.  See b/19090488.
         // mPhone.getState reports the merge of CS and PS (volte) voice call state
         // but we only care about CS calls here for data/voice concurrency issues.
@@ -1421,52 +1329,110 @@
         // This should be redesigned to ask explicitly what we want:
         // voiceCallStateAllowDataCall, or dataCallAllowed or something similar.
         if (mPhone.getCallTracker() != null) {
-            state = mPhone.getCallTracker().getState();
+            phoneState = mPhone.getCallTracker().getState();
         }
 
-        if (failureReason != null) failureReason.clearAllReasons();
+        // Step 2: Special handling for emergency APN.
+        if (apnContext != null
+                && apnContext.getApnType().equals(PhoneConstants.APN_TYPE_EMERGENCY)
+                && apnContext.isConnectable()) {
+            // If this is an emergency APN, as long as the APN is connectable, we
+            // should allow it.
+            if (dataConnectionReasons != null) {
+                dataConnectionReasons.add(DataAllowedReasonType.EMERGENCY_APN);
+            }
+            // Bail out without further checks.
+            return true;
+        }
+
+        // Step 3. Build disallowed reasons.
+        if (apnContext != null && !apnContext.isConnectable()) {
+            reasons.add(DataDisallowedReasonType.APN_NOT_CONNECTABLE);
+        }
+
+        // If RAT is IWLAN then don't allow default/IA PDP at all.
+        // Rest of APN types can be evaluated for remaining conditions.
+        if ((apnContext != null && (apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DEFAULT)
+                || apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IA)))
+                && (radioTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN)) {
+            reasons.add(DataDisallowedReasonType.ON_IWLAN);
+        }
+
+        if (isEmergency()) {
+            reasons.add(DataDisallowedReasonType.IN_ECBM);
+        }
+
         if (!(attachedState || mAutoAttachOnCreation.get())) {
-            if(failureReason == null) return false;
-            failureReason.addDataAllowFailReason(DataAllowFailReasonType.NOT_ATTACHED);
+            reasons.add(DataDisallowedReasonType.NOT_ATTACHED);
         }
         if (!recordsLoaded) {
-            if(failureReason == null) return false;
-            failureReason.addDataAllowFailReason(DataAllowFailReasonType.RECORD_NOT_LOADED);
+            reasons.add(DataDisallowedReasonType.RECORD_NOT_LOADED);
         }
-        if (state != PhoneConstants.State.IDLE &&
-                !mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
-            if(failureReason == null) return false;
-            failureReason.addDataAllowFailReason(DataAllowFailReasonType.INVALID_PHONE_STATE);
-            failureReason.addDataAllowFailReason(
-                    DataAllowFailReasonType.CONCURRENT_VOICE_DATA_NOT_ALLOWED);
+        if (phoneState != PhoneConstants.State.IDLE
+                && !mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
+            reasons.add(DataDisallowedReasonType.INVALID_PHONE_STATE);
+            reasons.add(DataDisallowedReasonType.CONCURRENT_VOICE_DATA_NOT_ALLOWED);
         }
         if (!internalDataEnabled) {
-            if(failureReason == null) return false;
-            failureReason.addDataAllowFailReason(DataAllowFailReasonType.INTERNAL_DATA_DISABLED);
+            reasons.add(DataDisallowedReasonType.INTERNAL_DATA_DISABLED);
         }
         if (!defaultDataSelected) {
-            if(failureReason == null) return false;
-            failureReason.addDataAllowFailReason(
-                    DataAllowFailReasonType.DEFAULT_DATA_UNSELECTED);
+            reasons.add(DataDisallowedReasonType.DEFAULT_DATA_UNSELECTED);
         }
         if (mPhone.getServiceState().getDataRoaming() && !getDataRoamingEnabled()) {
-            if(failureReason == null) return false;
-            failureReason.addDataAllowFailReason(DataAllowFailReasonType.ROAMING_DISABLED);
+            reasons.add(DataDisallowedReasonType.ROAMING_DISABLED);
         }
         if (mIsPsRestricted) {
-            if(failureReason == null) return false;
-            failureReason.addDataAllowFailReason(DataAllowFailReasonType.PS_RESTRICTED);
+            reasons.add(DataDisallowedReasonType.PS_RESTRICTED);
         }
         if (!desiredPowerState) {
-            if(failureReason == null) return false;
-            failureReason.addDataAllowFailReason(DataAllowFailReasonType.UNDESIRED_POWER_STATE);
+            reasons.add(DataDisallowedReasonType.UNDESIRED_POWER_STATE);
         }
         if (!radioStateFromCarrier) {
-            if(failureReason == null) return false;
-            failureReason.addDataAllowFailReason(DataAllowFailReasonType.RADIO_DISABLED_BY_CARRIER);
+            reasons.add(DataDisallowedReasonType.RADIO_DISABLED_BY_CARRIER);
+        }
+        if (!mDataEnabledSettings.isDataEnabled()) {
+            reasons.add(DataDisallowedReasonType.DATA_DISABLED);
         }
 
-        return failureReason == null || !failureReason.isFailed();
+        // If there are hard disallowed reasons, we should not allow data connection no matter what.
+        if (reasons.containsHardDisallowedReasons()) {
+            if (dataConnectionReasons != null) {
+                dataConnectionReasons.copyFrom(reasons);
+            }
+            return false;
+        }
+
+        // Step 4: Determine if data should be allowed in some special conditions.
+
+        // At this point, if data is not allowed, it must be because of the soft reasons. We
+        // should start to check some special conditions that data will be allowed.
+
+        // If the request APN type is unmetered and there are soft disallowed reasons (e.g. data
+        // disabled, data roaming disabled) existing, we should allow the data because the user
+        // won't be charged anyway.
+        if (!isMeteredApnType && !reasons.allowed()) {
+            reasons.add(DataAllowedReasonType.UNMETERED_APN);
+        }
+
+        // If the request is restricted and there are only soft disallowed reasons (e.g. data
+        // disabled, data roaming disabled) existing, we should allow the data.
+        if (apnContext != null
+                && !apnContext.hasNoRestrictedRequests(true)
+                && !reasons.allowed()) {
+            reasons.add(DataAllowedReasonType.RESTRICTED_REQUEST);
+        }
+
+        // If at this point, we still haven't built any disallowed reasons, we should allow data.
+        if (reasons.allowed()) {
+            reasons.add(DataAllowedReasonType.NORMAL);
+        }
+
+        if (dataConnectionReasons != null) {
+            dataConnectionReasons.copyFrom(reasons);
+        }
+
+        return reasons.allowed();
     }
 
     // arg for setupDataOnConnectableApns
@@ -1548,11 +1514,6 @@
     }
 
     private boolean trySetupData(ApnContext apnContext, ArrayList<ApnSetting> waitingApns) {
-        if (DBG) {
-            log("trySetupData for type:" + apnContext.getApnType() +
-                    " due to " + apnContext.getReason() + ", mIsPsRestricted=" + mIsPsRestricted);
-        }
-        apnContext.requestLog("trySetupData due to " + apnContext.getReason());
 
         if (mPhone.getSimulatedRadioControl() != null) {
             // Assume data is connected on the simulator
@@ -1564,58 +1525,13 @@
             return true;
         }
 
-        // Allow SETUP_DATA request for E-APN to be completed during emergency call
-        // and MOBILE DATA On/Off cases as well.
-        boolean isEmergencyApn = apnContext.getApnType().equals(PhoneConstants.APN_TYPE_EMERGENCY);
-        final ServiceStateTracker sst = mPhone.getServiceStateTracker();
-
-        DataAllowFailReason failureReason = new DataAllowFailReason();
-
-        boolean unmeteredUseOnly = false;
-        boolean isDataAllowed = isDataAllowed(failureReason);
-        boolean isMeteredApnType = ApnSetting.isMeteredApnType(apnContext.getApnType(),
-                mPhone.getContext(), mPhone.getSubId(), mPhone.getServiceState().getDataRoaming());
-        if (!isDataAllowed) {
-            // If the data not allowed due to roaming disabled, but the request APN type is not
-            // metered, we should allow data (because the user won't be charged anyway)
-            if (failureReason.isFailForSingleReason(DataAllowFailReasonType.ROAMING_DISABLED)
-                    && !isMeteredApnType) {
-                isDataAllowed = true;
-                unmeteredUseOnly = true;
-            }
-        }
-
+        DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
+        boolean isDataAllowed = isDataAllowed(apnContext, dataConnectionReasons);
+        String logStr = "trySetupData for APN type " + apnContext.getApnType() + ", reason: "
+                + apnContext.getReason() + ". " + dataConnectionReasons.toString();
+        if (DBG) log(logStr);
+        apnContext.requestLog(logStr);
         if (isDataAllowed) {
-            if (!mDataEnabledSettings.isDataEnabled()) {
-                // If the data is turned off, we should not allow a data connection.
-                isDataAllowed = false;
-
-                // But there are some exceptions we should allow data even when data is turned off.
-                if (!apnContext.hasNoRestrictedRequests(true /*exclude DUN */)) {
-                    // A restricted request can request data even when data is turned off.
-                    // Note this takes precedence over unmetered request.
-                    isDataAllowed = true;
-                    unmeteredUseOnly = false;
-                } else if (!isMeteredApnType) {
-                    // If the APN is unmetered, we should allow data even if data is turned off.
-                    // This will allow users to make VoLTE calls or send MMS while data is
-                    // turned off.
-                    isDataAllowed = true;
-
-                    // When data is off, if we allow a data call because it's unmetered, we
-                    // should restrict it for unmetered use only. For example, if one
-                    // carrier has both internet (metered) and MMS (unmetered) APNs mixed in one
-                    // APN setting, when we bring up a data call for MMS purpose, we should
-                    // restrict it for unmetered use only. We should not allow it for other
-                    // metered purposes such as internet.
-                    unmeteredUseOnly = true;
-                }
-            }
-        }
-
-        if (apnContext.isConnectable()
-                && (isEmergencyApn
-                || (isDataAllowed && isDataAllowedForApn(apnContext) && !isEmergency()))) {
             if (apnContext.getState() == DctConstants.State.FAILED) {
                 String str = "trySetupData: make a FAILED ApnContext IDLE so its reusable";
                 if (DBG) log(str);
@@ -1623,7 +1539,8 @@
                 apnContext.setState(DctConstants.State.IDLE);
             }
             int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
-            apnContext.setConcurrentVoiceAndDataAllowed(sst.isConcurrentVoiceAndDataAllowed());
+            apnContext.setConcurrentVoiceAndDataAllowed(mPhone.getServiceStateTracker()
+                    .isConcurrentVoiceAndDataAllowed());
             if (apnContext.getState() == DctConstants.State.IDLE) {
                 if (waitingApns == null) {
                     waitingApns = buildWaitingApns(apnContext.getApnType(), radioTech);
@@ -1644,7 +1561,8 @@
                 }
             }
 
-            boolean retValue = setupData(apnContext, radioTech, unmeteredUseOnly);
+            boolean retValue = setupData(apnContext, radioTech, dataConnectionReasons.contains(
+                    DataAllowedReasonType.UNMETERED_APN));
             notifyOffApnsOfAvailability(apnContext.getReason());
 
             if (DBG) log("trySetupData: X retValue=" + retValue);
@@ -1658,48 +1576,30 @@
 
             StringBuilder str = new StringBuilder();
 
-            str.append("trySetupData failed. apnContext = [type=" + apnContext.getApnType() +
-                    ", mState=" + apnContext.getState() + ", mDataEnabled=" +
-                    apnContext.isEnabled() + ", mDependencyMet=" +
-                    apnContext.getDependencyMet() + "] ");
+            str.append("trySetupData failed. apnContext = [type=" + apnContext.getApnType()
+                    + ", mState=" + apnContext.getState() + ", apnEnabled="
+                    + apnContext.isEnabled() + ", mDependencyMet="
+                    + apnContext.getDependencyMet() + "] ");
 
-            if (!apnContext.isConnectable()) {
-                str.append("isConnectable = false. ");
-            }
-            if (!isDataAllowed) {
-                str.append("data not allowed: " + failureReason.getDataAllowFailReason() + ". ");
-            }
-            if (!isDataAllowedForApn(apnContext)) {
-                str.append("isDataAllowedForApn = false. RAT = " +
-                        mPhone.getServiceState().getRilDataRadioTechnology());
-            }
             if (!mDataEnabledSettings.isDataEnabled()) {
-                str.append("isDataEnabled() = false. "
-                        + "isInternalDataEnabled = " + mDataEnabledSettings.isInternalDataEnabled()
-                        + ", userDataEnabled = " + mDataEnabledSettings.isUserDataEnabled()
-                        + ", isPolicyDataEnabled = " + mDataEnabledSettings.isPolicyDataEnabled()
-                        + ", isCarrierDataEnabled = "
-                        + mDataEnabledSettings.isCarrierDataEnabled());
+                str.append("isDataEnabled() = false. " + mDataEnabledSettings);
             }
-            if (isEmergency()) {
-                str.append("emergency = true");
+
+            // If this is a data retry, we should set the APN state to FAILED so it won't stay
+            // in SCANNING forever.
+            if (apnContext.getState() == DctConstants.State.SCANNING) {
+                apnContext.setState(DctConstants.State.FAILED);
+                str.append(" Stop retrying.");
             }
 
             if (DBG) log(str.toString());
             apnContext.requestLog(str.toString());
-
             return false;
         }
     }
 
     // Disabled apn's still need avail/unavail notifications - send them out
     private void notifyOffApnsOfAvailability(String reason) {
-        if (DBG) {
-            DataAllowFailReason failureReason = new DataAllowFailReason();
-            if (!isDataAllowed(failureReason)) {
-                log(failureReason.getDataAllowFailReason());
-            }
-        }
         for (ApnContext apnContext : mApnContexts.values()) {
             if (!mAttached.get() || !apnContext.isReady()) {
                 if (VDBG) log("notifyOffApnOfAvailability type:" + apnContext.getApnType());
@@ -1744,8 +1644,7 @@
                 // Use ApnSetting to decide metered or non-metered.
                 // Tear down all metered data connections.
                 ApnSetting apnSetting = apnContext.getApnSetting();
-                if (apnSetting != null && apnSetting.isMetered(mPhone.getContext(),
-                        mPhone.getSubId(), mPhone.getServiceState().getDataRoaming())) {
+                if (apnSetting != null && apnSetting.isMetered(mPhone)) {
                     if (DBG) log("clean up metered ApnContext Type: " + apnContext.getApnType());
                     apnContext.setReason(reason);
                     cleanUpConnection(tearDown, apnContext);
@@ -2105,6 +2004,15 @@
         return null;
     }
 
+    /**
+     * Setup a data connection based on given APN type.
+     *
+     * @param apnContext APN context
+     * @param radioTech RAT of the data connection
+     * @param unmeteredUseOnly True if this data connection should be only used for unmetered
+     *                         purposes only.
+     * @return True if successful, otherwise false.
+     */
     private boolean setupData(ApnContext apnContext, int radioTech, boolean unmeteredUseOnly) {
         if (DBG) log("setupData: apnContext=" + apnContext);
         apnContext.requestLog("setupData");
@@ -2518,12 +2426,12 @@
 
     private void onSetPolicyDataEnabled(boolean enabled) {
         synchronized (mDataEnabledSettings) {
-            final boolean prevEnabled = getAnyDataEnabled();
+            final boolean prevEnabled = isDataEnabled();
             if (mDataEnabledSettings.isPolicyDataEnabled() != enabled) {
                 mDataEnabledSettings.setPolicyDataEnabled(enabled);
                 // TODO: We should register for DataEnabledSetting's data enabled/disabled event and
                 // handle the rest from there.
-                if (prevEnabled != getAnyDataEnabled()) {
+                if (prevEnabled != isDataEnabled()) {
                     if (!prevEnabled) {
                         reevaluateDataConnections();
                         onTrySetupData(Phone.REASON_DATA_ENABLED);
@@ -2550,7 +2458,6 @@
                 DctConstants.State state = apnContext.getState();
                 switch(state) {
                     case CONNECTING:
-                    case SCANNING:
                     case CONNECTED:
                     case DISCONNECTING:
                         // We're "READY" and active so just return
@@ -2560,6 +2467,7 @@
                     case IDLE:
                         // fall through: this is unexpected but if it happens cleanup and try setup
                     case FAILED:
+                    case SCANNING:
                     case RETRYING: {
                         // We're "READY" but not active so disconnect (cleanup = true) and
                         // connect (trySetup = true) to be sure we retry the connection.
@@ -4283,9 +4191,8 @@
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("DcTracker:");
         pw.println(" RADIO_TESTS=" + RADIO_TESTS);
-        pw.println(" isInternalDataEnabled=" + mDataEnabledSettings.isInternalDataEnabled());
-        pw.println(" isUserDataEnabled=" + mDataEnabledSettings.isUserDataEnabled());
-        pw.println(" isPolicyDataEnabled=" + mDataEnabledSettings.isPolicyDataEnabled());
+        pw.println(" mDataEnabledSettings=" + mDataEnabledSettings);
+        pw.println(" isDataAllowed=" + isDataAllowed(null));
         pw.flush();
         pw.println(" mRequestedApnType=" + mRequestedApnType);
         pw.println(" mPhone=" + mPhone.getPhoneName());
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
index c5e0c78..8228cca 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
@@ -491,7 +491,7 @@
     }
 
     @Override
-    public boolean isDataConnectivityPossible() {
+    public boolean isDataAllowed() {
         return false;
     }
 
diff --git a/src/java/com/android/internal/telephony/sip/SipPhoneBase.java b/src/java/com/android/internal/telephony/sip/SipPhoneBase.java
index 966e952..eff731e 100755
--- a/src/java/com/android/internal/telephony/sip/SipPhoneBase.java
+++ b/src/java/com/android/internal/telephony/sip/SipPhoneBase.java
@@ -461,7 +461,7 @@
     }
 
     @Override
-    public boolean isDataConnectivityPossible() {
+    public boolean isDataAllowed() {
         return false;
     }
 
diff --git a/src/java/com/android/internal/telephony/test/SimulatedCommands.java b/src/java/com/android/internal/telephony/test/SimulatedCommands.java
index 0fb50a6..59d999f 100644
--- a/src/java/com/android/internal/telephony/test/SimulatedCommands.java
+++ b/src/java/com/android/internal/telephony/test/SimulatedCommands.java
@@ -151,6 +151,12 @@
         mPin2Code = DEFAULT_SIM_PIN2_CODE;
     }
 
+    public void dispose() {
+        if (mHandlerThread != null) {
+            mHandlerThread.quit();
+        }
+    }
+
     private void log(String str) {
         Rlog.d(LOG_TAG, str);
     }
diff --git a/src/java/com/android/internal/telephony/uicc/IccCardProxy.java b/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
index 2235654..5466402 100644
--- a/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
+++ b/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
@@ -34,6 +34,7 @@
 import android.text.TextUtils;
 
 import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.CommandsInterface.RadioState;
 import com.android.internal.telephony.IccCard;
 import com.android.internal.telephony.IccCardConstants;
 import com.android.internal.telephony.IccCardConstants.State;
@@ -107,7 +108,7 @@
     private UiccCardApplication mUiccApplication = null;
     private IccRecords mIccRecords = null;
     private CdmaSubscriptionSourceManager mCdmaSSM = null;
-    private boolean mRadioOn = false;
+    private RadioState mRadioState = RadioState.RADIO_UNAVAILABLE;
     private boolean mQuietMode = false; // when set to true IccCardProxy will not broadcast
                                         // ACTION_SIM_STATE_CHANGED intents
     private boolean mInitialized = false;
@@ -130,7 +131,6 @@
         ci.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_UNAVAILABLE, null);
 
         resetProperties();
-        setExternalState(State.NOT_READY, false);
     }
 
     public void dispose() {
@@ -211,15 +211,18 @@
     public void handleMessage(Message msg) {
         switch (msg.what) {
             case EVENT_RADIO_OFF_OR_UNAVAILABLE:
-                mRadioOn = false;
-                if (CommandsInterface.RadioState.RADIO_UNAVAILABLE == mCi.getRadioState()) {
-                    setExternalState(State.NOT_READY);
-                }
+                mRadioState = mCi.getRadioState();
+                updateExternalState();
                 break;
             case EVENT_RADIO_ON:
-                mRadioOn = true;
+                mRadioState = RadioState.RADIO_ON;
                 if (!mInitialized) {
                     updateQuietMode();
+                } else {
+                    // updateQuietMode() triggers ICC_CHANGED, which eventually
+                    // calls updateExternalState; thus, we don't need this in the
+                    // above case
+                    updateExternalState();
                 }
                 break;
             case EVENT_ICC_CHANGED:
@@ -326,11 +329,9 @@
     private void updateIccAvailability() {
         synchronized (mLock) {
             UiccCard newCard = mUiccController.getUiccCard(mPhoneId);
-            CardState state = CardState.CARDSTATE_ABSENT;
             UiccCardApplication newApp = null;
             IccRecords newRecords = null;
             if (newCard != null) {
-                state = newCard.getCardState();
                 newApp = newCard.getApplication(mCurrentAppType);
                 if (newApp != null) {
                     newRecords = newApp.getIccRecords();
@@ -338,7 +339,7 @@
             }
 
             if (mIccRecords != newRecords || mUiccApplication != newApp || mUiccCard != newCard) {
-                if (DBG) log("Icc changed. Reregestering.");
+                if (DBG) log("Icc changed. Reregistering.");
                 unregisterUiccCardEvents();
                 mUiccCard = newCard;
                 mUiccApplication = newApp;
@@ -368,15 +369,27 @@
         // mUiccCard could be null at bootup, before valid card states have
         // been received from UiccController.
         if (mUiccCard == null) {
-            setExternalState(State.NOT_READY);
+            setExternalState(State.UNKNOWN);
             return;
         }
 
         if (mUiccCard.getCardState() == CardState.CARDSTATE_ABSENT) {
-            if (mRadioOn) {
-                setExternalState(State.ABSENT);
+            /*
+             * Both IccCardProxy and UiccController are registered for
+             * RadioState changes. When the UiccController receives a radio
+             * state changed to Unknown it will dispose of all of the IccCard
+             * objects, which will then notify the IccCardProxy and the null
+             * object will force the state to unknown. However, because the
+             * IccCardProxy is also registered for RadioState changes, it will
+             * recieve that signal first. By triggering on radio state changes
+             * directly, we reduce the time window during which the modem is
+             * UNAVAILABLE but the IccStatus is reported as something valid.
+             * This is not ideal.
+             */
+            if (mRadioState == RadioState.RADIO_UNAVAILABLE) {
+                setExternalState(State.UNKNOWN);
             } else {
-                setExternalState(State.NOT_READY);
+                setExternalState(State.ABSENT);
             }
             return;
         }
@@ -396,9 +409,20 @@
             return;
         }
 
+        // By process of elimination, the UICC Card State = PRESENT
         switch (mUiccApplication.getState()) {
             case APPSTATE_UNKNOWN:
-                setExternalState(State.UNKNOWN);
+                /*
+                 * APPSTATE_UNKNOWN is a catch-all state reported whenever the app
+                 * is not explicitly in one of the other states. To differentiate the
+                 * case where we know that there is a card present, but the APP is not
+                 * ready, we choose NOT_READY here instead of unknown. This is possible
+                 * in at least two cases:
+                 * 1) A transient during the process of the SIM bringup
+                 * 2) There is no valid App on the SIM to load, which can be the case with an
+                 *    eSIM/soft SIM.
+                 */
+                setExternalState(State.NOT_READY);
                 break;
             case APPSTATE_DETECTED:
                 HandleDetectedState();
@@ -516,15 +540,16 @@
             }
 
             if (!override && newState == mExternalState) {
-                loge("setExternalState: !override and newstate unchanged from " + newState);
+                log("setExternalState: !override and newstate unchanged from " + newState);
                 return;
             }
             mExternalState = newState;
-            loge("setExternalState: set mPhoneId=" + mPhoneId + " mExternalState=" + mExternalState);
+            log("setExternalState: set mPhoneId=" + mPhoneId + " mExternalState=" + mExternalState);
             mTelephonyManager.setSimStateForPhone(mPhoneId, getState().toString());
 
             // For locked states, we should be sending internal broadcast.
-            if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(getIccStateIntentString(mExternalState))) {
+            if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(
+                        getIccStateIntentString(mExternalState))) {
                 broadcastInternalIccStateChangedIntent(getIccStateIntentString(mExternalState),
                         getIccStateReason(mExternalState));
             } else {
@@ -937,7 +962,7 @@
         pw.println(" mUiccApplication=" + mUiccApplication);
         pw.println(" mIccRecords=" + mIccRecords);
         pw.println(" mCdmaSSM=" + mCdmaSSM);
-        pw.println(" mRadioOn=" + mRadioOn);
+        pw.println(" mRadioState=" + mRadioState);
         pw.println(" mQuietMode=" + mQuietMode);
         pw.println(" mInitialized=" + mInitialized);
         pw.println(" mExternalState=" + mExternalState);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CallManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/CallManagerTest.java
index a255f26..8231d06 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CallManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CallManagerTest.java
@@ -122,7 +122,7 @@
     @After
     public void tearDown() throws Exception {
         CallManager.getInstance().unregisterPhone(mPhone);
-        mCallManagerHandlerThread.quitSafely();
+        mCallManagerHandlerThread.quit();
         super.tearDown();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierActionAgentTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierActionAgentTest.java
index c6a0163..711c84e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierActionAgentTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierActionAgentTest.java
@@ -15,6 +15,13 @@
  */
 package com.android.internal.telephony;
 
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Bundle;
@@ -32,19 +39,13 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.verify;
-
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
-
 public class CarrierActionAgentTest extends TelephonyTest {
     private CarrierActionAgent mCarrierActionAgentUT;
     private FakeContentResolver mFakeContentResolver;
     private FakeContentProvider mFakeContentProvider;
     private static int DATA_CARRIER_ACTION_EVENT = 0;
     private static int RADIO_CARRIER_ACTION_EVENT = 1;
+    private CarrierActionAgentHandler mCarrierActionAgentHandler;
     @Mock
     private Handler mDataActionHandler;
     @Mock
@@ -107,7 +108,8 @@
         mFakeContentProvider = new FakeContentProvider();
         mFakeContentResolver.addProvider(Settings.AUTHORITY, mFakeContentProvider);
         doReturn(mFakeContentResolver).when(mContext).getContentResolver();
-        new CarrierActionAgentHandler(getClass().getSimpleName()).start();
+        mCarrierActionAgentHandler = new CarrierActionAgentHandler(getClass().getSimpleName());
+        mCarrierActionAgentHandler.start();
         waitUntilReady();
         logd("CarrierActionAgentTest -Setup!");
     }
@@ -118,7 +120,7 @@
         Settings.Global.putInt(mFakeContentResolver, Settings.Global.AIRPLANE_MODE_ON, 1);
         mFakeContentProvider.simulateChange(
                 Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON));
-        waitForMs(50);
+        waitForMs(200);
         ArgumentCaptor<Message> message = ArgumentCaptor.forClass(Message.class);
 
         verify(mDataActionHandler).sendMessageAtTime(message.capture(), anyLong());
@@ -130,6 +132,8 @@
 
     @After
     public void tearDown() throws Exception {
+        Settings.Global.putInt(mFakeContentResolver, Settings.Global.AIRPLANE_MODE_ON, 0);
+        mCarrierActionAgentHandler.quit();
         super.tearDown();
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/DeviceStateMonitorTest.java b/tests/telephonytests/src/com/android/internal/telephony/DeviceStateMonitorTest.java
index 0745092..5d89d08 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/DeviceStateMonitorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/DeviceStateMonitorTest.java
@@ -71,7 +71,7 @@
     @After
     public void tearDown() throws Exception {
         mDeviceStateMonitor = null;
-        mDeviceStateMonitorTestHandler.quitSafely();
+        mDeviceStateMonitorTestHandler.quit();
         super.tearDown();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
index 077610b..bc43aae 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
@@ -85,7 +85,7 @@
     @After
     public void tearDown() throws Exception {
         mCTUT = null;
-        mGsmCdmaCTHandlerThread.quitSafely();
+        mGsmCdmaCTHandlerThread.quit();
         super.tearDown();
     }
 
@@ -150,6 +150,7 @@
         assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
     }
 
+    @FlakyTest
     @Test
     @MediumTest
     public void testMOCallDialPickUpHangup() {
@@ -282,6 +283,7 @@
 
     }
 
+    @FlakyTest
     @Test
     @MediumTest
     public void testMOCallSwitchHangupForeGround() {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
index 39fdf13..6301d54 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
@@ -132,7 +132,7 @@
     public void tearDown() throws Exception {
         mPhoneUT.removeCallbacksAndMessages(null);
         mPhoneUT = null;
-        mGsmCdmaPhoneTestHandler.quitSafely();
+        mGsmCdmaPhoneTestHandler.quit();
         super.tearDown();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ImsSMSDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/ImsSMSDispatcherTest.java
index 24c1283..be829ef 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ImsSMSDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ImsSMSDispatcherTest.java
@@ -95,7 +95,7 @@
     @After
     public void tearDown() throws Exception {
         mImsSmsDispatcher = null;
-        mImsSmsDispatcherTestHandler.quitSafely();
+        mImsSmsDispatcherTestHandler.quit();
         super.tearDown();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneStateListenerTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneStateListenerTest.java
index 4a17199..fdda80f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneStateListenerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneStateListenerTest.java
@@ -60,7 +60,7 @@
 
     @After
     public void tearDown() throws Exception {
-        mPhoneStateListenerHandler.quitSafely();
+        mPhoneStateListenerHandler.quit();
         super.tearDown();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
index e68262f..b5ec320 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
@@ -388,6 +388,12 @@
 //        }
 //        if (commandsInterfaces[0].isDataAllowed()) fail("data allowed");
 //        if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
+
+        for (int i = 0; i < numPhones; i++) {
+            commandsInterfaces[i].dispose();
+        }
+
+        connectivityServiceMock.die();
         testHandler.die();
         handlerThread.quit();
     }
@@ -468,6 +474,11 @@
         if (commandsInterfaces[0].isDataAllowed()) fail("data allowed");
         if (commandsInterfaces[1].isDataAllowed() == false) fail("data not allowed");
 
+        for (int i = 0; i < numPhones; i++) {
+            commandsInterfaces[i].dispose();
+        }
+
+        connectivityServiceMock.die();
         testHandler.die();
         handlerThread.quit();
     }
@@ -537,6 +548,11 @@
         if (commandsInterfaces[0].isDataAllowed() == false) fail("data not allowed");
         if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
 
+        for (int i = 0; i < numPhones; i++) {
+            commandsInterfaces[i].dispose();
+        }
+
+        connectivityServiceMock.die();
         testHandler.die();
         handlerThread.quit();
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index 3be623c..c8d459a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -26,6 +26,7 @@
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.nullable;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.spy;
@@ -70,6 +71,7 @@
 import org.mockito.Mock;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
 
 public class ServiceStateTrackerTest extends TelephonyTest {
@@ -88,13 +90,15 @@
 
     private static final int EVENT_REGISTERED_TO_NETWORK = 1;
     private static final int EVENT_SUBSCRIPTION_INFO_READY = 2;
-    private static final int EVENT_ROAMING_ON = 3;
-    private static final int EVENT_ROAMING_OFF = 4;
+    private static final int EVENT_DATA_ROAMING_ON = 3;
+    private static final int EVENT_DATA_ROAMING_OFF = 4;
     private static final int EVENT_DATA_CONNECTION_ATTACHED = 5;
     private static final int EVENT_DATA_CONNECTION_DETACHED = 6;
     private static final int EVENT_DATA_RAT_CHANGED = 7;
     private static final int EVENT_PS_RESTRICT_ENABLED = 8;
     private static final int EVENT_PS_RESTRICT_DISABLED = 9;
+    private static final int EVENT_VOICE_ROAMING_ON = 10;
+    private static final int EVENT_VOICE_ROAMING_OFF = 11;
 
     private class ServiceStateTrackerTestHandler extends HandlerThread {
 
@@ -146,7 +150,7 @@
     @After
     public void tearDown() throws Exception {
         sst = null;
-        mSSTTestHandler.quitSafely();
+        mSSTTestHandler.quit();
         super.tearDown();
     }
 
@@ -326,7 +330,6 @@
         assertFalse(sst.isImsRegistered());
     }
 
-    @FlakyTest
     @Test
     @MediumTest
     public void testSignalStrength() {
@@ -360,14 +363,15 @@
         sst.mSS.setRilDataRadioTechnology(ServiceState.RIL_RADIO_TECHNOLOGY_LTE);
 
         mSimulatedCommands.notifySignalStrength();
-        waitForMs(200);
+        waitForMs(300);
         assertEquals(sst.getSignalStrength(), ss);
         assertEquals(sst.getSignalStrength().isGsm(), true);
 
         // notify signal strength again, but this time data RAT is not LTE
+        sst.mSS.setRilVoiceRadioTechnology(ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT);
         sst.mSS.setRilDataRadioTechnology(ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD);
         mSimulatedCommands.notifySignalStrength();
-        waitForMs(200);
+        waitForMs(300);
         assertEquals(sst.getSignalStrength(), ss);
         assertEquals(sst.getSignalStrength().isGsm(), false);
     }
@@ -439,7 +443,7 @@
     @Test
     @MediumTest
     public void testRegAndUnregForVoiceRoamingOn() throws Exception {
-        sst.registerForVoiceRoamingOn(mTestHandler, EVENT_ROAMING_ON, null);
+        sst.registerForVoiceRoamingOn(mTestHandler, EVENT_DATA_ROAMING_ON, null);
 
         // Enable roaming and trigger events to notify handler registered
         doReturn(true).when(mPhone).isPhoneTypeGsm();
@@ -452,7 +456,7 @@
         // verify if registered handler has message posted to it
         ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
         verify(mTestHandler).sendMessageAtTime(messageArgumentCaptor.capture(), anyLong());
-        assertEquals(EVENT_ROAMING_ON, messageArgumentCaptor.getValue().what);
+        assertEquals(EVENT_DATA_ROAMING_ON, messageArgumentCaptor.getValue().what);
 
         // Disable roaming
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_HOME);
@@ -486,7 +490,7 @@
 
         waitForMs(100);
 
-        sst.registerForVoiceRoamingOff(mTestHandler, EVENT_ROAMING_OFF, null);
+        sst.registerForVoiceRoamingOff(mTestHandler, EVENT_DATA_ROAMING_OFF, null);
 
         // Disable roaming
         doReturn(true).when(mPhone).isPhoneTypeGsm();
@@ -499,7 +503,7 @@
         // verify if registered handler has message posted to it
         ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
         verify(mTestHandler).sendMessageAtTime(messageArgumentCaptor.capture(), anyLong());
-        assertEquals(EVENT_ROAMING_OFF, messageArgumentCaptor.getValue().what);
+        assertEquals(EVENT_DATA_ROAMING_OFF, messageArgumentCaptor.getValue().what);
 
         // Enable roaming
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
@@ -525,7 +529,7 @@
     @Test
     @MediumTest
     public void testRegAndUnregForDataRoamingOn() throws Exception {
-        sst.registerForDataRoamingOn(mTestHandler, EVENT_ROAMING_ON, null);
+        sst.registerForDataRoamingOn(mTestHandler, EVENT_DATA_ROAMING_ON, null);
 
         // Enable roaming and trigger events to notify handler registered
         doReturn(true).when(mPhone).isPhoneTypeGsm();
@@ -538,7 +542,7 @@
         // verify if registered handler has message posted to it
         ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
         verify(mTestHandler).sendMessageAtTime(messageArgumentCaptor.capture(), anyLong());
-        assertEquals(EVENT_ROAMING_ON, messageArgumentCaptor.getValue().what);
+        assertEquals(EVENT_DATA_ROAMING_ON, messageArgumentCaptor.getValue().what);
 
         // Disable roaming
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_HOME);
@@ -572,7 +576,7 @@
 
         waitForMs(100);
 
-        sst.registerForDataRoamingOff(mTestHandler, EVENT_ROAMING_OFF, null);
+        sst.registerForDataRoamingOff(mTestHandler, EVENT_DATA_ROAMING_OFF, null);
 
         // Disable roaming
         doReturn(true).when(mPhone).isPhoneTypeGsm();
@@ -585,7 +589,7 @@
         // verify if registered handler has message posted to it
         ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
         verify(mTestHandler).sendMessageAtTime(messageArgumentCaptor.capture(), anyLong());
-        assertEquals(EVENT_ROAMING_OFF, messageArgumentCaptor.getValue().what);
+        assertEquals(EVENT_DATA_ROAMING_OFF, messageArgumentCaptor.getValue().what);
 
         // Enable roaming
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
@@ -978,6 +982,40 @@
     }
 
     @Test
+    @MediumTest
+    public void testRoamingPhoneTypeSwitch() {
+        // Enable roaming
+        doReturn(true).when(mPhone).isPhoneTypeGsm();
+
+        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.notifyNetworkStateChanged();
+
+        waitForMs(200);
+
+        sst.registerForDataRoamingOff(mTestHandler, EVENT_DATA_ROAMING_OFF, null);
+        sst.registerForVoiceRoamingOff(mTestHandler, EVENT_VOICE_ROAMING_OFF, null);
+        sst.registerForDataConnectionDetached(mTestHandler, EVENT_DATA_CONNECTION_DETACHED, null);
+
+        // Call functions which would trigger posting of message on test handler
+        doReturn(false).when(mPhone).isPhoneTypeGsm();
+        sst.updatePhoneType();
+
+        // verify if registered handler has message posted to it
+        ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
+        verify(mTestHandler, atLeast(3)).sendMessageAtTime(
+                messageArgumentCaptor.capture(), anyLong());
+        HashSet<Integer> messageSet = new HashSet<>();
+        for (Message m : messageArgumentCaptor.getAllValues()) {
+            messageSet.add(m.what);
+        }
+
+        assertTrue(messageSet.contains(EVENT_DATA_ROAMING_OFF));
+        assertTrue(messageSet.contains(EVENT_VOICE_ROAMING_OFF));
+        assertTrue(messageSet.contains(EVENT_DATA_CONNECTION_DETACHED));
+    }
+
+    @Test
     @SmallTest
     public void testGetDesiredPowerState() {
         sst.setRadioPower(true);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsStorageMonitorTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsStorageMonitorTest.java
index 7c8c15c..cbc56ae 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SmsStorageMonitorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsStorageMonitorTest.java
@@ -67,7 +67,7 @@
     @After
     public void tearDown() throws Exception {
         mSmsStorageMonitor = null;
-        mSmsStorageMonitorTestHandler.quitSafely();
+        mSmsStorageMonitorTestHandler.quit();
         super.tearDown();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
index 4960e72..ea2f17d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
@@ -128,7 +128,7 @@
 
     @After
     public void tearDown() throws Exception {
-        mSubscriptionInfoUpdaterHandlerThread.quitSafely();
+        mSubscriptionInfoUpdaterHandlerThread.quit();
         super.tearDown();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index 174666e..df4f0ee 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -41,6 +41,7 @@
 import android.os.RegistrantList;
 import android.os.ServiceManager;
 import android.provider.BlockedNumberContract;
+import android.provider.Settings;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -441,11 +442,16 @@
         mSST.mSS = mServiceState;
         mServiceManagerMockedServices.put("connectivity_metrics_logger", mConnMetLoggerBinder);
 
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.ENABLE_CELLULAR_ON_BOOT, 1);
+
         setReady(false);
     }
 
     protected void tearDown() throws Exception {
 
+        mSimulatedCommands.dispose();
+
         SharedPreferences sharedPreferences = mContext.getSharedPreferences((String) null, 0);
         sharedPreferences.edit().clear().commit();
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java
index 82169d4..a26741f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java
@@ -159,7 +159,7 @@
         assertFalse(mCdmaInboundSmsHandler.getWakeLock().isHeld());
         mCdmaInboundSmsHandler = null;
         mContentProvider.shutdown();
-        mCdmaInboundSmsHandlerTestHandler.quitSafely();
+        mCdmaInboundSmsHandlerTestHandler.quit();
         super.tearDown();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsDispatcherTest.java
index e3ba4ff..a308762 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsDispatcherTest.java
@@ -72,7 +72,7 @@
     @After
     public void tearDown() throws Exception {
         mCdmaSmsDispatcher = null;
-        mCdmaSmsDispatcherTestHandler.quitSafely();
+        mCdmaSmsDispatcherTestHandler.quit();
         super.tearDown();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
index 92d8c34..d22fe6f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
@@ -16,15 +16,29 @@
 
 package com.android.internal.telephony.dataconnection;
 
+import static com.android.internal.telephony.PhoneConstants.APN_TYPE_ALL;
+import static com.android.internal.telephony.PhoneConstants.APN_TYPE_DEFAULT;
+import static com.android.internal.telephony.PhoneConstants.APN_TYPE_HIPRI;
+import static com.android.internal.telephony.PhoneConstants.APN_TYPE_IA;
+import static com.android.internal.telephony.PhoneConstants.APN_TYPE_MMS;
+import static com.android.internal.telephony.PhoneConstants.APN_TYPE_SUPL;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.doReturn;
+
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
+import android.telephony.ServiceState;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyTest;
 
 import org.junit.After;
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -33,22 +47,9 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import static com.android.internal.telephony.PhoneConstants.APN_TYPE_ALL;
-import static com.android.internal.telephony.PhoneConstants.APN_TYPE_DEFAULT;
-import static com.android.internal.telephony.PhoneConstants.APN_TYPE_HIPRI;
-import static com.android.internal.telephony.PhoneConstants.APN_TYPE_IA;
-import static com.android.internal.telephony.PhoneConstants.APN_TYPE_MMS;
-import static com.android.internal.telephony.PhoneConstants.APN_TYPE_SUPL;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.fail;
-import static org.junit.Assert.assertEquals;
-
-
 public class ApnSettingTest extends TelephonyTest {
 
     private PersistableBundle mBundle;
-    private boolean isRoaming = false;
 
     @Before
     public void setUp() throws Exception {
@@ -61,15 +62,15 @@
         super.tearDown();
     }
 
-    private ApnSetting createApnSetting(String[] apnTypes) {
+    static ApnSetting createApnSetting(String[] apnTypes) {
         return createApnSettingInternal(apnTypes, true);
     }
 
-    private ApnSetting createDisabledApnSetting(String[] apnTypes) {
+    private static ApnSetting createDisabledApnSetting(String[] apnTypes) {
         return createApnSettingInternal(apnTypes, false);
     }
 
-    private ApnSetting createApnSettingInternal(String[] apnTypes, boolean carrierEnabled) {
+    private static ApnSetting createApnSettingInternal(String[] apnTypes, boolean carrierEnabled) {
         return new ApnSetting(
                 2163,                   // id
                 "44010",                // numeric
@@ -229,63 +230,52 @@
         mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
 
+        doReturn(false).when(mServiceState).getDataRoaming();
+        doReturn(1).when(mPhone).getSubId();
         assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_DEFAULT}).
-                isMetered(mContext, 1, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT}).isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS}).
-                isMetered(mContext, 1, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_MMS}).
-                isMetered(mContext, 1, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_MMS}).isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_MMS, PhoneConstants.APN_TYPE_SUPL}).
-                isMetered(mContext, 1, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_DUN}).
-                isMetered(mContext, 1, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_ALL}).
-                isMetered(mContext, 1, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
 
         assertFalse(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_SUPL}).
-                isMetered(mContext, 1, isRoaming));
+                isMetered(mPhone));
 
         assertFalse(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_CBS}).
-                isMetered(mContext, 1, isRoaming));
+                isMetered(mPhone));
 
-        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT,
-                mContext, 1, isRoaming));
-        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS,
-                mContext, 1, isRoaming));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_SUPL,
-                mContext, 1, isRoaming));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_CBS,
-                mContext, 1, isRoaming));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DUN,
-                mContext, 1, isRoaming));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA,
-                mContext, 1, isRoaming));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_IA,
-                mContext, 1, isRoaming));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_HIPRI,
-                mContext, 1, isRoaming));
+        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_SUPL, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_CBS, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DUN, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_IA, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_HIPRI, mPhone));
 
         // Carrier config settings changes.
         mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT});
 
-        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT,
-                mContext, 1, isRoaming));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS,
-                mContext, 1, isRoaming));
+        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
     }
 
     @Test
@@ -293,50 +283,93 @@
     public void testIsRoamingMetered() throws Exception {
         mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
-        isRoaming = true;
+        doReturn(true).when(mServiceState).getDataRoaming();
+        doReturn(1).when(mPhone).getSubId();
 
         assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_DEFAULT}).
-                isMetered(mContext, 1, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT}).isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS}).
-                isMetered(mContext, 1, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_MMS}).
-                isMetered(mContext, 1, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_MMS}).isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_MMS, PhoneConstants.APN_TYPE_SUPL}).
-                isMetered(mContext, 1, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_DUN}).
-                isMetered(mContext, 1, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_ALL}).
-                isMetered(mContext, 1, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
 
         assertFalse(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_SUPL}).
-                isMetered(mContext, 1, isRoaming));
+                isMetered(mPhone));
 
         assertFalse(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_CBS}).
-                isMetered(mContext, 1, isRoaming));
+                isMetered(mPhone));
 
         // Carrier config settings changes.
         mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
                 new String[]{PhoneConstants.APN_TYPE_FOTA});
 
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT,
-                mContext, 1, isRoaming));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS,
-                mContext, 1, isRoaming));
-        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA,
-                mContext, 1, isRoaming));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
+        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
+    }
+
+    @Test
+    @SmallTest
+    public void testIsIwlanMetered() throws Exception {
+        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_IWLAN_APN_TYPES_STRINGS,
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
+        doReturn(false).when(mServiceState).getDataRoaming();
+        doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN).when(mServiceState)
+                .getRilDataRadioTechnology();
+        doReturn(1).when(mPhone).getSubId();
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT}).isMetered(mPhone));
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS})
+                .isMetered(mPhone));
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_MMS}).isMetered(mPhone));
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_MMS, PhoneConstants.APN_TYPE_SUPL})
+                .isMetered(mPhone));
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_DUN})
+                .isMetered(mPhone));
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+
+        assertFalse(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_SUPL})
+                .isMetered(mPhone));
+
+        assertFalse(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_CBS})
+                .isMetered(mPhone));
+
+        // Carrier config settings changes.
+        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_IWLAN_APN_TYPES_STRINGS,
+                new String[]{PhoneConstants.APN_TYPE_FOTA});
+
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
+        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
     }
 
     @Test
@@ -345,38 +378,35 @@
         mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
                 new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_CBS});
 
+        doReturn(false).when(mServiceState).getDataRoaming();
+        doReturn(1).when(mPhone).getSubId();
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_CBS}).
-                isMetered(mContext, 2, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_SUPL}).
-                isMetered(mContext, 2, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_SUPL}).isMetered(mPhone));
 
         assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_CBS}).
-                isMetered(mContext, 2, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_CBS}).isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS}).
-                isMetered(mContext, 2, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_IA}).
-                isMetered(mContext, 2, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_ALL}).
-                isMetered(mContext, 2, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
 
         assertFalse(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_IMS}).
-                isMetered(mContext, 2, isRoaming));
+                isMetered(mPhone));
 
         assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_IMS}).
-                isMetered(mContext, 2, isRoaming));
-
+                new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
     }
 
     @Test
@@ -384,56 +414,91 @@
     public void testIsRoamingMeteredAnother() throws Exception {
         mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
                 new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_CBS});
-        isRoaming = true;
+        doReturn(true).when(mServiceState).getDataRoaming();
+        doReturn(2).when(mPhone).getSubId();
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_CBS}).
-                isMetered(mContext, 2, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_SUPL}).
-                isMetered(mContext, 2, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_SUPL}).isMetered(mPhone));
 
         assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_CBS}).
-                isMetered(mContext, 2, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_CBS}).isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS}).
-                isMetered(mContext, 2, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_IA}).
-                isMetered(mContext, 2, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_ALL}).
-                isMetered(mContext, 2, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
 
         assertFalse(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_IMS}).
-                isMetered(mContext, 2, isRoaming));
+                isMetered(mPhone));
 
         assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_IMS}).
-                isMetered(mContext, 2, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
 
-        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_SUPL,
-                mContext, 2, isRoaming));
-        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_CBS,
-                mContext, 2, isRoaming));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT,
-                mContext, 2, isRoaming));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS,
-                mContext, 2, isRoaming));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DUN,
-                mContext, 2, isRoaming));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA,
-                mContext, 2, isRoaming));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_IA,
-                mContext, 2, isRoaming));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_HIPRI,
-                mContext, 2, isRoaming));
+        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_SUPL, mPhone));
+        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_CBS, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DUN, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_IA, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_HIPRI, mPhone));
+    }
 
+    @Test
+    @SmallTest
+    public void testIsIwlanMeteredAnother() throws Exception {
+        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_IWLAN_APN_TYPES_STRINGS,
+                new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_CBS});
+        doReturn(true).when(mServiceState).getDataRoaming();
+        doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN).when(mServiceState)
+                .getRilDataRadioTechnology();
+        doReturn(2).when(mPhone).getSubId();
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_CBS})
+                .isMetered(mPhone));
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_SUPL}).isMetered(mPhone));
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_CBS}).isMetered(mPhone));
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS})
+                .isMetered(mPhone));
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_IA})
+                .isMetered(mPhone));
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+
+        assertFalse(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_IMS})
+                .isMetered(mPhone));
+
+        assertFalse(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
+
+        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_SUPL, mPhone));
+        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_CBS, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DUN, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_IA, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_HIPRI, mPhone));
     }
 
     @Test
@@ -442,21 +507,22 @@
         mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
                 new String[]{});
 
-        assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_IMS}).
-                isMetered(mContext, 3, isRoaming));
+        doReturn(false).when(mServiceState).getDataRoaming();
+        doReturn(3).when(mPhone).getSubId();
 
         assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_IMS, PhoneConstants.APN_TYPE_MMS}).
-                isMetered(mContext, 3, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
 
         assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_FOTA}).
-                isMetered(mContext, 3, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_IMS, PhoneConstants.APN_TYPE_MMS})
+                .isMetered(mPhone));
 
         assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_ALL}).
-                isMetered(mContext, 3, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_FOTA})
+                .isMetered(mPhone));
+
+        assertFalse(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
     }
 
     @Test
@@ -464,23 +530,48 @@
     public void testIsRoamingMeteredNothingCharged() throws Exception {
         mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
                 new String[]{});
-        isRoaming = true;
+        doReturn(true).when(mServiceState).getDataRoaming();
+        doReturn(3).when(mPhone).getSubId();
 
         assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_IMS}).
-                isMetered(mContext, 3, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
 
         assertFalse(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_IMS, PhoneConstants.APN_TYPE_MMS}).
-                isMetered(mContext, 3, isRoaming));
+                isMetered(mPhone));
 
         assertFalse(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_FOTA}).
-                isMetered(mContext, 3, isRoaming));
+                isMetered(mPhone));
 
         assertFalse(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_ALL}).
-                isMetered(mContext, 3, isRoaming));
+                isMetered(mPhone));
+    }
+
+    @Test
+    @SmallTest
+    public void testIsIwlanMeteredNothingCharged() throws Exception {
+        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_IWLAN_APN_TYPES_STRINGS,
+                new String[]{});
+        doReturn(true).when(mServiceState).getDataRoaming();
+        doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN).when(mServiceState)
+                .getRilDataRadioTechnology();
+        doReturn(3).when(mPhone).getSubId();
+
+        assertFalse(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
+
+        assertFalse(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_IMS, PhoneConstants.APN_TYPE_MMS})
+                .isMetered(mPhone));
+
+        assertFalse(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_FOTA})
+                .isMetered(mPhone));
+
+        assertFalse(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
     }
 
     @Test
@@ -489,21 +580,24 @@
         mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
                 new String[]{PhoneConstants.APN_TYPE_ALL});
 
+        doReturn(false).when(mServiceState).getDataRoaming();
+        doReturn(4).when(mPhone).getSubId();
+
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_ALL}).
-                isMetered(mContext, 4, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS}).
-                isMetered(mContext, 4, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS}).
-                isMetered(mContext, 4, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_DUN}).
-                isMetered(mContext, 4, isRoaming));
+                isMetered(mPhone));
 
     }
 
@@ -512,23 +606,51 @@
     public void testIsRoamingMeteredNothingFree() throws Exception {
         mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
                 new String[]{PhoneConstants.APN_TYPE_ALL});
-        isRoaming = true;
+
+        doReturn(true).when(mServiceState).getDataRoaming();
+        doReturn(4).when(mPhone).getSubId();
 
         assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_ALL}).
-                isMetered(mContext, 4, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS})
+                .isMetered(mPhone));
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS})
+                .isMetered(mPhone));
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_DUN})
+                .isMetered(mPhone));
+    }
+
+    @Test
+    @SmallTest
+    public void testIsIwlanMeteredNothingFree() throws Exception {
+        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_IWLAN_APN_TYPES_STRINGS,
+                new String[]{PhoneConstants.APN_TYPE_ALL});
+
+        doReturn(false).when(mServiceState).getDataRoaming();
+        doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN).when(mServiceState)
+                .getRilDataRadioTechnology();
+        doReturn(4).when(mPhone).getSubId();
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS}).
-                isMetered(mContext, 4, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS}).
-                isMetered(mContext, 4, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_DUN}).
-                isMetered(mContext, 4, isRoaming));
+                isMetered(mPhone));
     }
 
     @Test
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 21ba173..1e34910 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
@@ -164,7 +164,7 @@
         logd("tearDown");
         mDc = null;
         mDcc = null;
-        mDataConnectionTestHandler.quitSafely();
+        mDataConnectionTestHandler.quit();
         super.tearDown();
     }
 
@@ -278,8 +278,8 @@
         return (NetworkInfo) f.get(mDc);
     }
 
-    private NetworkCapabilities getCopyNetworkCapabilities() throws Exception {
-        Method method = DataConnection.class.getDeclaredMethod("getCopyNetworkCapabilities");
+    private NetworkCapabilities getNetworkCapabilities() throws Exception {
+        Method method = DataConnection.class.getDeclaredMethod("getNetworkCapabilities");
         method.setAccessible(true);
         return (NetworkCapabilities) method.invoke(mDc);
     }
@@ -294,8 +294,8 @@
 
         testConnectEvent();
 
-        assertFalse(getCopyNetworkCapabilities().
-                hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED));
+        assertFalse(getNetworkCapabilities()
+                .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED));
         assertTrue(getNetworkInfo().isMetered());
     }
 
@@ -310,8 +310,8 @@
 
         testConnectEvent();
 
-        assertTrue(getCopyNetworkCapabilities().
-                hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED));
+        assertTrue(getNetworkCapabilities()
+                .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED));
         assertFalse(getNetworkInfo().isMetered());
     }
 }
\ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java
index f2661f6..d209639 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java
@@ -114,7 +114,7 @@
 
     @After
     public void tearDown() throws Exception {
-        mDcControllerTestHandler.quitSafely();
+        mDcControllerTestHandler.quit();
         super.tearDown();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
index c35e498..3a8cf61 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
@@ -17,6 +17,7 @@
 package com.android.internal.telephony.dataconnection;
 
 import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+import static com.android.internal.telephony.dataconnection.ApnSettingTest.createApnSetting;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -61,6 +62,7 @@
 import android.test.mock.MockContentProvider;
 import android.test.mock.MockContentResolver;
 import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
 import android.util.LocalLog;
 
 import com.android.internal.telephony.DctConstants;
@@ -69,7 +71,6 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.TestApplication;
-import com.android.internal.telephony.dataconnection.DcTracker.DataAllowFailReason;
 
 import org.junit.After;
 import org.junit.Before;
@@ -114,6 +115,12 @@
     NetworkRequest mNetworkRequest;
     @Mock
     SubscriptionInfo mSubscriptionInfo;
+    @Mock
+    ApnContext mApnContext;
+    @Mock
+    ApnSetting mApnSetting;
+    @Mock
+    DcAsyncChannel mDcac;
 
     private DcTracker mDct;
     private DcTrackerTestHandler mDcTrackerTestHandler;
@@ -338,6 +345,8 @@
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
         mBundle = mContextFixture.getCarrierConfigBundle();
 
+        mSimulatedCommands.setDataCallResponse(true, createDataCallResponse());
+
         mDcTrackerTestHandler = new DcTrackerTestHandler(getClass().getSimpleName());
         mDcTrackerTestHandler.start();
         waitUntilReady();
@@ -350,7 +359,7 @@
         logd("DcTrackerTest -tearDown");
         mDct.removeCallbacksAndMessages(null);
         mDct = null;
-        mDcTrackerTestHandler.quitSafely();
+        mDcTrackerTestHandler.quit();
         super.tearDown();
     }
 
@@ -392,7 +401,6 @@
 
         assertEquals(apnSetting, mDct.getActiveApnString(PhoneConstants.APN_TYPE_DEFAULT));
         assertArrayEquals(new String[]{PhoneConstants.APN_TYPE_DEFAULT}, mDct.getActiveApnTypes());
-        assertTrue(mDct.getAnyDataEnabled());
 
         assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
         assertEquals(DctConstants.State.CONNECTED, mDct.getState(PhoneConstants.APN_TYPE_DEFAULT));
@@ -406,12 +414,12 @@
         assertEquals(FAKE_GATEWAY, linkProperties.getRoutes().get(0).getGateway().getHostAddress());
     }
 
-    private boolean isDataAllowed(DataAllowFailReason dataAllowFailReasons) {
+    private boolean isDataAllowed(DataConnectionReasons dataConnectionReasons) {
         try {
             Method method = DcTracker.class.getDeclaredMethod("isDataAllowed",
-                    DataAllowFailReason.class);
+                    DataConnectionReasons.class);
             method.setAccessible(true);
-            return (boolean) method.invoke(mDct, dataAllowFailReasons);
+            return (boolean) method.invoke(mDct, dataConnectionReasons);
         } catch (Exception e) {
             fail(e.toString());
             return false;
@@ -427,9 +435,9 @@
 
         mSimulatedCommands.setDataCallResponse(true, createDataCallResponse());
 
-        DataAllowFailReason failureReason = new DataAllowFailReason();
-        boolean allowed = isDataAllowed(failureReason);
-        assertFalse(failureReason.getDataAllowFailReason(), allowed);
+        DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
+        boolean allowed = isDataAllowed(dataConnectionReasons);
+        assertFalse(dataConnectionReasons.toString(), allowed);
 
         logd("Sending EVENT_RECORDS_LOADED");
         mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
@@ -465,9 +473,9 @@
         mDct.setEnabled(0, true);
         waitForMs(200);
 
-        failureReason.clearAllReasons();
-        allowed = isDataAllowed(failureReason);
-        assertTrue(failureReason.getDataAllowFailReason(), allowed);
+        dataConnectionReasons = new DataConnectionReasons();
+        allowed = isDataAllowed(dataConnectionReasons);
+        assertTrue(dataConnectionReasons.toString(), allowed);
 
         ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
         // Verify if RIL command was sent properly.
@@ -492,9 +500,9 @@
         // Simulate RIL fails the data call setup
         mSimulatedCommands.setDataCallResponse(false, dcResponse);
 
-        DataAllowFailReason failureReason = new DataAllowFailReason();
-        boolean allowed = isDataAllowed(failureReason);
-        assertFalse(failureReason.getDataAllowFailReason(), allowed);
+        DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
+        boolean allowed = isDataAllowed(dataConnectionReasons);
+        assertFalse(dataConnectionReasons.toString(), allowed);
 
         logd("Sending EVENT_RECORDS_LOADED");
         mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
@@ -531,9 +539,9 @@
         waitForMs(200);
 
 
-        failureReason.clearAllReasons();
-        allowed = isDataAllowed(failureReason);
-        assertTrue(failureReason.getDataAllowFailReason(), allowed);
+        dataConnectionReasons = new DataConnectionReasons();
+        allowed = isDataAllowed(dataConnectionReasons);
+        assertTrue(dataConnectionReasons.toString(), allowed);
 
         ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
         // Verify if RIL command was sent properly.
@@ -672,7 +680,6 @@
         waitForMs(200);
     }
 
-    @FlakyTest
     @Test
     @MediumTest
     public void testDataCallOnUserDisableRoaming() throws Exception {
@@ -682,13 +689,16 @@
 
         boolean roamingEnabled = mDct.getDataRoamingEnabled();
         boolean dataEnabled = mDct.getDataEnabled();
+        doReturn(true).when(mServiceState).getDataRoaming();
 
         //set Default and MMS to be metered in the CarrierConfigManager
         mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
         mDct.setEnabled(5, true);
         mDct.setEnabled(0, true);
-        doReturn(true).when(mServiceState).getDataRoaming();
+
+        logd("Sending DATA_ENABLED_CMD");
+        mDct.setDataEnabled(true);
 
         logd("Sending DISABLE_ROAMING_CMD");
         mDct.setDataRoamingEnabled(false);
@@ -701,9 +711,6 @@
         mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null));
         waitForMs(200);
 
-        logd("Sending DATA_ENABLED_CMD");
-        mDct.setDataEnabled(true);
-
         waitForMs(200);
         verify(mSimulatedCommandsVerifier, times(1)).setInitialAttachApn(any(DataProfile.class),
                 eq(true), nullable(Message.class));
@@ -731,9 +738,9 @@
 
         mSimulatedCommands.setDataCallResponse(true, createDataCallResponse());
 
-        DataAllowFailReason failureReason = new DataAllowFailReason();
-        boolean allowed = isDataAllowed(failureReason);
-        assertFalse(failureReason.getDataAllowFailReason(), allowed);
+        DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
+        boolean allowed = isDataAllowed(dataConnectionReasons);
+        assertFalse(dataConnectionReasons.toString(), allowed);
 
         ArgumentCaptor<Integer> intArgumentCaptor = ArgumentCaptor.forClass(Integer.class);
         verify(mUiccController, times(1)).registerForIccChanged(eq(mDct),
@@ -774,9 +781,9 @@
         waitForMs(200);
 
         // Data should not be allowed since auto attach flag has been reset.
-        failureReason.clearAllReasons();
-        allowed = isDataAllowed(failureReason);
-        assertFalse(failureReason.getDataAllowFailReason(), allowed);
+        dataConnectionReasons = new DataConnectionReasons();
+        allowed = isDataAllowed(dataConnectionReasons);
+        assertFalse(dataConnectionReasons.toString(), allowed);
     }
 
     // Test for API carrierActionSetMeteredApnsEnabled.
@@ -828,4 +835,187 @@
         mDct.setDataEnabled(dataEnabled);
         waitForMs(200);
     }
+
+    private void initApns(String targetApn, String[] canHandleTypes) {
+        doReturn(targetApn).when(mApnContext).getApnType();
+        doReturn(true).when(mApnContext).isConnectable();
+        ApnSetting apnSetting = createApnSetting(canHandleTypes);
+        doReturn(apnSetting).when(mApnContext).getNextApnSetting();
+        doReturn(apnSetting).when(mApnContext).getApnSetting();
+        doReturn(mDcac).when(mApnContext).getDcAc();
+        doReturn(true).when(mApnContext).isEnabled();
+        doReturn(true).when(mApnContext).getDependencyMet();
+        doReturn(true).when(mApnContext).isReady();
+        doReturn(true).when(mApnContext).hasNoRestrictedRequests(eq(true));
+    }
+
+    // Test the emergency APN setup.
+    @Test
+    @SmallTest
+    public void testTrySetupDataEmergencyApn() throws Exception {
+        initApns(PhoneConstants.APN_TYPE_EMERGENCY, new String[]{PhoneConstants.APN_TYPE_ALL});
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
+        waitForMs(200);
+
+        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
+                eq(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS), any(DataProfile.class),
+                eq(false), eq(false), any(Message.class));
+    }
+
+    // Test the unmetered APN setup when data is disabled.
+    @Test
+    @SmallTest
+    public void testTrySetupDataUnmeteredDataDisabled() throws Exception {
+        initApns(PhoneConstants.APN_TYPE_FOTA, new String[]{PhoneConstants.APN_TYPE_ALL});
+        mDct.setDataEnabled(false);
+
+        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+
+        logd("Sending EVENT_RECORDS_LOADED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
+        waitForMs(200);
+
+        logd("Sending EVENT_DATA_CONNECTION_ATTACHED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null));
+        waitForMs(200);
+
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
+        waitForMs(200);
+
+        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
+                eq(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS), any(DataProfile.class),
+                eq(false), eq(false), any(Message.class));
+    }
+
+    // Test the metered APN setup when data is disabled.
+    @Test
+    @SmallTest
+    public void testTrySetupMeteredDataDisabled() throws Exception {
+        initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
+        mDct.setDataEnabled(false);
+
+        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+
+        logd("Sending EVENT_RECORDS_LOADED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
+        waitForMs(200);
+
+        logd("Sending EVENT_DATA_CONNECTION_ATTACHED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null));
+        waitForMs(200);
+
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
+        waitForMs(200);
+
+        verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(
+                anyInt(), any(DataProfile.class), eq(false), eq(false), any(Message.class));
+    }
+
+    // Test the restricted data request when data is disabled.
+    @Test
+    @SmallTest
+    public void testTrySetupRestrictedDataDisabled() throws Exception {
+        initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
+        doReturn(false).when(mApnContext).hasNoRestrictedRequests(eq(true));
+
+        mDct.setDataEnabled(false);
+
+        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+
+        logd("Sending EVENT_RECORDS_LOADED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
+        waitForMs(200);
+
+        logd("Sending EVENT_DATA_CONNECTION_ATTACHED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null));
+        waitForMs(200);
+
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
+        waitForMs(200);
+
+        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
+                anyInt(), any(DataProfile.class), eq(false), eq(false), any(Message.class));
+    }
+
+    // Test the default data when data is not connectable.
+    @Test
+    @SmallTest
+    public void testTrySetupNotConnectable() throws Exception {
+        initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
+        doReturn(false).when(mApnContext).isConnectable();
+        mDct.setDataEnabled(true);
+
+        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+
+        logd("Sending EVENT_RECORDS_LOADED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
+        waitForMs(200);
+
+        logd("Sending EVENT_DATA_CONNECTION_ATTACHED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null));
+        waitForMs(200);
+
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
+        waitForMs(200);
+
+        verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(
+                anyInt(), any(DataProfile.class), eq(false), eq(false), any(Message.class));
+    }
+
+    // Test the default data on IWLAN.
+    @Test
+    @SmallTest
+    public void testTrySetupDefaultOnIWLAN() throws Exception {
+        initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
+        doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN).when(mServiceState)
+                .getRilDataRadioTechnology();
+        mDct.setDataEnabled(true);
+
+        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+
+        logd("Sending EVENT_RECORDS_LOADED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
+        waitForMs(200);
+
+        logd("Sending EVENT_DATA_CONNECTION_ATTACHED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null));
+        waitForMs(200);
+
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
+        waitForMs(200);
+
+        verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(
+                anyInt(), any(DataProfile.class), eq(false), eq(false), any(Message.class));
+    }
+
+    // Test the default data when the phone is in ECBM.
+    @Test
+    @SmallTest
+    public void testTrySetupDefaultInECBM() throws Exception {
+        initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
+        doReturn(true).when(mPhone).isInEcm();
+        mDct.setDataEnabled(true);
+
+        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+
+        logd("Sending EVENT_RECORDS_LOADED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
+        waitForMs(200);
+
+        logd("Sending EVENT_DATA_CONNECTION_ATTACHED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null));
+        waitForMs(200);
+
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
+        waitForMs(200);
+
+        verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(
+                anyInt(), any(DataProfile.class), eq(false), eq(false), any(Message.class));
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java
index e377600..a60b502 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java
@@ -98,6 +98,7 @@
         }
 
         void die() {
+            connectivityServiceMock.die();
             looper.quit();
             handlerThread.quit();
         }
@@ -139,7 +140,7 @@
 
         TestSetup ts = new TestSetup(numberOfPhones);
 
-        TelephonyNetworkFactory tnf = makeTnf(phoneId, ts);
+        makeTnf(phoneId, ts);
 
         ts.subscriptionControllerMock.setDefaultDataSubId(subId);
         ts.subscriptionControllerMock.setSlotSubId(phoneId, subId);
@@ -234,7 +235,7 @@
 
         TestSetup ts = new TestSetup(numberOfPhones);
 
-        TelephonyNetworkFactory tnf = makeTnf(phoneId, ts);
+        makeTnf(phoneId, ts);
 
         ts.subscriptionControllerMock.setDefaultDataSubId(subId);
         ts.subscriptionControllerMock.setSlotSubId(phoneId, subId);
@@ -272,7 +273,7 @@
             fail("test 5, LiveRequests != 0");
         }
 
-        NetworkRequest subSpecificMms = makeSubSpecificMmsRequest(ts, subId);
+        makeSubSpecificMmsRequest(ts, subId);
         waitABit();
         if (ts.dcTrackerMock.getNumberOfLiveRequests() != 1) {
             fail("test 6,  LiveRequests != 1");
@@ -285,7 +286,7 @@
             fail("test 7,  LiveRequests != 0");
         }
 
-        NetworkRequest subSpecificDefault = makeSubSpecificDefaultRequest(ts, subId);
+        makeSubSpecificDefaultRequest(ts, subId);
         waitABit();
         if (ts.dcTrackerMock.getNumberOfLiveRequests() != 0) {
             fail("test 8, LiveRequests != 0");
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmCellBroadcastHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmCellBroadcastHandlerTest.java
index 20cefb9..2575cac 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmCellBroadcastHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmCellBroadcastHandlerTest.java
@@ -95,7 +95,7 @@
     @After
     public void tearDown() throws Exception {
         mGsmCellBroadcastHandler = null;
-        mGsmCellBroadcastHandlerTestHandler.quitSafely();
+        mGsmCellBroadcastHandlerTestHandler.quit();
         super.tearDown();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
index 340ff35..92f2bf6 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
@@ -187,7 +187,7 @@
         assertFalse(mGsmInboundSmsHandler.getWakeLock().isHeld());
         mGsmInboundSmsHandler = null;
         mContentProvider.shutdown();
-        mGsmInboundSmsHandlerTestHandler.quitSafely();
+        mGsmInboundSmsHandlerTestHandler.quit();
         super.tearDown();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
index fc06962..6da7ca9 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
@@ -91,7 +91,7 @@
     @After
     public void tearDown() throws Exception {
         mGsmSmsDispatcher = null;
-        mGsmSmsDispatcherTestHandler.quitSafely();
+        mGsmSmsDispatcherTestHandler.quit();
         super.tearDown();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
index 923cd05..db1dcf1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
@@ -15,6 +15,25 @@
  */
 package com.android.internal.telephony.imsphone;
 
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.isNull;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
@@ -26,7 +45,6 @@
 import android.telephony.PhoneNumberUtils;
 import android.telephony.ims.feature.ImsFeature;
 import android.test.suitebuilder.annotation.SmallTest;
-import android.test.suitebuilder.annotation.LargeTest;
 
 import com.android.ims.ImsCall;
 import com.android.ims.ImsCallProfile;
@@ -38,7 +56,6 @@
 import com.android.ims.ImsServiceClass;
 import com.android.ims.internal.ImsCallSession;
 import com.android.internal.telephony.Call;
-import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyTest;
@@ -46,33 +63,12 @@
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.mockito.Mock;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.isNull;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
 public class ImsPhoneCallTrackerTest extends TelephonyTest {
     private ImsPhoneCallTracker mCTUT;
     private ImsCTHandlerThread mImsCTHandlerThread;
@@ -211,7 +207,7 @@
     @After
     public void tearDown() throws Exception {
         mCTUT = null;
-        mImsCTHandlerThread.quitSafely();
+        mImsCTHandlerThread.quit();
         super.tearDown();
     }
 
@@ -370,6 +366,8 @@
         assertEquals(Call.State.ALERTING, mCTUT.mForegroundCall.getState());
     }
 
+    @FlakyTest
+    @Ignore
     @Test
     @SmallTest
     public void testImsMTActiveMODial() {
@@ -476,6 +474,8 @@
                 nullable(ImsConnectionStateListener.class));
     }
 
+    @FlakyTest
+    @Ignore
     @Test
     @SmallTest
     public void testTTYImsServiceUnavailable() throws ImsException {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneFactoryTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneFactoryTest.java
index 8a7c53a..c31541e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneFactoryTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneFactoryTest.java
@@ -60,7 +60,7 @@
 
     @After
     public void tearDown() throws Exception {
-        mImsPhoneFactoryHandler.quitSafely();
+        mImsPhoneFactoryHandler.quit();
         super.tearDown();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
index de0c8ce..fc3c296 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
@@ -141,7 +141,7 @@
     @After
     public void tearDown() throws Exception {
         mImsPhoneUT = null;
-        mImsPhoneTestHandler.quitSafely();
+        mImsPhoneTestHandler.quit();
         super.tearDown();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/ConnectivityServiceMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/ConnectivityServiceMock.java
index acab717..43763ec 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/ConnectivityServiceMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/ConnectivityServiceMock.java
@@ -127,6 +127,9 @@
 
     public void die() {
         // clean up threads/handlers
+        if (mHandlerThread != null) {
+            mHandlerThread.quit();
+        }
     }
 
     private class InternalHandler extends Handler {
@@ -518,6 +521,10 @@
         throw new RuntimeException("not implemented");
     }
 
+    public void startCaptivePortalApp(Network network) {
+        throw new RuntimeException("not implemented");
+    }
+
     public int getMultipathPreference(Network network) {
         throw new RuntimeException("not implemented");
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/DcTrackerMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/DcTrackerMock.java
index 11b1a81..6d0e327 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/DcTrackerMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/DcTrackerMock.java
@@ -61,10 +61,6 @@
         throw new RuntimeException("Not Implemented");
     }
     @Override
-    public boolean isDataPossible(String apnType) {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
     public LinkProperties getLinkProperties(String apnType) {
         throw new RuntimeException("Not Implemented");
     }
@@ -89,10 +85,6 @@
         throw new RuntimeException("Not Implemented");
     }
     @Override
-    public boolean getAnyDataEnabled() {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
     public boolean hasMatchedTetherApnSetting() {
         throw new RuntimeException("Not Implemented");
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java
index cfba328..6cecec7 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java
@@ -801,11 +801,7 @@
         throw new RuntimeException("not implemented");
     }
 
-    public boolean isDataConnectivityPossible() {
-        throw new RuntimeException("not implemented");
-    }
-
-    public boolean isDataConnectivityPossible(String apnType) {
+    public boolean isDataAllowed() {
         throw new RuntimeException("not implemented");
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/IccCardProxyTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccCardProxyTest.java
new file mode 100644
index 0000000..b211218
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccCardProxyTest.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2017 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.internal.telephony.uicc;
+
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.IccCardConstants.State;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
+import com.android.internal.telephony.uicc.IccCardStatus.CardState;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+public class IccCardProxyTest extends TelephonyTest {
+    private IccCardProxy mIccCardProxyUT;
+    // private UiccCard mUiccCard;
+    private IccCardProxyHandlerThread mIccCardProxyHandlerThread;
+    private static final int PHONE_ID = 0;
+    private static final int PHONE_COUNT = 1;
+
+    private static final int SCARY_SLEEP_MS = 200;
+    // Must match IccCardProxy.EVENT_ICC_CHANGED
+    private static final int EVENT_ICC_CHANGED = 3;
+
+    @Mock private Handler mMockedHandler;
+    @Mock private IccCardStatus mIccCardStatus;
+    @Mock private UiccCard mUiccCard;
+    @Mock private UiccCardApplication mUiccCardApplication;
+
+    private class IccCardProxyHandlerThread extends HandlerThread {
+
+        private IccCardProxyHandlerThread(String name) {
+            super(name);
+        }
+
+        @Override
+        public void onLooperPrepared() {
+            /* create a new UICC Controller associated with the simulated Commands */
+            mIccCardProxyUT = new IccCardProxy(mContext, mSimulatedCommands, PHONE_ID);
+            setReady(true);
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp(this.getClass().getSimpleName());
+        doReturn(PHONE_COUNT).when(mTelephonyManager).getPhoneCount();
+        doReturn(PHONE_COUNT).when(mTelephonyManager).getSimCount();
+        mSimulatedCommands.setIccCardStatus(mIccCardStatus);
+        mIccCardProxyHandlerThread = new IccCardProxyHandlerThread(TAG);
+        mIccCardProxyHandlerThread.start();
+        waitUntilReady();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mIccCardProxyHandlerThread.quitSafely();
+        super.tearDown();
+    }
+
+    @Test
+    @SmallTest
+    public void testInitialCardState() {
+        assertEquals(mIccCardProxyUT.getState(), State.UNKNOWN);
+    }
+
+    @Test
+    @SmallTest
+    public void testPowerOn() {
+        mSimulatedCommands.setRadioPower(true, null);
+        mSimulatedCommands.notifyRadioOn();
+        when(mUiccController.getUiccCard(anyInt())).thenReturn(mUiccCard);
+        mIccCardProxyUT.sendMessage(mIccCardProxyUT.obtainMessage(EVENT_ICC_CHANGED));
+        waitForMs(SCARY_SLEEP_MS);
+        assertEquals(CommandsInterface.RadioState.RADIO_ON, mSimulatedCommands.getRadioState());
+        assertEquals(mIccCardProxyUT.getState(), State.NOT_READY);
+        logd("IccCardProxy state = " + mIccCardProxyUT.getState());
+    }
+
+    @Test
+    @SmallTest
+    public void testCardLoaded() {
+        testPowerOn();
+        when(mUiccCard.getCardState()).thenReturn(CardState.CARDSTATE_PRESENT);
+        mIccCardProxyUT.sendMessage(mIccCardProxyUT.obtainMessage(EVENT_ICC_CHANGED));
+        waitForMs(SCARY_SLEEP_MS);
+        assertEquals(mIccCardProxyUT.getState(), State.NOT_READY);
+    }
+
+    @Test
+    @SmallTest
+    public void testAppNotLoaded() {
+        testPowerOn();
+        when(mUiccCard.getCardState()).thenReturn(CardState.CARDSTATE_PRESENT);
+        mIccCardProxyUT.sendMessage(mIccCardProxyUT.obtainMessage(EVENT_ICC_CHANGED));
+        when(mUiccCardApplication.getState()).thenReturn(AppState.APPSTATE_UNKNOWN);
+        when(mUiccCard.getApplication(anyInt())).thenReturn(mUiccCardApplication);
+
+        waitForMs(SCARY_SLEEP_MS);
+        assertEquals(mIccCardProxyUT.getState(), State.NOT_READY);
+    }
+
+    @Test
+    @SmallTest
+    public void testAppReady() {
+        testPowerOn();
+        when(mUiccCard.getCardState()).thenReturn(CardState.CARDSTATE_PRESENT);
+        mIccCardProxyUT.sendMessage(mIccCardProxyUT.obtainMessage(EVENT_ICC_CHANGED));
+        when(mUiccCardApplication.getState()).thenReturn(AppState.APPSTATE_READY);
+        when(mUiccCard.getApplication(anyInt())).thenReturn(mUiccCardApplication);
+
+        waitForMs(SCARY_SLEEP_MS);
+        assertEquals(mIccCardProxyUT.getState(), State.READY);
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/IccPhoneBookInterfaceManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccPhoneBookInterfaceManagerTest.java
index c99bd75..9510619 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/IccPhoneBookInterfaceManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccPhoneBookInterfaceManagerTest.java
@@ -89,7 +89,7 @@
 
     @After
     public void tearDown() throws Exception {
-        mIccPhoneBookInterfaceManagerHandler.quitSafely();
+        mIccPhoneBookInterfaceManagerHandler.quit();
         super.tearDown();
     }
     @Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardApplicationTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardApplicationTest.java
index 1a5daeb..60f5f6f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardApplicationTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardApplicationTest.java
@@ -110,7 +110,7 @@
 
     @After
     public void tearDown() throws Exception {
-        mTestHandlerThread.quitSafely();
+        mTestHandlerThread.quit();
         super.tearDown();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java
index 9a24a86..e090c25 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java
@@ -134,7 +134,7 @@
 
     @After
     public void tearDown() throws Exception {
-        mTestHandlerThread.quitSafely();
+        mTestHandlerThread.quit();
         super.tearDown();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
index 12c2690..e62700a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
@@ -96,7 +96,7 @@
 
     @After
     public void tearDown() throws Exception {
-        mUiccControllerHandlerThread.quitSafely();
+        mUiccControllerHandlerThread.quit();
         super.tearDown();
     }