Merge "Mms: Add the retrieve conf type support in MMS PDU composer."
diff --git a/proto/src/telephony.proto b/proto/src/telephony.proto
index b9243f7..215d14e 100644
--- a/proto/src/telephony.proto
+++ b/proto/src/telephony.proto
@@ -1199,6 +1199,9 @@
 
       // Notification about received SMS
       SMS_RECEIVED = 8;
+
+      // CB message received
+      CB_SMS_RECEIVED = 9;
     }
 
     // Formats used to encode SMS messages
@@ -1224,6 +1227,51 @@
       SMS_IMS = 3;
     }
 
+    message CBMessage {
+      // CB message format
+      optional Format msgFormat = 1;
+
+      // CB message priority
+      optional CBPriority msgPriority = 2;
+
+      // Type of CB msg
+      optional CBMessageType msgType = 3;
+
+      // Service category of CB message
+      optional int32 serviceCategory = 4;
+    }
+
+    enum CBMessageType {
+      // Unknown type
+      TYPE_UNKNOWN = 0;
+
+      // ETWS CB msg
+      ETWS = 1;
+
+      // CMAS CB msg
+      CMAS = 2;
+
+      // CB msg other than ETWS and CMAS
+      OTHER = 3;
+    }
+
+    enum CBPriority {
+      // Unknown priority
+      PRIORITY_UNKNOWN = 0;
+
+      // NORMAL priority
+      NORMAL = 1;
+
+      // Interactive priority
+      INTERACTIVE = 2;
+
+      // Urgent priority
+      URGENT = 3;
+
+      // Emergency priority
+      EMERGENCY = 4;
+    }
+
     // Event type
     optional Type type = 1;
 
@@ -1261,6 +1309,9 @@
 
     // Numeric ID
     optional int32 ril_request_id = 12;
+
+    // Cellbroadcast message content
+    optional CBMessage cell_broadcast_message = 13;
   }
 
   // Time when session has started, in minutes since epoch,
diff --git a/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
index 5b53bcf..1513a33 100644
--- a/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
+++ b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
@@ -522,6 +522,7 @@
             DownloadManager.Request request = new DownloadManager.Request(Uri.parse(mURL));
             request.setAllowedOverMetered(false);
             request.setVisibleInDownloadsUi(false);
+            request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
             Long carrierKeyDownloadRequestId = mDownloadManager.enqueue(request);
             SharedPreferences.Editor editor = getDefaultSharedPreferences(mContext).edit();
 
diff --git a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
index 59fdd0f..2e9170d 100644
--- a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.database.ContentObserver;
 import android.os.Handler;
 import android.os.Message;
 import android.os.PersistableBundle;
@@ -30,6 +31,8 @@
 import android.telephony.CarrierConfigManager;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.util.NotificationChannelController;
@@ -38,6 +41,7 @@
 import java.util.Map;
 
 
+
 /**
  * This contains Carrier specific logic based on the states/events
  * managed in ServiceStateTracker.
@@ -53,18 +57,70 @@
     private static final int UNINITIALIZED_DELAY_VALUE = -1;
     private Phone mPhone;
     private ServiceStateTracker mSST;
-
+    private final Map<Integer, NotificationType> mNotificationTypeMap = new HashMap<>();
+    private int mPreviousSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     public static final int NOTIFICATION_PREF_NETWORK = 1000;
     public static final int NOTIFICATION_EMERGENCY_NETWORK = 1001;
 
-    private final Map<Integer, NotificationType> mNotificationTypeMap = new HashMap<>();
-
     public CarrierServiceStateTracker(Phone phone, ServiceStateTracker sst) {
         this.mPhone = phone;
         this.mSST = sst;
         phone.getContext().registerReceiver(mBroadcastReceiver, new IntentFilter(
                 CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+        // Listen for subscriber changes
+        SubscriptionManager.from(mPhone.getContext()).addOnSubscriptionsChangedListener(
+                new OnSubscriptionsChangedListener(this.getLooper()) {
+                    @Override
+                    public void onSubscriptionsChanged() {
+                        int subId = mPhone.getSubId();
+                        if (mPreviousSubId != subId) {
+                            mPreviousSubId = subId;
+                            registerPrefNetworkModeObserver();
+                        }
+                    }
+                });
+
         registerNotificationTypes();
+        registerPrefNetworkModeObserver();
+    }
+
+    private ContentObserver mPrefNetworkModeObserver = new ContentObserver(this) {
+        @Override
+        public void onChange(boolean selfChange) {
+            handlePrefNetworkModeChanged();
+        }
+    };
+
+    /**
+     * Return preferred network mode observer
+     */
+    @VisibleForTesting
+    public ContentObserver getContentObserver() {
+        return mPrefNetworkModeObserver;
+    }
+
+    private void registerPrefNetworkModeObserver() {
+        int subId = mPhone.getSubId();
+        unregisterPrefNetworkModeObserver();
+        if (SubscriptionManager.isValidSubscriptionId(subId)) {
+            mPhone.getContext().getContentResolver().registerContentObserver(
+                    Settings.Global.getUriFor(Settings.Global.PREFERRED_NETWORK_MODE + subId),
+                    true,
+                    mPrefNetworkModeObserver);
+        }
+    }
+
+    private void unregisterPrefNetworkModeObserver() {
+        mPhone.getContext().getContentResolver().unregisterContentObserver(
+                mPrefNetworkModeObserver);
+    }
+
+    /**
+     * Returns mNotificationTypeMap
+     */
+    @VisibleForTesting
+    public Map<Integer, NotificationType> getNotificationTypeMap() {
+        return mNotificationTypeMap;
     }
 
     private void registerNotificationTypes() {
@@ -152,14 +208,25 @@
     private void handleConfigChanges() {
         for (Map.Entry<Integer, NotificationType> entry : mNotificationTypeMap.entrySet()) {
             NotificationType notificationType = entry.getValue();
-            if (evaluateSendingMessage(notificationType)) {
-                Message notificationMsg = obtainMessage(notificationType.getTypeId(), null);
-                Rlog.i(LOG_TAG, "starting timer for notifications." + notificationType.getTypeId());
-                sendMessageDelayed(notificationMsg, getDelay(notificationType));
-            } else {
-                cancelNotification(notificationType.getTypeId());
-                Rlog.i(LOG_TAG, "canceling notifications: " + notificationType.getTypeId());
-            }
+            evaluateSendingMessageOrCancelNotification(notificationType);
+        }
+    }
+
+    private void handlePrefNetworkModeChanged() {
+        NotificationType notificationType = mNotificationTypeMap.get(NOTIFICATION_PREF_NETWORK);
+        if (notificationType != null) {
+            evaluateSendingMessageOrCancelNotification(notificationType);
+        }
+    }
+
+    private void evaluateSendingMessageOrCancelNotification(NotificationType notificationType) {
+        if (evaluateSendingMessage(notificationType)) {
+            Message notificationMsg = obtainMessage(notificationType.getTypeId(), null);
+            Rlog.i(LOG_TAG, "starting timer for notifications." + notificationType.getTypeId());
+            sendMessageDelayed(notificationMsg, getDelay(notificationType));
+        } else {
+            cancelNotification(notificationType.getTypeId());
+            Rlog.i(LOG_TAG, "canceling notifications: " + notificationType.getTypeId());
         }
     }
 
@@ -240,6 +307,13 @@
     }
 
     /**
+     * Dispose the CarrierServiceStateTracker.
+     */
+    public void dispose() {
+        unregisterPrefNetworkModeObserver();
+    }
+
+    /**
      * Class that defines the different types of notifications.
      */
     public interface NotificationType {
@@ -293,7 +367,7 @@
             }
             this.mDelay = bundle.getInt(
                     CarrierConfigManager.KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT);
-            Rlog.i(LOG_TAG, "reading time to delay notification emergency: " + mDelay);
+            Rlog.i(LOG_TAG, "reading time to delay notification pref network: " + mDelay);
         }
 
         public int getDelay() {
@@ -311,7 +385,7 @@
             Rlog.i(LOG_TAG, "PrefNetworkNotification: sendMessage() w/values: "
                     + "," + isPhoneStillRegistered() + "," + mDelay + "," + isGlobalMode()
                     + "," + mSST.isRadioOn());
-            if (mDelay == UNINITIALIZED_DELAY_VALUE ||  isPhoneStillRegistered() || isGlobalMode()
+            if (mDelay == UNINITIALIZED_DELAY_VALUE || isPhoneStillRegistered() || isGlobalMode()
                     || isRadioOffOrAirplaneMode()) {
                 return false;
             }
diff --git a/src/java/com/android/internal/telephony/CellBroadcastHandler.java b/src/java/com/android/internal/telephony/CellBroadcastHandler.java
index 4227148..8fc4642 100644
--- a/src/java/com/android/internal/telephony/CellBroadcastHandler.java
+++ b/src/java/com/android/internal/telephony/CellBroadcastHandler.java
@@ -30,6 +30,12 @@
 import android.provider.Telephony;
 import android.telephony.SmsCbMessage;
 import android.telephony.SubscriptionManager;
+import android.util.LocalLog;
+
+import com.android.internal.telephony.metrics.TelephonyMetrics;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 
 /**
  * Dispatch new Cell Broadcasts to receivers. Acquires a private wakelock until the broadcast
@@ -37,6 +43,8 @@
  */
 public class CellBroadcastHandler extends WakeLockStateMachine {
 
+    private final LocalLog mLocalLog = new LocalLog(100);
+
     private CellBroadcastHandler(Context context, Phone phone) {
         this("CellBroadcastHandler", context, phone);
     }
@@ -82,9 +90,18 @@
         String receiverPermission;
         int appOp;
 
+        // Log Cellbroadcast msg received event
+        TelephonyMetrics metrics = TelephonyMetrics.getInstance();
+        metrics.writeNewCBSms(mPhone.getPhoneId(), message.getMessageFormat(),
+                message.getMessagePriority(), message.isCmasMessage(), message.isEtwsMessage(),
+                message.getServiceCategory());
+
+        String msg;
         Intent intent;
         if (message.isEmergencyMessage()) {
-            log("Dispatching emergency SMS CB, SmsCbMessage is: " + message);
+            msg = "Dispatching emergency SMS CB, SmsCbMessage is: " + message;
+            log(msg);
+            mLocalLog.log(msg);
             intent = new Intent(Telephony.Sms.Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION);
             // Explicitly send the intent to the default cell broadcast receiver.
             intent.setPackage(mContext.getResources().getString(
@@ -92,7 +109,9 @@
             receiverPermission = Manifest.permission.RECEIVE_EMERGENCY_BROADCAST;
             appOp = AppOpsManager.OP_RECEIVE_EMERGECY_SMS;
         } else {
-            log("Dispatching SMS CB, SmsCbMessage is: " + message);
+            msg = "Dispatching SMS CB, SmsCbMessage is: " + message;
+            log(msg);
+            mLocalLog.log(msg);
             intent = new Intent(Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION);
             // Send implicit intent since there are various 3rd party carrier apps listen to
             // this intent.
@@ -121,4 +140,11 @@
         mContext.sendOrderedBroadcastAsUser(intent, UserHandle.ALL, receiverPermission, appOp,
                 mReceiver, getHandler(), Activity.RESULT_OK, null, null);
     }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("CellBroadcastHandler:");
+        mLocalLog.dump(fd, pw, args);
+        pw.flush();
+    }
 }
diff --git a/src/java/com/android/internal/telephony/CellularNetworkService.java b/src/java/com/android/internal/telephony/CellularNetworkService.java
index e5b0ae0..8b840de 100644
--- a/src/java/com/android/internal/telephony/CellularNetworkService.java
+++ b/src/java/com/android/internal/telephony/CellularNetworkService.java
@@ -228,7 +228,7 @@
                 CellIdentity cellIdentity =
                         convertHalCellIdentityToCellIdentity(voiceRegState.cellIdentity);
 
-                return new NetworkRegistrationState(transportType, domain, regState,
+                return new NetworkRegistrationState(domain, transportType, regState,
                         accessNetworkTechnology, reasonForDenial, emergencyOnly, availableServices,
                         cellIdentity, cssSupported, roamingIndicator, systemIsInPrl,
                         defaultRoamingIndicator);
@@ -248,7 +248,7 @@
                 CellIdentity cellIdentity =
                         convertHalCellIdentityToCellIdentity(voiceRegState.cellIdentity);
 
-                return new NetworkRegistrationState(transportType, domain, regState,
+                return new NetworkRegistrationState(domain, transportType, regState,
                         accessNetworkTechnology, reasonForDenial, emergencyOnly, availableServices,
                         cellIdentity, cssSupported, roamingIndicator, systemIsInPrl,
                         defaultRoamingIndicator);
@@ -258,8 +258,8 @@
         }
 
         private NetworkRegistrationState createRegistrationStateFromDataRegState(Object result) {
-            int transportType = TransportType.WWAN;
             int domain = NetworkRegistrationState.DOMAIN_PS;
+            int transportType = TransportType.WWAN;
 
             if (result instanceof android.hardware.radio.V1_0.DataRegStateResult) {
                 android.hardware.radio.V1_0.DataRegStateResult dataRegState =
@@ -273,7 +273,7 @@
                 CellIdentity cellIdentity =
                         convertHalCellIdentityToCellIdentity(dataRegState.cellIdentity);
 
-                return new NetworkRegistrationState(transportType, domain, regState,
+                return new NetworkRegistrationState(domain, transportType, regState,
                         accessNetworkTechnology, reasonForDenial, emergencyOnly, availableServices,
                         cellIdentity, maxDataCalls);
             } else if (result instanceof android.hardware.radio.V1_2.DataRegStateResult) {
@@ -288,7 +288,7 @@
                 CellIdentity cellIdentity =
                         convertHalCellIdentityToCellIdentity(dataRegState.cellIdentity);
 
-                return new NetworkRegistrationState(transportType, domain, regState,
+                return new NetworkRegistrationState(domain, transportType, regState,
                         accessNetworkTechnology, reasonForDenial, emergencyOnly, availableServices,
                         cellIdentity, maxDataCalls);
             }
@@ -330,7 +330,8 @@
                                 cellIdentity.cellIdentityTdscdma.get(0);
                         result = new  CellIdentityTdscdma(cellIdentityTdscdma.mcc,
                                 cellIdentityTdscdma.mnc, cellIdentityTdscdma.lac,
-                                cellIdentityTdscdma.cid, cellIdentityTdscdma.cpid);
+                                cellIdentityTdscdma.cid, cellIdentityTdscdma.cpid,
+                                Integer.MAX_VALUE, null, null);
                     }
                     break;
                 }
@@ -417,6 +418,7 @@
                                 cellIdentityTdscdma.base.lac,
                                 cellIdentityTdscdma.base.cid,
                                 cellIdentityTdscdma.base.cpid,
+                                cellIdentityTdscdma.uarfcn,
                                 cellIdentityTdscdma.operatorNames.alphaLong,
                                 cellIdentityTdscdma.operatorNames.alphaShort);
                     }
diff --git a/src/java/com/android/internal/telephony/ExponentialBackoff.java b/src/java/com/android/internal/telephony/ExponentialBackoff.java
deleted file mode 100644
index 80958c0..0000000
--- a/src/java/com/android/internal/telephony/ExponentialBackoff.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * 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;
-
-import android.annotation.NonNull;
-import android.os.Handler;
-import android.os.Looper;
-
-/** The implementation of exponential backoff with jitter applied. */
-public class ExponentialBackoff {
-    private int mRetryCounter;
-    private long mStartDelayMs;
-    private long mMaximumDelayMs;
-    private long mCurrentDelayMs;
-    private int mMultiplier;
-    private Runnable mRunnable;
-    private Handler mHandler;
-
-    public ExponentialBackoff(
-            long initialDelayMs,
-            long maximumDelayMs,
-            int multiplier,
-            @NonNull Looper looper,
-            @NonNull Runnable runnable) {
-        this(initialDelayMs, maximumDelayMs, multiplier, new Handler(looper), runnable);
-    }
-
-    public ExponentialBackoff(
-            long initialDelayMs,
-            long maximumDelayMs,
-            int multiplier,
-            @NonNull Handler handler,
-            @NonNull Runnable runnable) {
-        mRetryCounter = 0;
-        mStartDelayMs = initialDelayMs;
-        mMaximumDelayMs = maximumDelayMs;
-        mMultiplier = multiplier;
-        mHandler = handler;
-        mRunnable = runnable;
-    }
-
-    /** Starts the backoff, the runnable will be executed after {@link #mStartDelayMs}. */
-    public void start() {
-        mRetryCounter = 0;
-        mCurrentDelayMs = mStartDelayMs;
-        mHandler.removeCallbacks(mRunnable);
-        mHandler.postDelayed(mRunnable, mCurrentDelayMs);
-    }
-
-    /** Stops the backoff, all pending messages will be removed from the message queue. */
-    public void stop() {
-        mRetryCounter = 0;
-        mHandler.removeCallbacks(mRunnable);
-    }
-
-    /** Should call when the retry action has failed and we want to retry after a longer delay. */
-    public void notifyFailed() {
-        mRetryCounter++;
-        long temp = Math.min(
-                mMaximumDelayMs, (long) (mStartDelayMs * Math.pow(mMultiplier, mRetryCounter)));
-        mCurrentDelayMs = (long) (((1 + Math.random()) / 2) * temp);
-        mHandler.removeCallbacks(mRunnable);
-        mHandler.postDelayed(mRunnable, mCurrentDelayMs);
-    }
-
-    /** Returns the delay for the most recently posted message. */
-    public long getCurrentDelay() {
-        return mCurrentDelayMs;
-    }
-}
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index 4c3bb6a..5dd36ad 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -1114,7 +1114,8 @@
         // Check non-emergency voice CS call - shouldn't dial when POWER_OFF
         if (mSST != null && mSST.mSS.getState() == ServiceState.STATE_POWER_OFF /* CS POWER_OFF */
                 && !VideoProfile.isVideo(dialArgs.videoState) /* voice call */
-                && !isEmergency /* non-emergency call */) {
+                && !isEmergency /* non-emergency call */
+                && !(isUt && useImsForUt) /* not UT */) {
             throw new CallStateException(
                 CallStateException.ERROR_POWER_OFF,
                 "cannot dial voice call in airplane mode");
@@ -1669,7 +1670,13 @@
         Message resp;
         mVmNumber = voiceMailNumber;
         resp = obtainMessage(EVENT_SET_VM_NUMBER_DONE, 0, 0, onComplete);
+
         IccRecords r = mIccRecords.get();
+
+        if (!isPhoneTypeGsm() && mSimRecords != null) {
+            r = mSimRecords;
+        }
+
         if (r != null) {
             r.setVoiceMailNumber(alphaTag, mVmNumber, resp);
         }
diff --git a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
index f2383f5..d7a45d8 100644
--- a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
@@ -40,6 +40,7 @@
 import android.telephony.Rlog;
 import android.telephony.SmsManager;
 import android.telephony.SmsMessage;
+import android.util.LocalLog;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -50,6 +51,8 @@
 import com.android.internal.telephony.uicc.IccUtils;
 import com.android.internal.util.HexDump;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -86,6 +89,8 @@
     final private UserManager mUserManager;
     protected SmsDispatchersController mDispatchersController;
 
+    private final LocalLog mCellBroadcastLocalLog = new LocalLog(100);
+
     protected Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
@@ -788,7 +793,7 @@
         } else if (ranType == SmsManager.CELL_BROADCAST_RAN_TYPE_CDMA) {
             return enableCdmaBroadcastRange(startMessageId, endMessageId);
         } else {
-            throw new IllegalArgumentException("Not a supportted RAN Type");
+            throw new IllegalArgumentException("Not a supported RAN Type");
         }
     }
 
@@ -798,7 +803,7 @@
         } else if (ranType == SmsManager.CELL_BROADCAST_RAN_TYPE_CDMA)  {
             return disableCdmaBroadcastRange(startMessageId, endMessageId);
         } else {
-            throw new IllegalArgumentException("Not a supportted RAN Type");
+            throw new IllegalArgumentException("Not a supported RAN Type");
         }
     }
 
@@ -811,15 +816,21 @@
         String client = mContext.getPackageManager().getNameForUid(
                 Binder.getCallingUid());
 
+        String msg;
         if (!mCellBroadcastRangeManager.enableRange(startMessageId, endMessageId, client)) {
-            log("Failed to add GSM cell broadcast subscription for MID range " + startMessageId
-                    + " to " + endMessageId + " from client " + client);
+            msg = "Failed to add GSM cell broadcast channels range " + startMessageId
+                    + " to " + endMessageId;
+            log(msg);
+            mCellBroadcastLocalLog.log(msg);
             return false;
         }
 
-        if (DBG)
-            log("Added GSM cell broadcast subscription for MID range " + startMessageId
-                    + " to " + endMessageId + " from client " + client);
+        if (DBG) {
+            msg = "Added GSM cell broadcast channels range " + startMessageId
+                    + " to " + endMessageId;
+            log(msg);
+            mCellBroadcastLocalLog.log(msg);
+        }
 
         setCellBroadcastActivation(!mCellBroadcastRangeManager.isEmpty());
 
@@ -835,15 +846,21 @@
         String client = mContext.getPackageManager().getNameForUid(
                 Binder.getCallingUid());
 
+        String msg;
         if (!mCellBroadcastRangeManager.disableRange(startMessageId, endMessageId, client)) {
-            log("Failed to remove GSM cell broadcast subscription for MID range " + startMessageId
-                    + " to " + endMessageId + " from client " + client);
+            msg = "Failed to remove GSM cell broadcast channels range " + startMessageId
+                    + " to " + endMessageId;
+            log(msg);
+            mCellBroadcastLocalLog.log(msg);
             return false;
         }
 
-        if (DBG)
-            log("Removed GSM cell broadcast subscription for MID range " + startMessageId
-                    + " to " + endMessageId + " from client " + client);
+        if (DBG) {
+            msg = "Removed GSM cell broadcast channels range " + startMessageId
+                    + " to " + endMessageId;
+            log(msg);
+            mCellBroadcastLocalLog.log(msg);
+        }
 
         setCellBroadcastActivation(!mCellBroadcastRangeManager.isEmpty());
 
@@ -859,15 +876,20 @@
         String client = mContext.getPackageManager().getNameForUid(
                 Binder.getCallingUid());
 
+        String msg;
         if (!mCdmaBroadcastRangeManager.enableRange(startMessageId, endMessageId, client)) {
-            log("Failed to add cdma broadcast subscription for MID range " + startMessageId
-                    + " to " + endMessageId + " from client " + client);
+            msg = "Failed to add cdma broadcast channels range " + startMessageId + " to "
+                    + endMessageId;
+            log(msg);
+            mCellBroadcastLocalLog.log(msg);
             return false;
         }
 
-        if (DBG)
-            log("Added cdma broadcast subscription for MID range " + startMessageId
-                    + " to " + endMessageId + " from client " + client);
+        if (DBG) {
+            msg = "Added cdma broadcast channels range " + startMessageId + " to " + endMessageId;
+            log(msg);
+            mCellBroadcastLocalLog.log(msg);
+        }
 
         setCdmaBroadcastActivation(!mCdmaBroadcastRangeManager.isEmpty());
 
@@ -883,15 +905,20 @@
         String client = mContext.getPackageManager().getNameForUid(
                 Binder.getCallingUid());
 
+        String msg;
         if (!mCdmaBroadcastRangeManager.disableRange(startMessageId, endMessageId, client)) {
-            log("Failed to remove cdma broadcast subscription for MID range " + startMessageId
-                    + " to " + endMessageId + " from client " + client);
+            msg = "Failed to remove cdma broadcast channels range " + startMessageId + " to "
+                    + endMessageId;
+            log(msg);
+            mCellBroadcastLocalLog.log(msg);
             return false;
         }
 
-        if (DBG)
-            log("Removed cdma broadcast subscription for MID range " + startMessageId
-                    + " to " + endMessageId + " from client " + client);
+        if (DBG) {
+            msg = "Removed cdma broadcast channels range " + startMessageId + " to " + endMessageId;
+            log(msg);
+            mCellBroadcastLocalLog.log(msg);
+        }
 
         setCdmaBroadcastActivation(!mCdmaBroadcastRangeManager.isEmpty());
 
@@ -1335,4 +1362,11 @@
         return result != null ? result : destAddr;
     }
 
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("CellBroadcast log:");
+        mCellBroadcastLocalLog.dump(fd, pw, args);
+        pw.println("SMS dispatcher controller log:");
+        mDispatchersController.dump(fd, pw, args);
+        pw.flush();
+    }
 }
diff --git a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
index 184798e..5bc62d5 100644
--- a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
+++ b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
@@ -16,8 +16,11 @@
 
 package com.android.internal.telephony;
 
+import android.content.Context;
+import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.provider.Telephony.Sms.Intents;
+import android.telephony.CarrierConfigManager;
 import android.telephony.Rlog;
 import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.aidl.IImsSmsListener;
@@ -26,6 +29,8 @@
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.telephony.ims.stub.ImsSmsImplBase;
 import android.telephony.ims.stub.ImsSmsImplBase.SendStatusResult;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.ServiceState;
 import android.util.Pair;
 
 import com.android.ims.ImsException;
@@ -228,6 +233,50 @@
         getImsManager().onSmsReady();
     }
 
+    private boolean isLteService() {
+        return ((mPhone.getServiceState().getRilVoiceRadioTechnology() ==
+            ServiceState.RIL_RADIO_TECHNOLOGY_LTE) && (mPhone.getServiceState().
+                getState() == ServiceState.STATE_IN_SERVICE));
+    }
+
+    private boolean isLimitedLteService() {
+        return ((mPhone.getServiceState().getRilVoiceRadioTechnology() ==
+            ServiceState.RIL_RADIO_TECHNOLOGY_LTE) && mPhone.getServiceState().isEmergencyOnly());
+    }
+
+    private boolean isEmergencySmsPossible() {
+        return isLteService() || isLimitedLteService();
+    }
+
+    public boolean isEmergencySmsSupport(String destAddr) {
+        PersistableBundle b;
+        boolean eSmsCarrierSupport = false;
+        if (!PhoneNumberUtils.isLocalEmergencyNumber(mContext, mPhone.getSubId(), destAddr)) {
+            Rlog.e(TAG, "Emergency Sms is not supported for: " + destAddr);
+            return false;
+        }
+        CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
+                .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        if (configManager == null) {
+            Rlog.e(TAG, "configManager is null");
+            return false;
+        }
+        b = configManager.getConfigForSubId(getSubId());
+        if (b == null) {
+            Rlog.e(TAG, "PersistableBundle is null");
+            return false;
+        }
+        eSmsCarrierSupport = b.getBoolean(CarrierConfigManager.
+                                                      KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL);
+        boolean lteOrLimitedLte = isEmergencySmsPossible();
+        Rlog.i(TAG, "isEmergencySmsSupport emergencySmsCarrierSupport: "
+               + eSmsCarrierSupport + " destAddr: " + destAddr + " mIsImsServiceUp: "
+               + mIsImsServiceUp + " lteOrLimitedLte: " + lteOrLimitedLte);
+
+        return eSmsCarrierSupport && mIsImsServiceUp && lteOrLimitedLte;
+    }
+
+
     public boolean isAvailable() {
         synchronized (mLock) {
             Rlog.d(TAG, "isAvailable: up=" + mIsImsServiceUp + ", reg= " + mIsRegistered
@@ -247,8 +296,10 @@
     }
 
     @Override
-    protected boolean shouldBlockSms() {
-        return SMSDispatcherUtil.shouldBlockSms(isCdmaMo(), mPhone);
+    protected boolean shouldBlockSmsForEcbm() {
+        // We should not block outgoing SMS during ECM on IMS. It only applies to outgoing CDMA
+        // SMS.
+        return false;
     }
 
     @Override
@@ -278,6 +329,10 @@
                 + " mMessageRef=" + tracker.mMessageRef
                 + " SS=" + mPhone.getServiceState().getState());
 
+        // Flag that this Tracker is using the ImsService implementation of SMS over IMS for sending
+        // this message. Any fallbacks will happen over CS only.
+        tracker.mUsesImsServiceForIms = true;
+
         HashMap<String, Object> map = tracker.getData();
 
         byte[] pdu = (byte[]) map.get(MAP_KEY_PDU);
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index 53072c7..b51498e 100644
--- a/src/java/com/android/internal/telephony/InboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -60,6 +60,7 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
+import android.util.LocalLog;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
@@ -69,6 +70,8 @@
 import com.android.internal.util.StateMachine;
 
 import java.io.ByteArrayOutputStream;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
@@ -157,6 +160,9 @@
     /** New SMS received as an AsyncResult. */
     public static final int EVENT_INJECT_SMS = 8;
 
+    /** Update the sms tracker */
+    public static final int EVENT_UPDATE_TRACKER = 9;
+
     /** Wakelock release delay when returning to idle state. */
     private static final int WAKELOCK_TIMEOUT = 3000;
 
@@ -205,6 +211,8 @@
 
     private UserManager mUserManager;
 
+    private LocalLog mLocalLog = new LocalLog(64);
+
     IDeviceIdleController mDeviceIdleController;
 
     // Delete permanently from raw table
@@ -449,6 +457,7 @@
                     // if any broadcasts were sent, transition to waiting state
                     InboundSmsTracker inboundSmsTracker = (InboundSmsTracker) msg.obj;
                     if (processMessagePart(inboundSmsTracker)) {
+                        sendMessage(obtainMessage(EVENT_UPDATE_TRACKER, msg.obj));
                         transitionTo(mWaitingState);
                     } else {
                         // if event is sent from SmsBroadcastUndelivered.broadcastSms(), and
@@ -474,10 +483,17 @@
                     }
                     return HANDLED;
 
+                case EVENT_UPDATE_TRACKER:
+                    logd("process tracker message in DeliveringState " + msg.arg1);
+                    return HANDLED;
+
                 // we shouldn't get this message type in this state, log error and halt.
                 case EVENT_BROADCAST_COMPLETE:
                 case EVENT_START_ACCEPTING_SMS:
                 default:
+                    String errorMsg = "Unhandled msg in delivering state, msg.what = " + msg.what;
+                    loge(errorMsg);
+                    mLocalLog.log(errorMsg);
                     // let DefaultState handle these unexpected message types
                     return NOT_HANDLED;
             }
@@ -493,6 +509,8 @@
      */
     private class WaitingState extends State {
 
+        private InboundSmsTracker mLastDeliveredSmsTracker;
+
         @Override
         public void enter() {
             if (DBG) log("entering Waiting state");
@@ -512,10 +530,20 @@
             switch (msg.what) {
                 case EVENT_BROADCAST_SMS:
                     // defer until the current broadcast completes
+                    if (mLastDeliveredSmsTracker != null) {
+                        String str = "Defer sms broadcast due to undelivered sms, "
+                                + " messageCount = " + mLastDeliveredSmsTracker.getMessageCount()
+                                + " destPort = " + mLastDeliveredSmsTracker.getDestPort()
+                                + " timestamp = " + mLastDeliveredSmsTracker.getTimestamp()
+                                + " currentTimestamp = " + System.currentTimeMillis();
+                        logd(str);
+                        mLocalLog.log(str);
+                    }
                     deferMessage(msg);
                     return HANDLED;
 
                 case EVENT_BROADCAST_COMPLETE:
+                    mLastDeliveredSmsTracker = null;
                     // return to idle after handling all deferred messages
                     sendMessage(EVENT_RETURN_TO_IDLE);
                     transitionTo(mDeliveringState);
@@ -525,6 +553,13 @@
                     // not ready to return to idle; ignore
                     return HANDLED;
 
+                case EVENT_UPDATE_TRACKER:
+                    for (int i = 1; i < 10; i++) {
+                        deferMessage(obtainMessage(EVENT_UPDATE_TRACKER, i, i, msg.obj));
+                    }
+                    mLastDeliveredSmsTracker = (InboundSmsTracker) msg.obj;
+                    return HANDLED;
+
                 default:
                     // parent state handles the other message types
                     return NOT_HANDLED;
@@ -835,8 +870,10 @@
         // Do not process null pdu(s). Check for that and return false in that case.
         List<byte[]> pduList = Arrays.asList(pdus);
         if (pduList.size() == 0 || pduList.contains(null)) {
-            loge("processMessagePart: returning false due to " +
-                    (pduList.size() == 0 ? "pduList.size() == 0" : "pduList.contains(null)"));
+            String errorMsg = "processMessagePart: returning false due to "
+                    + (pduList.size() == 0 ? "pduList.size() == 0" : "pduList.contains(null)");
+            loge(errorMsg);
+            mLocalLog.log(errorMsg);
             return false;
         }
 
@@ -1529,6 +1566,15 @@
         }
     }
 
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        super.dump(fd, pw, args);
+        if (mCellBroadcastHandler != null) {
+            mCellBroadcastHandler.dump(fd, pw, args);
+        }
+        mLocalLog.dump(fd, pw, args);
+    }
+
     // Some providers send formfeeds in their messages. Convert those formfeeds to newlines.
     private static String replaceFormFeeds(String s) {
         return s == null ? "" : s.replace('\f', '\n');
diff --git a/src/java/com/android/internal/telephony/LocaleTracker.java b/src/java/com/android/internal/telephony/LocaleTracker.java
index 96ffe0f..8977b03 100644
--- a/src/java/com/android/internal/telephony/LocaleTracker.java
+++ b/src/java/com/android/internal/telephony/LocaleTracker.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.telephony;
 
-import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
 import static android.text.format.DateUtils.SECOND_IN_MILLIS;
 
 import android.annotation.NonNull;
@@ -26,6 +26,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.wifi.WifiManager;
+import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -34,6 +35,7 @@
 import android.telephony.CellInfoLte;
 import android.telephony.CellInfoWcdma;
 import android.telephony.Rlog;
+import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.LocalLog;
@@ -54,19 +56,26 @@
     private static final boolean DBG = true;
     private static final String TAG = LocaleTracker.class.getSimpleName();
 
-    /** Event to trigger get cell info from the modem */
+    /** Event for getting cell info from the modem */
     private static final int EVENT_GET_CELL_INFO                = 1;
 
-    /** Event to trigger update operator numeric */
+    /** Event for operator numeric update */
     private static final int EVENT_UPDATE_OPERATOR_NUMERIC      = 2;
 
+    /** Event for service state changed */
+    private static final int EVENT_SERVICE_STATE_CHANGED        = 3;
+
     // Todo: Read this from Settings.
     /** The minimum delay to get cell info from the modem */
     private static final long CELL_INFO_MIN_DELAY_MS = 2 * SECOND_IN_MILLIS;
 
     // Todo: Read this from Settings.
     /** The maximum delay to get cell info from the modem */
-    private static final long CELL_INFO_MAX_DELAY_MS = 1 * HOUR_IN_MILLIS;
+    private static final long CELL_INFO_MAX_DELAY_MS = 10 * MINUTE_IN_MILLIS;
+
+    // Todo: Read this from Settings.
+    /** The delay for periodically getting cell info from the modem */
+    private static final long CELL_INFO_PERIODIC_POLLING_DELAY_MS = 10 * MINUTE_IN_MILLIS;
 
     private final Phone mPhone;
 
@@ -88,6 +97,9 @@
     @Nullable
     private String mCurrentCountryIso;
 
+    /** Current service state. Must be one of ServiceState.STATE_XXX. */
+    private int mLastServiceState = -1;
+
     private final LocalLog mLocalLog = new LocalLog(50);
 
     /** Broadcast receiver to get SIM card state changed event */
@@ -121,6 +133,10 @@
             case EVENT_UPDATE_OPERATOR_NUMERIC:
                 updateOperatorNumericSync((String) msg.obj);
                 break;
+            case EVENT_SERVICE_STATE_CHANGED:
+                AsyncResult ar = (AsyncResult) msg.obj;
+                onServiceStateChanged((ServiceState) ar.result);
+                break;
             default:
                 throw new IllegalStateException("Unexpected message arrives. msg = " + msg.what);
         }
@@ -140,6 +156,8 @@
         final IntentFilter filter = new IntentFilter();
         filter.addAction(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED);
         mPhone.getContext().registerReceiver(mBroadcastReceiver, filter);
+
+        mPhone.registerForServiceStateChanged(this, EVENT_SERVICE_STATE_CHANGED, null);
     }
 
     /**
@@ -207,6 +225,32 @@
     }
 
     /**
+     * Called when service state changed.
+     *
+     * @param serviceState Service state
+     */
+    private void onServiceStateChanged(ServiceState serviceState) {
+        int state = serviceState.getState();
+        if (state != mLastServiceState) {
+            if (state != ServiceState.STATE_POWER_OFF && TextUtils.isEmpty(mOperatorNumeric)) {
+                // When the device is out of airplane mode or powered on, and network's MCC/MNC is
+                // not available, we get cell info from the modem.
+                String msg = "Service state " + ServiceState.rilServiceStateToString(state)
+                        + ". Get cell info now.";
+                if (DBG) log(msg);
+                mLocalLog.log(msg);
+                getCellInfo();
+            } else if (state == ServiceState.STATE_POWER_OFF) {
+                // Clear the cell info when the device is in airplane mode.
+                if (mCellInfo != null) mCellInfo.clear();
+                stopCellInfoRetry();
+            }
+            updateLocale();
+            mLastServiceState = state;
+        }
+    }
+
+    /**
      * Update MCC/MNC from network service state synchronously. Note if this is called from phone
      * process's main thread and if the update operation requires getting cell info from the modem,
      * the cached cell info will be used to determine the locale. If the cached cell info is not
@@ -216,13 +260,11 @@
      */
     public synchronized void updateOperatorNumericSync(String operatorNumeric) {
         // Check if the operator numeric changes.
-        String msg = "updateOperatorNumeric. mcc/mnc=" + operatorNumeric;
-        if (DBG) log(msg);
-        mLocalLog.log(msg);
+        if (DBG) log("updateOperatorNumericSync. mcc/mnc=" + operatorNumeric);
         if (!Objects.equals(mOperatorNumeric, operatorNumeric)) {
-            if (DBG) {
-                log("onUpdateOperatorNumeric: operator numeric changes to " + operatorNumeric);
-            }
+            String msg = "Operator numeric changes to " + operatorNumeric;
+            if (DBG) log(msg);
+            mLocalLog.log(msg);
             mOperatorNumeric = operatorNumeric;
 
             // If the operator numeric becomes unavailable, we need to get the latest cell info so
@@ -233,9 +275,10 @@
                 }
                 getCellInfo();
             } else {
-                // If operator numeric is available, that means we camp on network. So reset the
-                // fail cell info count.
-                mFailCellInfoCount = 0;
+                // If operator numeric is available, that means we camp on network. So we should
+                // clear the cell info and stop cell info retry.
+                if (mCellInfo != null) mCellInfo.clear();
+                stopCellInfoRetry();
             }
             updateLocale();
         }
@@ -273,14 +316,25 @@
     }
 
     /**
+     * Stop retrying getting cell info from the modem. It cancels any scheduled cell info retrieving
+     * request.
+     */
+    private void stopCellInfoRetry() {
+        mFailCellInfoCount = 0;
+        removeMessages(EVENT_GET_CELL_INFO);
+    }
+
+    /**
      * Get cell info from the modem.
      */
     private void getCellInfo() {
         String msg;
         if (!mPhone.getServiceStateTracker().getDesiredPowerState()) {
-            msg = "Radio is off. No need to get cell info.";
+            msg = "Radio is off. Stopped cell info retry. Cleared the previous cached cell info.";
+            if (mCellInfo != null) mCellInfo.clear();
             if (DBG) log(msg);
             mLocalLog.log(msg);
+            stopCellInfoRetry();
             return;
         }
 
@@ -294,12 +348,16 @@
             // If we can't get a valid cell info. Try it again later.
             long delay = getCellInfoDelayTime(++mFailCellInfoCount);
             if (DBG) log("Can't get cell info. Try again in " + delay / 1000 + " secs.");
+            removeMessages(EVENT_GET_CELL_INFO);
             sendMessageDelayed(obtainMessage(EVENT_GET_CELL_INFO), delay);
         } else {
-            mFailCellInfoCount = 0;
-            // We successfully got cell info from the modem. Cancel the queued get cell info event
-            // if there is any.
-            removeMessages(EVENT_GET_CELL_INFO);
+            // We successfully got cell info from the modem. We should stop cell info retry.
+            stopCellInfoRetry();
+
+            // Now we need to get the cell info from the modem periodically even if we already got
+            // the cell info because the user can move.
+            sendMessageDelayed(obtainMessage(EVENT_GET_CELL_INFO),
+                    CELL_INFO_PERIODIC_POLLING_DELAY_MS);
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/MccTable.java b/src/java/com/android/internal/telephony/MccTable.java
index 4a469a2..fb28194 100644
--- a/src/java/com/android/internal/telephony/MccTable.java
+++ b/src/java/com/android/internal/telephony/MccTable.java
@@ -350,12 +350,25 @@
      * @param mcc Mobile Country Code of the SIM or SIM-like entity (build prop on CDMA)
      */
     private static void setTimezoneFromMccIfNeeded(Context context, int mcc) {
-        if (!TimeServiceHelper.isTimeZoneSettingInitializedStatic()) {
-            String zoneId = defaultTimeZoneForMcc(mcc);
-            if (zoneId != null && zoneId.length() > 0) {
-                // Set time zone based on MCC
-                TimeServiceHelper.setDeviceTimeZoneStatic(context, zoneId);
-                Slog.d(LOG_TAG, "timezone set to " + zoneId);
+        // Switch to use the time service helper associated with the NitzStateMachine impl
+        // being used. This logic will be removed once the old implementation is removed.
+        if (TelephonyComponentFactory.USE_NEW_NITZ_STATE_MACHINE) {
+            if (!NewTimeServiceHelper.isTimeZoneSettingInitializedStatic()) {
+                String zoneId = defaultTimeZoneForMcc(mcc);
+                if (zoneId != null && zoneId.length() > 0) {
+                    // Set time zone based on MCC
+                    NewTimeServiceHelper.setDeviceTimeZoneStatic(context, zoneId);
+                    Slog.d(LOG_TAG, "timezone set to " + zoneId);
+                }
+            }
+        } else {
+            if (!OldTimeServiceHelper.isTimeZoneSettingInitializedStatic()) {
+                String zoneId = defaultTimeZoneForMcc(mcc);
+                if (zoneId != null && zoneId.length() > 0) {
+                    // Set time zone based on MCC
+                    OldTimeServiceHelper.setDeviceTimeZoneStatic(context, zoneId);
+                    Slog.d(LOG_TAG, "timezone set to " + zoneId);
+                }
             }
         }
     }
diff --git a/src/java/com/android/internal/telephony/NewNitzStateMachine.java b/src/java/com/android/internal/telephony/NewNitzStateMachine.java
new file mode 100644
index 0000000..20c729f
--- /dev/null
+++ b/src/java/com/android/internal/telephony/NewNitzStateMachine.java
@@ -0,0 +1,491 @@
+/*
+ * Copyright 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;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.telephony.Rlog;
+import android.text.TextUtils;
+import android.util.LocalLog;
+import android.util.TimestampedValue;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.TimeZoneLookupHelper.CountryResult;
+import com.android.internal.telephony.TimeZoneLookupHelper.OffsetResult;
+import com.android.internal.telephony.metrics.TelephonyMetrics;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * {@hide}
+ */
+public final class NewNitzStateMachine implements NitzStateMachine {
+
+    private static final String LOG_TAG = ServiceStateTracker.LOG_TAG;
+    private static final boolean DBG = ServiceStateTracker.DBG;
+
+    // Time detection state.
+
+    /**
+     * The last NITZ-sourced time considered sent to the time detector service. Used to rate-limit
+     * calls to the time detector.
+     */
+    private TimestampedValue<Long> mSavedNitzTime;
+
+    // Time Zone detection state.
+
+    /** We always keep the last NITZ signal received in mLatestNitzSignal. */
+    private TimestampedValue<NitzData> mLatestNitzSignal;
+
+    /**
+     * Records whether the device should have a country code available via
+     * {@link DeviceState#getNetworkCountryIsoForPhone()}. Before this an NITZ signal
+     * received is (almost always) not enough to determine time zone. On test networks the country
+     * code should be available but can still be an empty string but this flag indicates that the
+     * information available is unlikely to improve.
+     */
+    private boolean mGotCountryCode = false;
+
+    /**
+     * The last time zone ID that has been determined. It may not have been set as the device time
+     * zone if automatic time zone detection is disabled but may later be used to set the time zone
+     * if the user enables automatic time zone detection.
+     */
+    private String mSavedTimeZoneId;
+
+    /**
+     * Boolean is {@code true} if NITZ has been used to determine a time zone (which may not
+     * ultimately have been used due to user settings). Cleared by {@link #handleNetworkAvailable()}
+     * and {@link #handleNetworkUnavailable()}. The flag can be used when historic NITZ data may no
+     * longer be valid. {@code false} indicates it is reasonable to try to set the time zone using
+     * less reliable algorithms than NITZ-based detection such as by just using network country
+     * code.
+     */
+    private boolean mNitzTimeZoneDetectionSuccessful = false;
+
+    // Miscellaneous dependencies and helpers not related to detection state.
+    private final LocalLog mTimeLog = new LocalLog(15);
+    private final LocalLog mTimeZoneLog = new LocalLog(15);
+    private final GsmCdmaPhone mPhone;
+    private final DeviceState mDeviceState;
+    private final NewTimeServiceHelper mTimeServiceHelper;
+    private final TimeZoneLookupHelper mTimeZoneLookupHelper;
+    /** Wake lock used while setting time of day. */
+    private final PowerManager.WakeLock mWakeLock;
+    private static final String WAKELOCK_TAG = "NitzStateMachine";
+
+    public NewNitzStateMachine(GsmCdmaPhone phone) {
+        this(phone,
+                new NewTimeServiceHelper(phone.getContext()),
+                new DeviceState(phone),
+                new TimeZoneLookupHelper());
+    }
+
+    @VisibleForTesting
+    public NewNitzStateMachine(GsmCdmaPhone phone, NewTimeServiceHelper timeServiceHelper,
+            DeviceState deviceState, TimeZoneLookupHelper timeZoneLookupHelper) {
+        mPhone = phone;
+
+        Context context = phone.getContext();
+        PowerManager powerManager =
+                (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
+
+        mDeviceState = deviceState;
+        mTimeZoneLookupHelper = timeZoneLookupHelper;
+        mTimeServiceHelper = timeServiceHelper;
+        mTimeServiceHelper.setListener(new NewTimeServiceHelper.Listener() {
+            @Override
+            public void onTimeZoneDetectionChange(boolean enabled) {
+                if (enabled) {
+                    handleAutoTimeZoneEnabled();
+                }
+            }
+        });
+    }
+
+    @Override
+    public void handleNetworkCountryCodeSet(boolean countryChanged) {
+        boolean hadCountryCode = mGotCountryCode;
+        mGotCountryCode = true;
+
+        String isoCountryCode = mDeviceState.getNetworkCountryIsoForPhone();
+        if (!TextUtils.isEmpty(isoCountryCode) && !mNitzTimeZoneDetectionSuccessful) {
+            updateTimeZoneFromNetworkCountryCode(isoCountryCode);
+        }
+
+        if (mLatestNitzSignal != null && (countryChanged || !hadCountryCode)) {
+            updateTimeZoneFromCountryAndNitz();
+        }
+    }
+
+    private void updateTimeZoneFromCountryAndNitz() {
+        String isoCountryCode = mDeviceState.getNetworkCountryIsoForPhone();
+        TimestampedValue<NitzData> nitzSignal = mLatestNitzSignal;
+
+        // TimeZone.getDefault() returns a default zone (GMT) even when time zone have never
+        // been set which makes it difficult to tell if it's what the user / time zone detection
+        // has chosen. isTimeZoneSettingInitialized() tells us whether the time zone of the
+        // device has ever been explicit set by the user or code.
+        final boolean isTimeZoneSettingInitialized =
+                mTimeServiceHelper.isTimeZoneSettingInitialized();
+
+        if (DBG) {
+            Rlog.d(LOG_TAG, "updateTimeZoneFromCountryAndNitz:"
+                    + " isTimeZoneSettingInitialized=" + isTimeZoneSettingInitialized
+                    + " nitzSignal=" + nitzSignal
+                    + " isoCountryCode=" + isoCountryCode);
+        }
+
+        try {
+            NitzData nitzData = nitzSignal.getValue();
+
+            String zoneId;
+            if (nitzData.getEmulatorHostTimeZone() != null) {
+                zoneId = nitzData.getEmulatorHostTimeZone().getID();
+            } else if (!mGotCountryCode) {
+                // We don't have a country code so we won't try to look up the time zone.
+                zoneId = null;
+            } else if (TextUtils.isEmpty(isoCountryCode)) {
+                // We have a country code but it's empty. This is most likely because we're on a
+                // test network that's using a bogus MCC (eg, "001"). Obtain a TimeZone based only
+                // on the NITZ parameters: it's only going to be correct in a few cases but it
+                // should at least have the correct offset.
+                OffsetResult lookupResult = mTimeZoneLookupHelper.lookupByNitz(nitzData);
+                String logMsg = "updateTimeZoneFromCountryAndNitz: lookupByNitz returned"
+                        + " lookupResult=" + lookupResult;
+                if (DBG) {
+                    Rlog.d(LOG_TAG, logMsg);
+                }
+                // We log this in the time zone log because it has been a source of bugs.
+                mTimeZoneLog.log(logMsg);
+
+                zoneId = lookupResult != null ? lookupResult.zoneId : null;
+            } else if (mLatestNitzSignal == null) {
+                if (DBG) {
+                    Rlog.d(LOG_TAG,
+                            "updateTimeZoneFromCountryAndNitz: No cached NITZ data available,"
+                                    + " not setting zone");
+                }
+                zoneId = null;
+            } else if (isNitzSignalOffsetInfoBogus(nitzSignal, isoCountryCode)) {
+                String logMsg = "updateTimeZoneFromCountryAndNitz: Received NITZ looks bogus, "
+                        + " isoCountryCode=" + isoCountryCode
+                        + " nitzSignal=" + nitzSignal;
+                if (DBG) {
+                    Rlog.d(LOG_TAG, logMsg);
+                }
+                // We log this in the time zone log because it has been a source of bugs.
+                mTimeZoneLog.log(logMsg);
+
+                zoneId = null;
+            } else {
+                OffsetResult lookupResult = mTimeZoneLookupHelper.lookupByNitzCountry(
+                        nitzData, isoCountryCode);
+                if (DBG) {
+                    Rlog.d(LOG_TAG, "updateTimeZoneFromCountryAndNitz: using"
+                            + " lookupByNitzCountry(nitzData, isoCountryCode),"
+                            + " nitzData=" + nitzData
+                            + " isoCountryCode=" + isoCountryCode
+                            + " lookupResult=" + lookupResult);
+                }
+                zoneId = lookupResult != null ? lookupResult.zoneId : null;
+            }
+
+            // Log the action taken to the dedicated time zone log.
+            final String tmpLog = "updateTimeZoneFromCountryAndNitz:"
+                    + " isTimeZoneSettingInitialized=" + isTimeZoneSettingInitialized
+                    + " isoCountryCode=" + isoCountryCode
+                    + " nitzSignal=" + nitzSignal
+                    + " zoneId=" + zoneId
+                    + " isTimeZoneDetectionEnabled()="
+                    + mTimeServiceHelper.isTimeZoneDetectionEnabled();
+            mTimeZoneLog.log(tmpLog);
+
+            // Set state as needed.
+            if (zoneId != null) {
+                if (DBG) {
+                    Rlog.d(LOG_TAG, "updateTimeZoneFromCountryAndNitz: zoneId=" + zoneId);
+                }
+                if (mTimeServiceHelper.isTimeZoneDetectionEnabled()) {
+                    setAndBroadcastNetworkSetTimeZone(zoneId);
+                } else {
+                    if (DBG) {
+                        Rlog.d(LOG_TAG, "updateTimeZoneFromCountryAndNitz: skip changing zone"
+                                + " as isTimeZoneDetectionEnabled() is false");
+                    }
+                }
+                mSavedTimeZoneId = zoneId;
+                mNitzTimeZoneDetectionSuccessful = true;
+            } else {
+                if (DBG) {
+                    Rlog.d(LOG_TAG,
+                            "updateTimeZoneFromCountryAndNitz: zoneId == null, do nothing");
+                }
+            }
+        } catch (RuntimeException ex) {
+            Rlog.e(LOG_TAG, "updateTimeZoneFromCountryAndNitz: Processing NITZ data"
+                    + " nitzSignal=" + nitzSignal
+                    + " isoCountryCode=" + isoCountryCode
+                    + " isTimeZoneSettingInitialized=" + isTimeZoneSettingInitialized
+                    + " ex=" + ex);
+        }
+    }
+
+    /**
+     * Returns true if the NITZ signal is definitely bogus, assuming that the country is correct.
+     */
+    private boolean isNitzSignalOffsetInfoBogus(
+            TimestampedValue<NitzData> nitzSignal, String isoCountryCode) {
+
+        if (TextUtils.isEmpty(isoCountryCode)) {
+            // We cannot say for sure.
+            return false;
+        }
+
+        NitzData newNitzData = nitzSignal.getValue();
+        boolean zeroOffsetNitz = newNitzData.getLocalOffsetMillis() == 0 && !newNitzData.isDst();
+        return zeroOffsetNitz && !countryUsesUtc(isoCountryCode, nitzSignal);
+    }
+
+    private boolean countryUsesUtc(
+            String isoCountryCode, TimestampedValue<NitzData> nitzSignal) {
+        return mTimeZoneLookupHelper.countryUsesUtc(
+                isoCountryCode,
+                nitzSignal.getValue().getCurrentTimeInMillis());
+    }
+
+    @Override
+    public void handleNetworkAvailable() {
+        if (DBG) {
+            Rlog.d(LOG_TAG, "handleNetworkAvailable: mNitzTimeZoneDetectionSuccessful="
+                    + mNitzTimeZoneDetectionSuccessful
+                    + ", Setting mNitzTimeZoneDetectionSuccessful=false");
+        }
+        mNitzTimeZoneDetectionSuccessful = false;
+    }
+
+    @Override
+    public void handleNetworkUnavailable() {
+        if (DBG) {
+            Rlog.d(LOG_TAG, "handleNetworkUnavailable");
+        }
+
+        mGotCountryCode = false;
+        mNitzTimeZoneDetectionSuccessful = false;
+    }
+
+    @Override
+    public void handleNitzReceived(TimestampedValue<NitzData> nitzSignal) {
+        // Always store the latest NITZ signal received.
+        mLatestNitzSignal = nitzSignal;
+
+        updateTimeZoneFromCountryAndNitz();
+        updateTimeFromNitz();
+    }
+
+    private void updateTimeFromNitz() {
+        TimestampedValue<NitzData> nitzSignal = mLatestNitzSignal;
+        try {
+            boolean ignoreNitz = mDeviceState.getIgnoreNitz();
+            if (ignoreNitz) {
+                if (DBG) {
+                    Rlog.d(LOG_TAG, "updateTimeFromNitz: Not suggesting system clock because"
+                            + " gsm.ignore-nitz is set");
+                }
+                return;
+            }
+
+            // Validate the nitzTimeSignal to reject obviously bogus elapsedRealtime values.
+            try {
+                // Acquire the wake lock as we are reading the elapsed realtime clock below.
+                mWakeLock.acquire();
+
+                long elapsedRealtime = mTimeServiceHelper.elapsedRealtime();
+                long millisSinceNitzReceived =
+                        elapsedRealtime - nitzSignal.getReferenceTimeMillis();
+                if (millisSinceNitzReceived < 0 || millisSinceNitzReceived > Integer.MAX_VALUE) {
+                    if (DBG) {
+                        Rlog.d(LOG_TAG, "updateTimeFromNitz: not setting time, unexpected"
+                                + " elapsedRealtime=" + elapsedRealtime
+                                + " nitzSignal=" + nitzSignal);
+                    }
+                    return;
+                }
+            } finally {
+                mWakeLock.release();
+            }
+
+            TimestampedValue<Long> newNitzTime = new TimestampedValue<>(
+                    nitzSignal.getReferenceTimeMillis(),
+                    nitzSignal.getValue().getCurrentTimeInMillis());
+
+            // Perform rate limiting: a NITZ signal received too close to a previous
+            // one will be disregarded unless there is a significant difference between the
+            // UTC times they represent.
+            if (mSavedNitzTime != null) {
+                int nitzUpdateSpacing = mDeviceState.getNitzUpdateSpacingMillis();
+                int nitzUpdateDiff = mDeviceState.getNitzUpdateDiffMillis();
+
+                // Calculate the elapsed time between the new signal and the last signal.
+                long elapsedRealtimeSinceLastSaved = newNitzTime.getReferenceTimeMillis()
+                        - mSavedNitzTime.getReferenceTimeMillis();
+
+                // Calculate the UTC difference between the time the two signals hold.
+                long utcTimeDifferenceMillis =
+                        newNitzTime.getValue() - mSavedNitzTime.getValue();
+
+                // Ideally the difference between elapsedRealtimeSinceLastSaved and
+                // utcTimeDifferenceMillis would be zero.
+                long millisGained = utcTimeDifferenceMillis - elapsedRealtimeSinceLastSaved;
+
+                if (elapsedRealtimeSinceLastSaved <= nitzUpdateSpacing
+                        && Math.abs(millisGained) <= nitzUpdateDiff) {
+                    if (DBG) {
+                        Rlog.d(LOG_TAG, "updateTimeFromNitz: not setting time. NITZ signal is"
+                                + " too similar to previous value received "
+                                + " mSavedNitzTime=" + mSavedNitzTime
+                                + ", nitzSignal=" + nitzSignal
+                                + ", nitzUpdateSpacing=" + nitzUpdateSpacing
+                                + ", nitzUpdateDiff=" + nitzUpdateDiff);
+                    }
+                    return;
+                }
+            }
+
+            String logMsg = "updateTimeFromNitz: suggesting system clock update"
+                    + " nitzSignal=" + nitzSignal
+                    + ", newNitzTime=" + newNitzTime
+                    + ", mSavedNitzTime= " + mSavedNitzTime;
+            if (DBG) {
+                Rlog.d(LOG_TAG, logMsg);
+            }
+            mTimeLog.log(logMsg);
+            mTimeServiceHelper.suggestDeviceTime(newNitzTime);
+            TelephonyMetrics.getInstance().writeNITZEvent(
+                    mPhone.getPhoneId(), newNitzTime.getValue());
+
+            // Save the last NITZ time signal that was suggested to enable rate limiting.
+            mSavedNitzTime = newNitzTime;
+        } catch (RuntimeException ex) {
+            Rlog.e(LOG_TAG, "updateTimeFromNitz: Processing NITZ data"
+                    + " nitzSignal=" + nitzSignal
+                    + " ex=" + ex);
+        }
+    }
+
+    private void setAndBroadcastNetworkSetTimeZone(String zoneId) {
+        if (DBG) {
+            Rlog.d(LOG_TAG, "setAndBroadcastNetworkSetTimeZone: zoneId=" + zoneId);
+        }
+        mTimeServiceHelper.setDeviceTimeZone(zoneId);
+        if (DBG) {
+            Rlog.d(LOG_TAG,
+                    "setAndBroadcastNetworkSetTimeZone: called setDeviceTimeZone()"
+                            + " zoneId=" + zoneId);
+        }
+    }
+
+    private void handleAutoTimeZoneEnabled() {
+        String tmpLog = "handleAutoTimeZoneEnabled: Reverting to NITZ TimeZone:"
+                + " mSavedTimeZoneId=" + mSavedTimeZoneId;
+        if (DBG) {
+            Rlog.d(LOG_TAG, tmpLog);
+        }
+        mTimeZoneLog.log(tmpLog);
+        if (mSavedTimeZoneId != null) {
+            setAndBroadcastNetworkSetTimeZone(mSavedTimeZoneId);
+        }
+    }
+
+    @Override
+    public void dumpState(PrintWriter pw) {
+        // Time Detection State
+        pw.println(" mSavedTime=" + mSavedNitzTime);
+
+        // Time Zone Detection State
+        pw.println(" mLatestNitzSignal=" + mLatestNitzSignal);
+        pw.println(" mGotCountryCode=" + mGotCountryCode);
+        pw.println(" mSavedTimeZoneId=" + mSavedTimeZoneId);
+        pw.println(" mNitzTimeZoneDetectionSuccessful=" + mNitzTimeZoneDetectionSuccessful);
+
+        // Miscellaneous
+        pw.println(" mWakeLock=" + mWakeLock);
+        pw.flush();
+    }
+
+    @Override
+    public void dumpLogs(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
+        ipw.println(" Time Logs:");
+        ipw.increaseIndent();
+        mTimeLog.dump(fd, ipw, args);
+        ipw.decreaseIndent();
+
+        ipw.println(" Time zone Logs:");
+        ipw.increaseIndent();
+        mTimeZoneLog.dump(fd, ipw, args);
+        ipw.decreaseIndent();
+    }
+
+    /**
+     * Update time zone by network country code, works well on countries which only have one time
+     * zone or multiple zones with the same offset.
+     *
+     * @param iso Country code from network MCC
+     */
+    private void updateTimeZoneFromNetworkCountryCode(String iso) {
+        CountryResult lookupResult = mTimeZoneLookupHelper.lookupByCountry(
+                iso, mTimeServiceHelper.currentTimeMillis());
+        if (lookupResult != null && lookupResult.allZonesHaveSameOffset) {
+            String logMsg = "updateTimeZoneFromNetworkCountryCode: tz result found"
+                    + " iso=" + iso
+                    + " lookupResult=" + lookupResult;
+            if (DBG) {
+                Rlog.d(LOG_TAG, logMsg);
+            }
+            mTimeZoneLog.log(logMsg);
+            String zoneId = lookupResult.zoneId;
+            if (mTimeServiceHelper.isTimeZoneDetectionEnabled()) {
+                setAndBroadcastNetworkSetTimeZone(zoneId);
+            }
+            mSavedTimeZoneId = zoneId;
+        } else {
+            if (DBG) {
+                Rlog.d(LOG_TAG, "updateTimeZoneFromNetworkCountryCode: no good zone for"
+                        + " iso=" + iso
+                        + " lookupResult=" + lookupResult);
+            }
+        }
+    }
+
+    public boolean getNitzTimeZoneDetectionSuccessful() {
+        return mNitzTimeZoneDetectionSuccessful;
+    }
+
+    @Override
+    public NitzData getCachedNitzData() {
+        return mLatestNitzSignal != null ? mLatestNitzSignal.getValue() : null;
+    }
+
+    @Override
+    public String getSavedTimeZoneId() {
+        return mSavedTimeZoneId;
+    }
+
+}
diff --git a/src/java/com/android/internal/telephony/TimeServiceHelper.java b/src/java/com/android/internal/telephony/NewTimeServiceHelper.java
similarity index 79%
copy from src/java/com/android/internal/telephony/TimeServiceHelper.java
copy to src/java/com/android/internal/telephony/NewTimeServiceHelper.java
index 94b094f..1346c5f 100644
--- a/src/java/com/android/internal/telephony/TimeServiceHelper.java
+++ b/src/java/com/android/internal/telephony/NewTimeServiceHelper.java
@@ -17,6 +17,8 @@
 package com.android.internal.telephony;
 
 import android.app.AlarmManager;
+import android.app.timedetector.TimeDetector;
+import android.app.timedetector.TimeSignal;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -26,24 +28,20 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.util.TimestampedValue;
 
 /**
  * An interface to various time / time zone detection behaviors that should be centralized into a
  * new service.
  */
 // Non-final to allow mocking.
-public class TimeServiceHelper {
+public class NewTimeServiceHelper {
 
     /**
      * Callback interface for automatic detection enable/disable changes.
      */
     public interface Listener {
         /**
-         * Automatic time detection has been enabled or disabled.
-         */
-        void onTimeDetectionChange(boolean enabled);
-
-        /**
          * Automatic time zone detection has been enabled or disabled.
          */
         void onTimeZoneDetectionChange(boolean enabled);
@@ -53,13 +51,15 @@
 
     private final Context mContext;
     private final ContentResolver mCr;
+    private final TimeDetector mTimeDetector;
 
     private Listener mListener;
 
     /** Creates a TimeServiceHelper */
-    public TimeServiceHelper(Context context) {
+    public NewTimeServiceHelper(Context context) {
         mContext = context;
         mCr = context.getContentResolver();
+        mTimeDetector = context.getSystemService(TimeDetector.class);
     }
 
     /**
@@ -75,13 +75,6 @@
         }
         this.mListener = listener;
         mCr.registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true,
-                new ContentObserver(new Handler()) {
-                    public void onChange(boolean selfChange) {
-                        listener.onTimeDetectionChange(isTimeDetectionEnabled());
-                    }
-                });
-        mCr.registerContentObserver(
                 Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
                 new ContentObserver(new Handler()) {
                     public void onChange(boolean selfChange) {
@@ -113,17 +106,6 @@
     }
 
     /**
-     * Returns true if automatic time detection is enabled in settings.
-     */
-    public boolean isTimeDetectionEnabled() {
-        try {
-            return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME) > 0;
-        } catch (Settings.SettingNotFoundException snfe) {
-            return true;
-        }
-    }
-
-    /**
      * Returns true if automatic time zone detection is enabled in settings.
      */
     public boolean isTimeZoneDetectionEnabled() {
@@ -145,17 +127,13 @@
     }
 
     /**
-     * Set the time and Send out a sticky broadcast so the system can determine
-     * if the time was set by the carrier.
+     * Suggest the time to the {@link TimeDetector}.
      *
-     * @param time time set by network
+     * @param signalTimeMillis the signal time as received from the network
      */
-    public void setDeviceTime(long time) {
-        SystemClock.setCurrentTimeMillis(time);
-        Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME);
-        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
-        intent.putExtra("time", time);
-        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+    public void suggestDeviceTime(TimestampedValue<Long> signalTimeMillis) {
+        TimeSignal timeSignal = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, signalTimeMillis);
+        mTimeDetector.suggestTime(timeSignal);
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/NitzStateMachine.java b/src/java/com/android/internal/telephony/NitzStateMachine.java
index 8fb36f2..6a5e47a 100644
--- a/src/java/com/android/internal/telephony/NitzStateMachine.java
+++ b/src/java/com/android/internal/telephony/NitzStateMachine.java
@@ -21,8 +21,8 @@
 import android.os.SystemProperties;
 import android.provider.Settings;
 import android.telephony.TelephonyManager;
+import android.util.TimestampedValue;
 
-import com.android.internal.telephony.util.TimeStampedValue;
 import com.android.internal.util.IndentingPrintWriter;
 
 import java.io.FileDescriptor;
@@ -55,7 +55,7 @@
     /**
      * Handle a new NITZ signal being received.
      */
-    void handleNitzReceived(TimeStampedValue<NitzData> nitzSignal);
+    void handleNitzReceived(TimestampedValue<NitzData> nitzSignal);
 
     /**
      * Dumps the current in-memory state to the supplied PrintWriter.
diff --git a/src/java/com/android/internal/telephony/OldNitzStateMachine.java b/src/java/com/android/internal/telephony/OldNitzStateMachine.java
index 23d2cc7..bb43f1e 100644
--- a/src/java/com/android/internal/telephony/OldNitzStateMachine.java
+++ b/src/java/com/android/internal/telephony/OldNitzStateMachine.java
@@ -21,12 +21,12 @@
 import android.telephony.Rlog;
 import android.text.TextUtils;
 import android.util.LocalLog;
+import android.util.TimestampedValue;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.TimeZoneLookupHelper.CountryResult;
 import com.android.internal.telephony.TimeZoneLookupHelper.OffsetResult;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
-import com.android.internal.telephony.util.TimeStampedValue;
 import com.android.internal.util.IndentingPrintWriter;
 
 import java.io.FileDescriptor;
@@ -47,12 +47,12 @@
      * not have been used to set the device time, but it can be used if auto time detection is
      * re-enabled.
      */
-    private TimeStampedValue<Long> mSavedNitzTime;
+    private TimestampedValue<Long> mSavedNitzTime;
 
     // Time Zone detection state.
 
     /** We always keep the last NITZ signal received in mLatestNitzSignal. */
-    private TimeStampedValue<NitzData> mLatestNitzSignal;
+    private TimestampedValue<NitzData> mLatestNitzSignal;
 
     /**
      * Records whether the device should have a country code available via
@@ -85,7 +85,7 @@
     private final LocalLog mTimeZoneLog = new LocalLog(15);
     private final GsmCdmaPhone mPhone;
     private final DeviceState mDeviceState;
-    private final TimeServiceHelper mTimeServiceHelper;
+    private final OldTimeServiceHelper mTimeServiceHelper;
     private final TimeZoneLookupHelper mTimeZoneLookupHelper;
     /** Wake lock used while setting time of day. */
     private final PowerManager.WakeLock mWakeLock;
@@ -93,13 +93,13 @@
 
     public OldNitzStateMachine(GsmCdmaPhone phone) {
         this(phone,
-                new TimeServiceHelper(phone.getContext()),
+                new OldTimeServiceHelper(phone.getContext()),
                 new DeviceState(phone),
                 new TimeZoneLookupHelper());
     }
 
     @VisibleForTesting
-    public OldNitzStateMachine(GsmCdmaPhone phone, TimeServiceHelper timeServiceHelper,
+    public OldNitzStateMachine(GsmCdmaPhone phone, OldTimeServiceHelper timeServiceHelper,
             DeviceState deviceState, TimeZoneLookupHelper timeZoneLookupHelper) {
         mPhone = phone;
 
@@ -111,7 +111,7 @@
         mDeviceState = deviceState;
         mTimeZoneLookupHelper = timeZoneLookupHelper;
         mTimeServiceHelper = timeServiceHelper;
-        mTimeServiceHelper.setListener(new TimeServiceHelper.Listener() {
+        mTimeServiceHelper.setListener(new OldTimeServiceHelper.Listener() {
             @Override
             public void onTimeDetectionChange(boolean enabled) {
                 if (enabled) {
@@ -145,7 +145,7 @@
 
     private void updateTimeZoneFromCountryAndNitz() {
         String isoCountryCode = mDeviceState.getNetworkCountryIsoForPhone();
-        TimeStampedValue<NitzData> nitzSignal = mLatestNitzSignal;
+        TimestampedValue<NitzData> nitzSignal = mLatestNitzSignal;
 
         // TimeZone.getDefault() returns a default zone (GMT) even when time zone have never
         // been set which makes it difficult to tell if it's what the user / time zone detection
@@ -162,7 +162,7 @@
         }
 
         try {
-            NitzData nitzData = nitzSignal.mValue;
+            NitzData nitzData = nitzSignal.getValue();
 
             String zoneId;
             if (nitzData.getEmulatorHostTimeZone() != null) {
@@ -260,23 +260,23 @@
      * Returns true if the NITZ signal is definitely bogus, assuming that the country is correct.
      */
     private boolean isNitzSignalOffsetInfoBogus(
-            TimeStampedValue<NitzData> nitzSignal, String isoCountryCode) {
+            TimestampedValue<NitzData> nitzSignal, String isoCountryCode) {
 
         if (TextUtils.isEmpty(isoCountryCode)) {
             // We cannot say for sure.
             return false;
         }
 
-        NitzData newNitzData = nitzSignal.mValue;
+        NitzData newNitzData = nitzSignal.getValue();
         boolean zeroOffsetNitz = newNitzData.getLocalOffsetMillis() == 0 && !newNitzData.isDst();
         return zeroOffsetNitz && !countryUsesUtc(isoCountryCode, nitzSignal);
     }
 
     private boolean countryUsesUtc(
-            String isoCountryCode, TimeStampedValue<NitzData> nitzSignal) {
+            String isoCountryCode, TimestampedValue<NitzData> nitzSignal) {
         return mTimeZoneLookupHelper.countryUsesUtc(
                 isoCountryCode,
-                nitzSignal.mValue.getCurrentTimeInMillis());
+                nitzSignal.getValue().getCurrentTimeInMillis());
     }
 
     @Override
@@ -300,7 +300,7 @@
     }
 
     @Override
-    public void handleNitzReceived(TimeStampedValue<NitzData> nitzSignal) {
+    public void handleNitzReceived(TimestampedValue<NitzData> nitzSignal) {
         // Always store the latest NITZ signal received.
         mLatestNitzSignal = nitzSignal;
 
@@ -309,7 +309,7 @@
     }
 
     private void updateTimeFromNitz() {
-        TimeStampedValue<NitzData> nitzSignal = mLatestNitzSignal;
+        TimestampedValue<NitzData> nitzSignal = mLatestNitzSignal;
         try {
             boolean ignoreNitz = mDeviceState.getIgnoreNitz();
             if (ignoreNitz) {
@@ -327,7 +327,8 @@
 
                 // Validate the nitzTimeSignal to reject obviously bogus elapsedRealtime values.
                 long elapsedRealtime = mTimeServiceHelper.elapsedRealtime();
-                long millisSinceNitzReceived = elapsedRealtime - nitzSignal.mElapsedRealtime;
+                long millisSinceNitzReceived =
+                        elapsedRealtime - nitzSignal.getReferenceTimeMillis();
                 if (millisSinceNitzReceived < 0 || millisSinceNitzReceived > Integer.MAX_VALUE) {
                     if (DBG) {
                         Rlog.d(LOG_TAG, "updateTimeFromNitz: not setting time, unexpected"
@@ -339,7 +340,7 @@
 
                 // Adjust the NITZ time by the delay since it was received to get the time now.
                 long adjustedCurrentTimeMillis =
-                        nitzSignal.mValue.getCurrentTimeInMillis() + millisSinceNitzReceived;
+                        nitzSignal.getValue().getCurrentTimeInMillis() + millisSinceNitzReceived;
                 long gained = adjustedCurrentTimeMillis - mTimeServiceHelper.currentTimeMillis();
 
                 if (mTimeServiceHelper.isTimeDetectionEnabled()) {
@@ -354,7 +355,7 @@
                         setAndBroadcastNetworkSetTime(logMsg, adjustedCurrentTimeMillis);
                     } else {
                         long elapsedRealtimeSinceLastSaved = mTimeServiceHelper.elapsedRealtime()
-                                - mSavedNitzTime.mElapsedRealtime;
+                                - mSavedNitzTime.getReferenceTimeMillis();
                         int nitzUpdateSpacing = mDeviceState.getNitzUpdateSpacingMillis();
                         int nitzUpdateDiff = mDeviceState.getNitzUpdateDiffMillis();
                         if (elapsedRealtimeSinceLastSaved > nitzUpdateSpacing
@@ -381,8 +382,9 @@
 
                 // Save the last NITZ time signal used so we can return to it later
                 // if auto-time detection is toggled.
-                mSavedNitzTime = new TimeStampedValue<>(
-                        adjustedCurrentTimeMillis, nitzSignal.mElapsedRealtime);
+                mSavedNitzTime = new TimestampedValue<>(
+                        nitzSignal.getReferenceTimeMillis(),
+                        nitzSignal.getValue().getCurrentTimeInMillis());
             } finally {
                 mWakeLock.release();
             }
@@ -434,8 +436,8 @@
                 String msg = "mSavedNitzTime: Reverting to NITZ time"
                         + " elapsedRealtime=" + elapsedRealtime
                         + " mSavedNitzTime=" + mSavedNitzTime;
-                long adjustedCurrentTimeMillis =
-                        mSavedNitzTime.mValue + (elapsedRealtime - mSavedNitzTime.mElapsedRealtime);
+                long adjustedCurrentTimeMillis = mSavedNitzTime.getValue()
+                        + (elapsedRealtime - mSavedNitzTime.getReferenceTimeMillis());
                 setAndBroadcastNetworkSetTime(msg, adjustedCurrentTimeMillis);
             } finally {
                 mWakeLock.release();
@@ -521,7 +523,7 @@
 
     @Override
     public NitzData getCachedNitzData() {
-        return mLatestNitzSignal != null ? mLatestNitzSignal.mValue : null;
+        return mLatestNitzSignal != null ? mLatestNitzSignal.getValue() : null;
     }
 
     @Override
diff --git a/src/java/com/android/internal/telephony/TimeServiceHelper.java b/src/java/com/android/internal/telephony/OldTimeServiceHelper.java
similarity index 98%
rename from src/java/com/android/internal/telephony/TimeServiceHelper.java
rename to src/java/com/android/internal/telephony/OldTimeServiceHelper.java
index 94b094f..9c7a763 100644
--- a/src/java/com/android/internal/telephony/TimeServiceHelper.java
+++ b/src/java/com/android/internal/telephony/OldTimeServiceHelper.java
@@ -32,7 +32,7 @@
  * new service.
  */
 // Non-final to allow mocking.
-public class TimeServiceHelper {
+public class OldTimeServiceHelper {
 
     /**
      * Callback interface for automatic detection enable/disable changes.
@@ -57,7 +57,7 @@
     private Listener mListener;
 
     /** Creates a TimeServiceHelper */
-    public TimeServiceHelper(Context context) {
+    public OldTimeServiceHelper(Context context) {
         mContext = context;
         mCr = context.getContentResolver();
     }
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index fdf1311..899f1fc 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -1420,8 +1420,6 @@
      */
     public void registerForServiceStateChanged(
             Handler h, int what, Object obj) {
-        checkCorrectThread(h);
-
         mServiceStateRegistrants.add(h, what, obj);
     }
 
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index 9ba5695..9f7ea05 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -29,6 +29,7 @@
 import android.hardware.radio.V1_0.CellInfoCdma;
 import android.hardware.radio.V1_0.CellInfoGsm;
 import android.hardware.radio.V1_0.CellInfoLte;
+import android.hardware.radio.V1_0.CellInfoTdscdma;
 import android.hardware.radio.V1_0.CellInfoType;
 import android.hardware.radio.V1_0.CellInfoWcdma;
 import android.hardware.radio.V1_0.DataProfileInfo;
@@ -3098,7 +3099,7 @@
             try {
                 radioProxy.sendImsSms(rr.mSerial, msg);
                 mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_IMS,
-                        SmsSession.Event.Format.SMS_FORMAT_3GPP);
+                        SmsSession.Event.Format.SMS_FORMAT_3GPP2);
             } catch (RemoteException | RuntimeException e) {
                 handleRadioProxyExceptionForRR(rr, "sendImsCdmaSms", e);
             }
@@ -3951,6 +3952,7 @@
             appendPrimitiveArrayToArrayList(
                     packetData.dstAddress.getAddress(), req.destinationAddress);
             req.destinationPort = packetData.dstPort;
+            req.maxKeepaliveIntervalMillis = intervalMillis;
 
             radioProxy11.startKeepalive(rr.mSerial, req);
         } catch (RemoteException | RuntimeException e) {
@@ -5278,7 +5280,7 @@
 
     private static void writeToParcelForWcdma(
             Parcel p, int lac, int cid, int psc, int uarfcn, String mcc, String mnc,
-            String al, String as, int ss, int ber) {
+            String al, String as, int ss, int ber, int rscp, int ecno) {
         p.writeInt(CellIdentity.TYPE_WCDMA);
         p.writeString(mcc);
         p.writeString(mnc);
@@ -5290,6 +5292,25 @@
         p.writeInt(uarfcn);
         p.writeInt(ss);
         p.writeInt(ber);
+        p.writeInt(rscp);
+        p.writeInt(ecno);
+    }
+
+    private static void writeToParcelForTdscdma(
+            Parcel p, int lac, int cid, int cpid, int uarfcn, String mcc, String mnc,
+            String al, String as, int ss, int ber, int rscp) {
+        p.writeInt(CellIdentity.TYPE_TDSCDMA);
+        p.writeString(mcc);
+        p.writeString(mnc);
+        p.writeString(al);
+        p.writeString(as);
+        p.writeInt(lac);
+        p.writeInt(cid);
+        p.writeInt(cpid);
+        p.writeInt(uarfcn);
+        p.writeInt(ss);
+        p.writeInt(ber);
+        p.writeInt(rscp);
     }
 
     /**
@@ -5383,10 +5404,29 @@
                             EMPTY_ALPHA_LONG,
                             EMPTY_ALPHA_SHORT,
                             cellInfoWcdma.signalStrengthWcdma.signalStrength,
-                            cellInfoWcdma.signalStrengthWcdma.bitErrorRate);
+                            cellInfoWcdma.signalStrengthWcdma.bitErrorRate,
+                            Integer.MAX_VALUE,
+                            Integer.MAX_VALUE);
                     break;
                 }
 
+                case CellInfoType.TD_SCDMA: {
+                    CellInfoTdscdma cellInfoTdscdma = record.tdscdma.get(0);
+                    writeToParcelForTdscdma(
+                            p,
+                            cellInfoTdscdma.cellIdentityTdscdma.lac,
+                            cellInfoTdscdma.cellIdentityTdscdma.cid,
+                            cellInfoTdscdma.cellIdentityTdscdma.cpid,
+                            Integer.MAX_VALUE,
+                            cellInfoTdscdma.cellIdentityTdscdma.mcc,
+                            cellInfoTdscdma.cellIdentityTdscdma.mnc,
+                            EMPTY_ALPHA_LONG,
+                            EMPTY_ALPHA_SHORT,
+                            Integer.MAX_VALUE,
+                            Integer.MAX_VALUE,
+                            convertTdscdmaRscpTo1_2(cellInfoTdscdma.signalStrengthTdscdma.rscp));
+                    break;
+                }
                 default:
                     throw new RuntimeException("unexpected cellinfotype: " + record.cellInfoType);
             }
@@ -5491,7 +5531,28 @@
                             cellInfoWcdma.cellIdentityWcdma.operatorNames.alphaLong,
                             cellInfoWcdma.cellIdentityWcdma.operatorNames.alphaShort,
                             cellInfoWcdma.signalStrengthWcdma.base.signalStrength,
-                            cellInfoWcdma.signalStrengthWcdma.base.bitErrorRate);
+                            cellInfoWcdma.signalStrengthWcdma.base.bitErrorRate,
+                            cellInfoWcdma.signalStrengthWcdma.rscp,
+                            cellInfoWcdma.signalStrengthWcdma.ecno);
+                    break;
+                }
+
+                case CellInfoType.TD_SCDMA: {
+                    android.hardware.radio.V1_2.CellInfoTdscdma cellInfoTdscdma =
+                            record.tdscdma.get(0);
+                    writeToParcelForTdscdma(
+                            p,
+                            cellInfoTdscdma.cellIdentityTdscdma.base.lac,
+                            cellInfoTdscdma.cellIdentityTdscdma.base.cid,
+                            cellInfoTdscdma.cellIdentityTdscdma.base.cpid,
+                            cellInfoTdscdma.cellIdentityTdscdma.uarfcn,
+                            cellInfoTdscdma.cellIdentityTdscdma.base.mcc,
+                            cellInfoTdscdma.cellIdentityTdscdma.base.mnc,
+                            cellInfoTdscdma.cellIdentityTdscdma.operatorNames.alphaLong,
+                            cellInfoTdscdma.cellIdentityTdscdma.operatorNames.alphaShort,
+                            cellInfoTdscdma.signalStrengthTdscdma.signalStrength,
+                            cellInfoTdscdma.signalStrengthTdscdma.bitErrorRate,
+                            cellInfoTdscdma.signalStrengthTdscdma.rscp);
                     break;
                 }
 
@@ -5508,19 +5569,22 @@
         return response;
     }
 
+    private static int convertTdscdmaRscpTo1_2(int rscp) {
+        // The HAL 1.0 range is 25..120; the ASU/ HAL 1.2 range is 0..96;
+        // yes, this means the range in 1.0 cannot express -24dBm = 96
+        if (rscp >= 25 && rscp <= 120) {
+            // First we flip the sign to convert from the HALs -rscp to the actual RSCP value.
+            int rscpDbm = -rscp;
+            // Then to convert from RSCP to ASU, we apply the offset which aligns 0 ASU to -120dBm.
+            return rscpDbm + 120;
+        }
+        return Integer.MAX_VALUE;
+    }
+
     /** Convert HAL 1.0 Signal Strength to android SignalStrength */
     @VisibleForTesting
     public static SignalStrength convertHalSignalStrength(
             android.hardware.radio.V1_0.SignalStrength signalStrength) {
-        int tdscdmaRscp_1_2 = 255; // 255 is the value for unknown/unreported ASU.
-        // The HAL 1.0 range is 25..120; the ASU/ HAL 1.2 range is 0..96;
-        // yes, this means the range in 1.0 cannot express -24dBm = 96
-        if (signalStrength.tdScdma.rscp >= 25 && signalStrength.tdScdma.rscp <= 120) {
-            // First we flip the sign to convert from the HALs -rscp to the actual RSCP value.
-            int rscpDbm = -signalStrength.tdScdma.rscp;
-            // Then to convert from RSCP to ASU, we apply the offset which aligns 0 ASU to -120dBm.
-            tdscdmaRscp_1_2 = rscpDbm + 120;
-        }
         return new SignalStrength(
                 signalStrength.gw.signalStrength,
                 signalStrength.gw.bitErrorRate,
@@ -5534,7 +5598,7 @@
                 signalStrength.lte.rsrq,
                 signalStrength.lte.rssnr,
                 signalStrength.lte.cqi,
-                tdscdmaRscp_1_2);
+                convertTdscdmaRscpTo1_2(signalStrength.tdScdma.rscp));
     }
 
     /** Convert HAL 1.2 Signal Strength to android SignalStrength */
diff --git a/src/java/com/android/internal/telephony/RadioResponse.java b/src/java/com/android/internal/telephony/RadioResponse.java
index e790ab6..0fc5279 100644
--- a/src/java/com/android/internal/telephony/RadioResponse.java
+++ b/src/java/com/android/internal/telephony/RadioResponse.java
@@ -1328,12 +1328,12 @@
                 } else {
                     ret = new KeepaliveStatus(keepaliveStatus.sessionHandle, convertedStatus);
                 }
+                // If responseInfo.error is NONE, response function sends the response message
+                // even if result is actually an error.
+                sendMessageResponse(rr.mResult, ret);
                 break;
             case RadioError.REQUEST_NOT_SUPPORTED:
                 ret = new KeepaliveStatus(KeepaliveStatus.ERROR_UNSUPPORTED);
-                // The request is unsupported, which is ok. We'll report it to the higher
-                // layer and treat it as acceptable in the RIL.
-                responseInfo.error = RadioError.NONE;
                 break;
             case RadioError.NO_RESOURCES:
                 ret = new KeepaliveStatus(KeepaliveStatus.ERROR_NO_RESOURCES);
@@ -1342,7 +1342,7 @@
                 ret = new KeepaliveStatus(KeepaliveStatus.ERROR_UNKNOWN);
                 break;
         }
-        sendMessageResponse(rr.mResult, ret);
+        // If responseInfo.error != NONE, the processResponseDone sends the response message.
         mRil.processResponseDone(rr, responseInfo, ret);
     }
 
diff --git a/src/java/com/android/internal/telephony/RatRatcheter.java b/src/java/com/android/internal/telephony/RatRatcheter.java
index 5e2850d..3745182 100644
--- a/src/java/com/android/internal/telephony/RatRatcheter.java
+++ b/src/java/com/android/internal/telephony/RatRatcheter.java
@@ -102,7 +102,7 @@
 
     /** Ratchets RATs and cell bandwidths if oldSS and newSS have the same RAT family. */
     public void ratchet(ServiceState oldSS, ServiceState newSS, boolean locationChange) {
-        if (isSameRatFamily(oldSS, newSS)) {
+        if (!locationChange && isSameRatFamily(oldSS, newSS)) {
             updateBandwidths(oldSS.getCellBandwidths(), newSS);
         }
         // temporarily disable rat ratchet on location change.
@@ -137,6 +137,10 @@
 
     private boolean isSameRatFamily(ServiceState ss1, ServiceState ss2) {
         synchronized (mRatFamilyMap) {
+            // Either the two technologies are the same or their families must be non-null
+            // and the same.
+            if (ss1.getRilDataRadioTechnology() == ss2.getRilDataRadioTechnology()) return true;
+            if (mRatFamilyMap.get(ss1.getRilDataRadioTechnology()) == null) return false;
             return mRatFamilyMap.get(ss1.getRilDataRadioTechnology())
                     == mRatFamilyMap.get(ss2.getRilDataRadioTechnology());
         }
diff --git a/src/java/com/android/internal/telephony/RetryManager.java b/src/java/com/android/internal/telephony/RetryManager.java
index 23c3498..0b4bc3c 100644
--- a/src/java/com/android/internal/telephony/RetryManager.java
+++ b/src/java/com/android/internal/telephony/RetryManager.java
@@ -22,11 +22,10 @@
 import android.os.SystemProperties;
 import android.telephony.CarrierConfigManager;
 import android.telephony.Rlog;
+import android.telephony.data.ApnSetting;
 import android.text.TextUtils;
 import android.util.Pair;
 
-import com.android.internal.telephony.dataconnection.ApnSetting;
-
 import java.util.ArrayList;
 import java.util.Random;
 
@@ -506,7 +505,9 @@
             if (++index == mWaitingApns.size()) index = 0;
 
             // Stop if we find the non-failed APN.
-            if (mWaitingApns.get(index).permanentFailed == false) break;
+            if (!mWaitingApns.get(index).getPermanentFailed()) {
+                break;
+            }
 
             // If we've already cycled through all the APNs, that means there is no APN we can try
             if (index == mCurrentApnIndex) return null;
@@ -553,7 +554,9 @@
             if (++index >= mWaitingApns.size()) index = 0;
 
             // Stop if we find the non-failed APN.
-            if (mWaitingApns.get(index).permanentFailed == false) break;
+            if (!mWaitingApns.get(index).getPermanentFailed()) {
+                break;
+            }
 
             // If we've already cycled through all the APNs, that means all APNs have
             // permanently failed
@@ -594,7 +597,7 @@
      * */
     public void markApnPermanentFailed(ApnSetting apn) {
         if (apn != null) {
-            apn.permanentFailed = true;
+            apn.setPermanentFailed(true);
         }
     }
 
@@ -627,7 +630,7 @@
         configureRetry();
 
         for (ApnSetting apn : mWaitingApns) {
-            apn.permanentFailed = false;
+            apn.setPermanentFailed(false);
         }
 
         log("Setting " + mWaitingApns.size() + " waiting APNs.");
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
index 5952c19..1e5afc7 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -628,7 +628,7 @@
      * Send an SMS PDU. Usually just calls {@link sendRawPdu}.
      */
     private void sendSubmitPdu(SmsTracker tracker) {
-        if (shouldBlockSms()) {
+        if (shouldBlockSmsForEcbm()) {
             Rlog.d(TAG, "Block SMS in Emergency Callback mode");
             tracker.onFailed(mContext, SmsManager.RESULT_ERROR_NO_SERVICE, 0/*errorCode*/);
         } else {
@@ -637,9 +637,9 @@
     }
 
     /**
-     * @return true if MO SMS should be blocked.
+     * @return true if MO SMS should be blocked for Emergency Callback Mode.
      */
-    protected abstract boolean shouldBlockSms();
+    protected abstract boolean shouldBlockSmsForEcbm();
 
     /**
      * Called when SMS send completes. Broadcasts a sentIntent on success.
@@ -1454,7 +1454,13 @@
         // fields need to be public for derived SmsDispatchers
         private final HashMap<String, Object> mData;
         public int mRetryCount;
-        public int mImsRetry; // nonzero indicates initial message was sent over Ims
+        // IMS retry counter. Nonzero indicates initial message was sent over IMS channel in RIL and
+        // counts how many retries have been made on the IMS channel.
+        // Used in older implementations where the message is sent over IMS using the RIL.
+        public int mImsRetry;
+        // Tag indicating that this SMS is being handled by the ImsSmsDispatcher. This tracker
+        // should not try to use SMS over IMS over the RIL interface in this case when falling back.
+        public boolean mUsesImsServiceForIms;
         public int mMessageRef;
         public boolean mExpectMore;
         public int mValidityPeriod;
@@ -1504,6 +1510,7 @@
             mFormat = format;
             mExpectMore = expectMore;
             mImsRetry = 0;
+            mUsesImsServiceForIms = false;
             mMessageRef = 0;
             mUnsentPartCount = unsentPartCount;
             mAnyPartFailed = anyPartFailed;
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index a90b4cb..84ec0dc 100644
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -80,6 +80,7 @@
 import android.util.Pair;
 import android.util.SparseArray;
 import android.util.TimeUtils;
+import android.util.TimestampedValue;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
@@ -94,7 +95,6 @@
 import com.android.internal.telephony.uicc.UiccCardApplication;
 import com.android.internal.telephony.uicc.UiccController;
 import com.android.internal.telephony.util.NotificationChannelController;
-import com.android.internal.telephony.util.TimeStampedValue;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.IndentingPrintWriter;
 
@@ -130,6 +130,7 @@
     private static final long LAST_CELL_INFO_LIST_MAX_AGE_MS = 2000;
     private long mLastCellInfoListTime;
     private List<CellInfo> mLastCellInfoList = null;
+    private List<PhysicalChannelConfig> mLastPhysicalChannelConfigList = null;
 
     private SignalStrength mSignalStrength;
 
@@ -581,11 +582,17 @@
         }
 
         // If we are previously in service, we need to notify that we are out of service now.
+        if (mSS != null && mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE) {
+            mNetworkDetachedRegistrants.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();
+        mSS.setStateOutOfService();
         mNewSS = new ServiceState();
         mLastCellInfoListTime = 0;
         mLastCellInfoList = null;
@@ -670,6 +677,10 @@
         mCi.unregisterForImsNetworkStateChanged(this);
         mPhone.getCarrierActionAgent().unregisterForCarrierAction(this,
                 CARRIER_ACTION_SET_RADIO_ENABLED);
+        if (mCSST != null) {
+            mCSST.dispose();
+            mCSST = null;
+        }
     }
 
     public boolean getDesiredPowerState() {
@@ -1449,6 +1460,7 @@
                                 + list);
                     }
                     mPhone.notifyPhysicalChannelConfiguration(list);
+                    mLastPhysicalChannelConfigList = list;
 
                     // only notify if bandwidths changed
                     if (RatRatcheter.updateBandwidths(getBandwidthsFromConfigs(list), mSS)) {
@@ -1648,9 +1660,9 @@
         mPollingContext[0]--;
 
         if (mPollingContext[0] == 0) {
+            mNewSS.setEmergencyOnly(mEmergencyOnly);
             if (mPhone.isPhoneTypeGsm()) {
                 updateRoamingState();
-                mNewSS.setEmergencyOnly(mEmergencyOnly);
             } else {
                 boolean namMatch = false;
                 if (!isSidsAllZeros() && isHomeSid(mNewSS.getCdmaSystemId())) {
@@ -1788,10 +1800,11 @@
                 mNewSS.setCssIndicator(cssIndicator);
                 mNewSS.setRilVoiceRadioTechnology(newVoiceRat);
                 mNewSS.addNetworkRegistrationState(networkRegState);
-                setChannelNumberFromCellIdentity(mNewSS, networkRegState.getCellIdentity());
+                setPhyCellInfoFromCellIdentity(mNewSS, networkRegState.getCellIdentity());
 
                 //Denial reason if registrationState = 3
-                int reasonForDenial = networkRegState.getReasonForDenial();
+                int reasonForDenial = networkRegState.getRejectCause();
+                mEmergencyOnly = networkRegState.isEmergencyEnabled();
                 if (mPhone.isPhoneTypeGsm()) {
 
                     mGsmRoaming = regCodeIsRoaming(registrationState);
@@ -1799,7 +1812,6 @@
 
                     boolean isVoiceCapable = mPhone.getContext().getResources()
                             .getBoolean(com.android.internal.R.bool.config_voice_capable);
-                    mEmergencyOnly = networkRegState.isEmergencyEnabled();
                 } else {
                     int roamingIndicator = voiceSpecificStates.roamingIndicator;
 
@@ -1864,11 +1876,18 @@
                 mNewSS.setDataRegState(serviceState);
                 mNewSS.setRilDataRadioTechnology(newDataRat);
                 mNewSS.addNetworkRegistrationState(networkRegState);
-                setChannelNumberFromCellIdentity(mNewSS, networkRegState.getCellIdentity());
+
+                // When we receive OOS reset the PhyChanConfig list so that non-return-to-idle
+                // implementers of PhyChanConfig unsol will not carry forward a CA report
+                // (2 or more cells) to a new cell if they camp for emergency service only.
+                if (serviceState == ServiceState.STATE_OUT_OF_SERVICE) {
+                    mLastPhysicalChannelConfigList = null;
+                }
+                setPhyCellInfoFromCellIdentity(mNewSS, networkRegState.getCellIdentity());
 
                 if (mPhone.isPhoneTypeGsm()) {
 
-                    mNewReasonDataDenied = networkRegState.getReasonForDenial();
+                    mNewReasonDataDenied = networkRegState.getRejectCause();
                     mNewMaxDataCalls = dataSpecificStates.maxDataCalls;
                     mDataRoaming = regCodeIsRoaming(registrationState);
                     // Save the data roaming state reported by modem registration before resource
@@ -2003,7 +2022,22 @@
         }
     }
 
-    private void setChannelNumberFromCellIdentity(ServiceState ss, CellIdentity cellIdentity) {
+    private static boolean isValidLteBandwidthKhz(int bandwidth) {
+        // Valid bandwidths, see 3gpp 36.101 sec. 5.6
+        switch (bandwidth) {
+            case 1400:
+            case 3000:
+            case 5000:
+            case 10000:
+            case 15000:
+            case 20000:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    private void setPhyCellInfoFromCellIdentity(ServiceState ss, CellIdentity cellIdentity) {
         if (cellIdentity == null) {
             if (DBG) {
                 log("Could not set ServiceState channel number. CellIdentity null");
@@ -2015,6 +2049,49 @@
         if (VDBG) {
             log("Setting channel number: " + cellIdentity.getChannelNumber());
         }
+
+        if (cellIdentity instanceof CellIdentityLte) {
+            CellIdentityLte cl = (CellIdentityLte) cellIdentity;
+            int[] bandwidths = null;
+            // Prioritize the PhysicalChannelConfig list because we might already be in carrier
+            // aggregation by the time poll state is performed.
+            if (!ArrayUtils.isEmpty(mLastPhysicalChannelConfigList)) {
+                bandwidths = getBandwidthsFromConfigs(mLastPhysicalChannelConfigList);
+                for (int bw : bandwidths) {
+                    if (!isValidLteBandwidthKhz(bw)) {
+                        loge("Invalid LTE Bandwidth in RegistrationState, " + bw);
+                        bandwidths = null;
+                        break;
+                    }
+                }
+            }
+            // If we don't have a PhysicalChannelConfig[] list, then pull from CellIdentityLte.
+            // This is normal if we're in idle mode and the PhysicalChannelConfig[] has already
+            // been updated. This is also a fallback in case the PhysicalChannelConfig info
+            // is invalid (ie, broken).
+            // Also, for vendor implementations that do not report return-to-idle, we should
+            // prioritize the bandwidth report in the CellIdentity, because the physical channel
+            // config report may be stale in the case where a single carrier was used previously
+            // and we transition to camped-for-emergency (since we never have a physical
+            // channel active). In the normal case of single-carrier non-return-to-idle, the
+            // values *must* be the same, so it doesn't matter which is chosen.
+            if (bandwidths == null || bandwidths.length == 1) {
+                final int cbw = cl.getBandwidth();
+                if (isValidLteBandwidthKhz(cbw)) {
+                    bandwidths = new int[] {cbw};
+                } else if (cbw == Integer.MAX_VALUE) {
+                    // Bandwidth is unreported; c'est la vie. This is not an error because
+                    // pre-1.2 HAL implementations do not support bandwidth reporting.
+                } else {
+                    loge("Invalid LTE Bandwidth in RegistrationState, " + cbw);
+                }
+            }
+            if (bandwidths != null) {
+                ss.setCellBandwidths(bandwidths);
+            }
+        } else {
+            if (VDBG) log("Skipping bandwidth update for Non-LTE cell.");
+        }
     }
 
     /**
@@ -2154,8 +2231,32 @@
         mNewSS.setCdmaEriIconIndex(EriInfo.ROAMING_INDICATOR_OFF);
     }
 
+    private void updateOperatorNameFromCarrierConfig() {
+        // Brand override gets a priority over carrier config. If brand override is not available,
+        // override the operator name in home network. Also do this only for CDMA. This is temporary
+        // and should be fixed in a proper way in a later release.
+        if (!mPhone.isPhoneTypeGsm() && !mSS.getRoaming()) {
+            boolean hasBrandOverride = mUiccController.getUiccCard(getPhoneId()) != null
+                    && mUiccController.getUiccCard(getPhoneId()).getOperatorBrandOverride() != null;
+            if (!hasBrandOverride) {
+                PersistableBundle config = getCarrierConfig();
+                if (config.getBoolean(
+                        CarrierConfigManager.KEY_CDMA_HOME_REGISTERED_PLMN_NAME_OVERRIDE_BOOL)) {
+                    String operator = config.getString(
+                            CarrierConfigManager.KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING);
+                    log("updateOperatorNameFromCarrierConfig: changing from "
+                            + mSS.getOperatorAlpha() + " to " + operator);
+                    // override long and short operator name, keeping numeric the same
+                    mSS.setOperatorName(operator, operator, mSS.getOperatorNumeric());
+                }
+            }
+        }
+    }
+
     protected void updateSpnDisplay() {
         updateOperatorNameFromEri();
+        // carrier config gets a priority over ERI
+        updateOperatorNameFromCarrierConfig();
 
         String wfcVoiceSpnFormat = null;
         String wfcDataSpnFormat = null;
@@ -2929,6 +3030,9 @@
 
         if (hasRilDataRadioTechnologyChanged || hasRilVoiceRadioTechnologyChanged) {
             logRatChange();
+
+            updateRatTypeForSignalStrength();
+            notifySignalStrength();
         }
 
         if (hasDataRegStateChanged || hasRilDataRadioTechnologyChanged) {
@@ -3454,8 +3558,8 @@
         NitzData newNitzData = NitzData.parse(nitzString);
         if (newNitzData != null) {
             try {
-                TimeStampedValue<NitzData> nitzSignal =
-                        new TimeStampedValue<>(newNitzData, nitzReceiveTime);
+                TimestampedValue<NitzData> nitzSignal =
+                        new TimestampedValue<>(nitzReceiveTime, newNitzData);
                 mNitzState.handleNitzReceived(nitzSignal);
             } finally {
                 if (DBG) {
@@ -3585,8 +3689,8 @@
                     return;
                 } else {
                     icon = com.android.internal.R.drawable.stat_notify_mmcc_indication_icn;
-                    // if using the single SIM resource, mSubId will be ignored
-                    title = context.getString(resId, mSubId);
+                    // if using the single SIM resource, simNumber will be ignored
+                    title = context.getString(resId, simNumber);
                     details = null;
                 }
                 break;
@@ -4024,18 +4128,6 @@
      * @return true if the signal strength changed and a notification was sent.
      */
     protected boolean onSignalStrengthResult(AsyncResult ar) {
-        boolean isGsm = false;
-        int dataRat = mSS.getRilDataRadioTechnology();
-        int voiceRat = mSS.getRilVoiceRadioTechnology();
-
-        // Override isGsm based on currently camped data and voice RATs
-        // Set isGsm to true if the RAT belongs to GSM family and not IWLAN
-        if ((dataRat != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
-                && ServiceState.isGsm(dataRat))
-                || (voiceRat != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
-                && ServiceState.isGsm(voiceRat))) {
-            isGsm = true;
-        }
 
         // This signal is used for both voice and data radio signal so parse
         // all fields
@@ -4043,12 +4135,6 @@
         if ((ar.exception == null) && (ar.result != null)) {
             mSignalStrength = (SignalStrength) ar.result;
             mSignalStrength.validateInput();
-            if (dataRat == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN
-                    && voiceRat == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) {
-                mSignalStrength.fixType();
-            } else {
-                mSignalStrength.setGsm(isGsm);
-            }
             mSignalStrength.setLteRsrpBoost(mSS.getLteEarfcnRsrpBoost());
 
             PersistableBundle config = getCarrierConfig();
@@ -4062,14 +4148,39 @@
                     CarrierConfigManager.KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY));
         } else {
             log("onSignalStrengthResult() Exception from RIL : " + ar.exception);
-            mSignalStrength = new SignalStrength(isGsm);
+            mSignalStrength = new SignalStrength(true);
         }
 
+        updateRatTypeForSignalStrength();
         boolean ssChanged = notifySignalStrength();
 
         return ssChanged;
     }
 
+    private void updateRatTypeForSignalStrength() {
+        if (mSignalStrength != null) {
+            boolean isGsm = false;
+            int dataRat = mSS.getRilDataRadioTechnology();
+            int voiceRat = mSS.getRilVoiceRadioTechnology();
+
+            // Override isGsm based on currently camped data and voice RATs
+            // Set isGsm to true if the RAT belongs to GSM family and not IWLAN
+            if ((dataRat != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+                    && ServiceState.isGsm(dataRat))
+                    || (voiceRat != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+                    && ServiceState.isGsm(voiceRat))) {
+                isGsm = true;
+            }
+
+            if (dataRat == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN
+                    && voiceRat == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) {
+                mSignalStrength.fixType();
+            } else {
+                mSignalStrength.setGsm(isGsm);
+            }
+        }
+    }
+
     /**
      * Hang up all voice call and turn off radio. Implemented by derived class.
      */
diff --git a/src/java/com/android/internal/telephony/SmsDispatchersController.java b/src/java/com/android/internal/telephony/SmsDispatchersController.java
index f3cd1c7..d8906e7 100644
--- a/src/java/com/android/internal/telephony/SmsDispatchersController.java
+++ b/src/java/com/android/internal/telephony/SmsDispatchersController.java
@@ -41,6 +41,8 @@
 import com.android.internal.telephony.gsm.GsmInboundSmsHandler;
 import com.android.internal.telephony.gsm.GsmSMSDispatcher;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
 
@@ -449,7 +451,7 @@
                             PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri,
                             String callingPkg, boolean persistMessage, int priority,
                             boolean expectMore, int validityPeriod) {
-        if (mImsSmsDispatcher.isAvailable()) {
+        if (mImsSmsDispatcher.isAvailable() || mImsSmsDispatcher.isEmergencySmsSupport(destAddr)) {
             mImsSmsDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
                     messageUri, callingPkg, persistMessage, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED,
                     false /*expectMore*/, SMS_MESSAGE_PERIOD_NOT_SPECIFIED);
@@ -627,4 +629,9 @@
     public interface SmsInjectionCallback {
         void onSmsInjectedResult(int result);
     }
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        mGsmInboundSmsHandler.dump(fd, pw, args);
+        mCdmaInboundSmsHandler.dump(fd, pw, args);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
index 1f81569..dc8d9fa 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -66,10 +66,18 @@
     }
 
     /**
+     * Sets the NitzStateMachine implementation to use during implementation. This boolean
+     * should be removed once the new implementation is stable.
+     */
+    static final boolean USE_NEW_NITZ_STATE_MACHINE = true;
+
+    /**
      * Returns a new {@link NitzStateMachine} instance.
      */
     public NitzStateMachine makeNitzStateMachine(GsmCdmaPhone phone) {
-        return new OldNitzStateMachine(phone);
+        return USE_NEW_NITZ_STATE_MACHINE
+                ? new NewNitzStateMachine(phone)
+                : new OldNitzStateMachine(phone);
     }
 
     public SimActivationTracker makeSimActivationTracker(Phone phone) {
@@ -104,8 +112,8 @@
      * Create a new UiccProfile object.
      */
     public UiccProfile makeUiccProfile(Context context, CommandsInterface ci, IccCardStatus ics,
-                                       int phoneId, UiccCard uiccCard) {
-        return new UiccProfile(context, ci, ics, phoneId, uiccCard);
+                                       int phoneId, UiccCard uiccCard, Object lock) {
+        return new UiccProfile(context, ci, ics, phoneId, uiccCard, lock);
     }
 
     public EriManager makeEriManager(Phone phone, Context context, int eriFileSource) {
diff --git a/src/java/com/android/internal/telephony/UiccSmsController.java b/src/java/com/android/internal/telephony/UiccSmsController.java
index 59449b8..b888c7f 100644
--- a/src/java/com/android/internal/telephony/UiccSmsController.java
+++ b/src/java/com/android/internal/telephony/UiccSmsController.java
@@ -24,7 +24,6 @@
 import android.content.Context;
 import android.net.Uri;
 import android.os.Binder;
-import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.provider.Telephony.Sms.Intents;
 import android.telephony.Rlog;
@@ -33,13 +32,17 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.List;
 
 /**
  * UiccSmsController to provide an inter-process communication to
  * access Sms in Icc.
  */
-public class UiccSmsController extends ISms.Stub {
+public class UiccSmsController extends ISmsBaseImpl {
     static final String LOG_TAG = "RIL_UiccSmsController";
 
     protected UiccSmsController() {
@@ -59,7 +62,7 @@
     @Override
     public boolean
     updateMessageOnIccEfForSubscriber(int subId, String callingPackage, int index, int status,
-                byte[] pdu) throws android.os.RemoteException {
+            byte[] pdu) {
         IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
         if (iccSmsIntMgr != null) {
             return iccSmsIntMgr.updateMessageOnIccEf(callingPackage, index, status, pdu);
@@ -72,7 +75,7 @@
 
     @Override
     public boolean copyMessageToIccEfForSubscriber(int subId, String callingPackage, int status,
-            byte[] pdu, byte[] smsc) throws android.os.RemoteException {
+            byte[] pdu, byte[] smsc) {
         IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
         if (iccSmsIntMgr != null) {
             return iccSmsIntMgr.copyMessageToIccEf(callingPackage, status, pdu, smsc);
@@ -84,8 +87,7 @@
     }
 
     @Override
-    public List<SmsRawData> getAllMessagesFromIccEfForSubscriber(int subId, String callingPackage)
-                throws android.os.RemoteException {
+    public List<SmsRawData> getAllMessagesFromIccEfForSubscriber(int subId, String callingPackage) {
         IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
         if (iccSmsIntMgr != null) {
             return iccSmsIntMgr.getAllMessagesFromIccEf(callingPackage);
@@ -176,7 +178,7 @@
 
     public void sendMultipartText(String callingPackage, String destAddr, String scAddr,
             List<String> parts, List<PendingIntent> sentIntents,
-            List<PendingIntent> deliveryIntents) throws android.os.RemoteException {
+            List<PendingIntent> deliveryIntents) {
          sendMultipartTextForSubscriber(getPreferredSmsSubscription(), callingPackage, destAddr,
                  scAddr, parts, sentIntents, deliveryIntents,
                  true /* persistMessageForNonDefaultSmsApp */);
@@ -185,8 +187,7 @@
     @Override
     public void sendMultipartTextForSubscriber(int subId, String callingPackage, String destAddr,
             String scAddr, List<String> parts, List<PendingIntent> sentIntents,
-            List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp)
-            throws android.os.RemoteException {
+            List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp) {
         IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
         if (iccSmsIntMgr != null ) {
             iccSmsIntMgr.sendMultipartText(callingPackage, destAddr, scAddr, parts, sentIntents,
@@ -215,15 +216,14 @@
     }
 
     @Override
-    public boolean enableCellBroadcastForSubscriber(int subId, int messageIdentifier, int ranType)
-                throws android.os.RemoteException {
+    public boolean enableCellBroadcastForSubscriber(int subId, int messageIdentifier, int ranType) {
         return enableCellBroadcastRangeForSubscriber(subId, messageIdentifier, messageIdentifier,
                 ranType);
     }
 
     @Override
     public boolean enableCellBroadcastRangeForSubscriber(int subId, int startMessageId,
-            int endMessageId, int ranType) throws android.os.RemoteException {
+            int endMessageId, int ranType) {
         IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
         if (iccSmsIntMgr != null ) {
             return iccSmsIntMgr.enableCellBroadcastRange(startMessageId, endMessageId, ranType);
@@ -235,15 +235,15 @@
     }
 
     @Override
-    public boolean disableCellBroadcastForSubscriber(int subId, int messageIdentifier, int ranType)
-                throws android.os.RemoteException {
+    public boolean disableCellBroadcastForSubscriber(int subId,
+            int messageIdentifier, int ranType) {
         return disableCellBroadcastRangeForSubscriber(subId, messageIdentifier, messageIdentifier,
                 ranType);
     }
 
     @Override
     public boolean disableCellBroadcastRangeForSubscriber(int subId, int startMessageId,
-            int endMessageId, int ranType) throws android.os.RemoteException {
+            int endMessageId, int ranType) {
         IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
         if (iccSmsIntMgr != null ) {
             return iccSmsIntMgr.disableCellBroadcastRange(startMessageId, endMessageId, ranType);
@@ -382,7 +382,7 @@
 
     @Override
     public void sendStoredText(int subId, String callingPkg, Uri messageUri, String scAddress,
-            PendingIntent sentIntent, PendingIntent deliveryIntent) throws RemoteException {
+            PendingIntent sentIntent, PendingIntent deliveryIntent) {
         IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
         if (iccSmsIntMgr != null) {
             iccSmsIntMgr.sendStoredText(callingPkg, messageUri, scAddress, sentIntent,
@@ -395,8 +395,8 @@
 
     @Override
     public void sendStoredMultipartText(int subId, String callingPkg, Uri messageUri,
-            String scAddress, List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents)
-            throws RemoteException {
+            String scAddress, List<PendingIntent> sentIntents,
+            List<PendingIntent> deliveryIntents) {
         IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
         if (iccSmsIntMgr != null ) {
             iccSmsIntMgr.sendStoredMultipartText(callingPkg, messageUri, scAddress, sentIntents,
@@ -413,6 +413,23 @@
         return getPhone(subId).getAppSmsManager().createAppSpecificSmsToken(callingPkg, intent);
     }
 
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        IndentingPrintWriter indentingPW =
+                new IndentingPrintWriter(pw, "    " /* singleIndent */);
+        for (Phone phone : PhoneFactory.getPhones()) {
+            int subId = phone.getSubId();
+            indentingPW.println(String.format("SmsManager for subId = %d:", subId));
+            indentingPW.increaseIndent();
+            if (getIccSmsInterfaceManager(subId) != null) {
+                getIccSmsInterfaceManager(subId).dump(fd, indentingPW, args);
+            }
+            indentingPW.decreaseIndent();
+        }
+        indentingPW.flush();
+    }
+
+
     private void sendErrorInPendingIntent(@Nullable PendingIntent intent, int errorCode) {
         if (intent != null) {
             try {
diff --git a/src/java/com/android/internal/telephony/cat/AppInterface.java b/src/java/com/android/internal/telephony/cat/AppInterface.java
index 1f2d3a0..9410b98 100755
--- a/src/java/com/android/internal/telephony/cat/AppInterface.java
+++ b/src/java/com/android/internal/telephony/cat/AppInterface.java
@@ -78,6 +78,7 @@
         SEND_SS(0x11),
         SEND_USSD(0x12),
         SEND_SMS(0x13),
+        RUN_AT(0x34),
         SEND_DTMF(0x14),
         SET_UP_EVENT_LIST(0x05),
         SET_UP_IDLE_MODE_TEXT(0x28),
diff --git a/src/java/com/android/internal/telephony/cat/CatCmdMessage.java b/src/java/com/android/internal/telephony/cat/CatCmdMessage.java
index 62d8869..cbad866 100644
--- a/src/java/com/android/internal/telephony/cat/CatCmdMessage.java
+++ b/src/java/com/android/internal/telephony/cat/CatCmdMessage.java
@@ -82,6 +82,8 @@
         case SET_UP_IDLE_MODE_TEXT:
         case SEND_DTMF:
         case SEND_SMS:
+        case REFRESH:
+        case RUN_AT:
         case SEND_SS:
         case SEND_USSD:
             mTextMsg = ((DisplayTextParams) cmdParams).mTextMsg;
@@ -121,7 +123,6 @@
             mSetupEventListSettings.eventList = ((SetEventListParams) cmdParams).mEventInfo;
             break;
         case PROVIDE_LOCAL_INFORMATION:
-        case REFRESH:
         default:
             break;
         }
diff --git a/src/java/com/android/internal/telephony/cat/CatService.java b/src/java/com/android/internal/telephony/cat/CatService.java
index e5f4b90..1084454 100644
--- a/src/java/com/android/internal/telephony/cat/CatService.java
+++ b/src/java/com/android/internal/telephony/cat/CatService.java
@@ -395,11 +395,6 @@
                 break;
             case DISPLAY_TEXT:
                 break;
-            case REFRESH:
-                // ME side only handles refresh commands which meant to remove IDLE
-                // MODE TEXT.
-                cmdParams.mCmdDet.typeOfCommand = CommandType.SET_UP_IDLE_MODE_TEXT.value();
-                break;
             case SET_UP_IDLE_MODE_TEXT:
                 resultCode = cmdParams.mLoadIconFailed ? ResultCode.PRFRMD_ICON_NOT_DISPLAYED
                                                                             : ResultCode.OK;
@@ -440,6 +435,13 @@
             case GET_INPUT:
             case GET_INKEY:
                 break;
+            case REFRESH:
+            case RUN_AT:
+                if (STK_DEFAULT.equals(((DisplayTextParams)cmdParams).mTextMsg.text)) {
+                    // Remove the default text which was temporarily added and shall not be shown
+                    ((DisplayTextParams)cmdParams).mTextMsg.text = null;
+                }
+                break;
             case SEND_DTMF:
             case SEND_SMS:
             case SEND_SS:
diff --git a/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java b/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
index 232f808..049e668 100644
--- a/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
+++ b/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
@@ -61,12 +61,6 @@
     static final int LOAD_SINGLE_ICON       = 1;
     static final int LOAD_MULTI_ICONS       = 2;
 
-    // Command Qualifier values for refresh command
-    static final int REFRESH_NAA_INIT_AND_FULL_FILE_CHANGE  = 0x00;
-    static final int REFRESH_NAA_INIT_AND_FILE_CHANGE       = 0x02;
-    static final int REFRESH_NAA_INIT                       = 0x03;
-    static final int REFRESH_UICC_RESET                     = 0x04;
-
     // Command Qualifier values for PLI command
     static final int DTTZ_SETTING                           = 0x03;
     static final int LANGUAGE_SETTING                       = 0x04;
@@ -188,6 +182,8 @@
                  break;
              case SEND_DTMF:
              case SEND_SMS:
+             case REFRESH:
+             case RUN_AT:
              case SEND_SS:
              case SEND_USSD:
                  cmdPending = processEventNotify(cmdDet, ctlvs);
@@ -196,10 +192,6 @@
              case SET_UP_CALL:
                  cmdPending = processSetupCall(cmdDet, ctlvs);
                  break;
-             case REFRESH:
-                processRefresh(cmdDet, ctlvs);
-                cmdPending = false;
-                break;
              case LAUNCH_BROWSER:
                  cmdPending = processLaunchBrowser(cmdDet, ctlvs);
                  break;
@@ -585,32 +577,6 @@
     }
 
     /**
-     * Processes REFRESH proactive command from the SIM card.
-     *
-     * @param cmdDet Command Details container object.
-     * @param ctlvs List of ComprehensionTlv objects following Command Details
-     *        object and Device Identities object within the proactive command
-     */
-    private boolean processRefresh(CommandDetails cmdDet,
-            List<ComprehensionTlv> ctlvs) {
-
-        CatLog.d(this, "process Refresh");
-
-        // REFRESH proactive command is rerouted by the baseband and handled by
-        // the telephony layer. IDLE TEXT should be removed for a REFRESH command
-        // with "initialization" or "reset"
-        switch (cmdDet.commandQualifier) {
-        case REFRESH_NAA_INIT_AND_FULL_FILE_CHANGE:
-        case REFRESH_NAA_INIT_AND_FILE_CHANGE:
-        case REFRESH_NAA_INIT:
-        case REFRESH_UICC_RESET:
-            mCmdParams = new DisplayTextParams(cmdDet, null);
-            break;
-        }
-        return false;
-    }
-
-    /**
      * Processes SELECT_ITEM proactive command from the SIM card.
      *
      * @param cmdDet Command Details container object.
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java b/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
index 5a40c4e..e2c178a 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
@@ -20,7 +20,6 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.os.Message;
-import android.os.SystemProperties;
 import android.provider.Telephony.Sms.Intents;
 import android.telephony.SmsCbMessage;
 
@@ -33,7 +32,6 @@
 import com.android.internal.telephony.SmsMessageBase;
 import com.android.internal.telephony.SmsStorageMonitor;
 import com.android.internal.telephony.TelephonyComponentFactory;
-import com.android.internal.telephony.TelephonyProperties;
 import com.android.internal.telephony.WspTypeDecoder;
 import com.android.internal.telephony.cdma.sms.SmsEnvelope;
 import com.android.internal.util.HexDump;
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index f000caa..bbaa3ca 100755
--- a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -16,12 +16,7 @@
 
 package com.android.internal.telephony.cdma;
 
-import android.app.Activity;
-import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
-import android.content.Intent;
 import android.os.Message;
-import android.provider.Telephony.Sms;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
@@ -73,8 +68,9 @@
     }
 
     @Override
-    protected boolean shouldBlockSms() {
-        return SMSDispatcherUtil.shouldBlockSms(isCdmaMo(), mPhone);
+    protected boolean shouldBlockSmsForEcbm() {
+        // We only block outgoing SMS during ECBM when using CDMA.
+        return mPhone.isInEcm() && isCdmaMo() && !isIms();
     }
 
     @Override
@@ -123,6 +119,7 @@
                 + " mRetryCount=" + tracker.mRetryCount
                 + " mImsRetry=" + tracker.mImsRetry
                 + " mMessageRef=" + tracker.mMessageRef
+                + " mUsesImsServiceForIms=" + tracker.mUsesImsServiceForIms
                 + " SS=" + mPhone.getServiceState().getState());
 
         int ss = mPhone.getServiceState().getState();
@@ -146,8 +143,11 @@
         // sms over cdma is used:
         //   if sms over IMS is not supported AND
         //   this is not a retry case after sms over IMS failed
-        //     indicated by mImsRetry > 0
-        if (0 == tracker.mImsRetry && !isIms() || imsSmsDisabled) {
+        //     indicated by mImsRetry > 0 OR
+        //   SMS over IMS is disabled because of the network type OR
+        //   SMS over IMS is being handled by the ImsSmsDispatcher implementation and has indicated
+        //   that the message should fall back to sending over CS.
+        if (0 == tracker.mImsRetry && !isIms() || imsSmsDisabled || tracker.mUsesImsServiceForIms) {
             mCi.sendCdmaSms(pdu, reply);
         } else {
             mCi.sendImsCdmaSms(pdu, tracker.mImsRetry, tracker.mMessageRef, reply);
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java b/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
index 1b42d4a..f068acd 100644
--- a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
+++ b/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
@@ -22,6 +22,8 @@
 import android.net.NetworkConfig;
 import android.net.NetworkRequest;
 import android.telephony.Rlog;
+import android.telephony.data.ApnSetting;
+import android.telephony.data.ApnSetting.ApnType;
 import android.text.TextUtils;
 import android.util.LocalLog;
 import android.util.SparseIntArray;
@@ -29,7 +31,6 @@
 import com.android.internal.R;
 import com.android.internal.telephony.DctConstants;
 import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.RetryManager;
 import com.android.internal.util.IndentingPrintWriter;
 
@@ -129,6 +130,14 @@
     }
 
     /**
+     * Gets the APN type bitmask.
+     * @return The APN type bitmask
+     */
+    public int getApnTypeBitmask() {
+        return ApnSetting.getApnTypesBitmaskFromString(mApnType);
+    }
+
+    /**
      * Get the data call async channel.
      * @return The data call async channel
      */
@@ -392,8 +401,8 @@
         String provisioningApn = mPhone.getContext().getResources()
                 .getString(R.string.mobile_provisioning_apn);
         if (!TextUtils.isEmpty(provisioningApn) &&
-                (mApnSetting != null) && (mApnSetting.apn != null)) {
-            return (mApnSetting.apn.equals(provisioningApn));
+                (mApnSetting != null) && (mApnSetting.getApnName() != null)) {
+            return (mApnSetting.getApnName().equals(provisioningApn));
         } else {
             return false;
         }
@@ -418,7 +427,7 @@
             } else {
                 mLocalLogs.add(log);
                 mNetworkRequests.add(networkRequest);
-                mDcTracker.setEnabled(apnIdForApnName(mApnType), true);
+                mDcTracker.setEnabled(ApnSetting.getApnTypesBitmaskFromString(mApnType), true);
             }
         }
     }
@@ -438,7 +447,7 @@
                 log.log("ApnContext.releaseNetwork left with " + mNetworkRequests.size() +
                         " requests.");
                 if (mNetworkRequests.size() == 0) {
-                    mDcTracker.setEnabled(apnIdForApnName(mApnType), false);
+                    mDcTracker.setEnabled(ApnSetting.getApnTypesBitmaskFromString(mApnType), false);
                 }
             }
         }
@@ -540,88 +549,78 @@
         return mRetryManager.getRetryAfterDisconnectDelay();
     }
 
-    public static int apnIdForType(int networkType) {
+    public static int getApnTypeFromNetworkType(int networkType) {
         switch (networkType) {
-        case ConnectivityManager.TYPE_MOBILE:
-            return DctConstants.APN_DEFAULT_ID;
-        case ConnectivityManager.TYPE_MOBILE_MMS:
-            return DctConstants.APN_MMS_ID;
-        case ConnectivityManager.TYPE_MOBILE_SUPL:
-            return DctConstants.APN_SUPL_ID;
-        case ConnectivityManager.TYPE_MOBILE_DUN:
-            return DctConstants.APN_DUN_ID;
-        case ConnectivityManager.TYPE_MOBILE_FOTA:
-            return DctConstants.APN_FOTA_ID;
-        case ConnectivityManager.TYPE_MOBILE_IMS:
-            return DctConstants.APN_IMS_ID;
-        case ConnectivityManager.TYPE_MOBILE_CBS:
-            return DctConstants.APN_CBS_ID;
-        case ConnectivityManager.TYPE_MOBILE_IA:
-            return DctConstants.APN_IA_ID;
-        case ConnectivityManager.TYPE_MOBILE_EMERGENCY:
-            return DctConstants.APN_EMERGENCY_ID;
-        default:
-            return DctConstants.APN_INVALID_ID;
+            case ConnectivityManager.TYPE_MOBILE:
+                return ApnSetting.TYPE_DEFAULT;
+            case ConnectivityManager.TYPE_MOBILE_MMS:
+                return ApnSetting.TYPE_MMS;
+            case ConnectivityManager.TYPE_MOBILE_SUPL:
+                return ApnSetting.TYPE_SUPL;
+            case ConnectivityManager.TYPE_MOBILE_DUN:
+                return ApnSetting.TYPE_DUN;
+            case ConnectivityManager.TYPE_MOBILE_FOTA:
+                return ApnSetting.TYPE_FOTA;
+            case ConnectivityManager.TYPE_MOBILE_IMS:
+                return ApnSetting.TYPE_IMS;
+            case ConnectivityManager.TYPE_MOBILE_CBS:
+                return ApnSetting.TYPE_CBS;
+            case ConnectivityManager.TYPE_MOBILE_IA:
+                return ApnSetting.TYPE_IA;
+            case ConnectivityManager.TYPE_MOBILE_EMERGENCY:
+                return ApnSetting.TYPE_EMERGENCY;
+            default:
+                return ApnSetting.TYPE_NONE;
         }
     }
 
-    public static int apnIdForNetworkRequest(NetworkRequest nr) {
+    static @ApnType int getApnTypeFromNetworkRequest(NetworkRequest nr) {
         NetworkCapabilities nc = nr.networkCapabilities;
         // For now, ignore the bandwidth stuff
         if (nc.getTransportTypes().length > 0 &&
                 nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == false) {
-            return DctConstants.APN_INVALID_ID;
+            return ApnSetting.TYPE_NONE;
         }
 
         // in the near term just do 1-1 matches.
         // TODO - actually try to match the set of capabilities
-        int apnId = DctConstants.APN_INVALID_ID;
+        int apnType = ApnSetting.TYPE_NONE;
         boolean error = false;
 
         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
-            apnId = DctConstants.APN_DEFAULT_ID;
+            apnType = ApnSetting.TYPE_DEFAULT;
         }
         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) {
-            if (apnId != DctConstants.APN_INVALID_ID) error = true;
-            apnId = DctConstants.APN_MMS_ID;
+            if (apnType != ApnSetting.TYPE_NONE) error = true;
+            apnType = ApnSetting.TYPE_MMS;
         }
         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
-            if (apnId != DctConstants.APN_INVALID_ID) error = true;
-            apnId = DctConstants.APN_SUPL_ID;
+            if (apnType != ApnSetting.TYPE_NONE) error = true;
+            apnType = ApnSetting.TYPE_SUPL;
         }
         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) {
-            if (apnId != DctConstants.APN_INVALID_ID) error = true;
-            apnId = DctConstants.APN_DUN_ID;
+            if (apnType != ApnSetting.TYPE_NONE) error = true;
+            apnType = ApnSetting.TYPE_DUN;
         }
         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) {
-            if (apnId != DctConstants.APN_INVALID_ID) error = true;
-            apnId = DctConstants.APN_FOTA_ID;
+            if (apnType != ApnSetting.TYPE_NONE) error = true;
+            apnType = ApnSetting.TYPE_FOTA;
         }
         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) {
-            if (apnId != DctConstants.APN_INVALID_ID) error = true;
-            apnId = DctConstants.APN_IMS_ID;
+            if (apnType != ApnSetting.TYPE_NONE) error = true;
+            apnType = ApnSetting.TYPE_IMS;
         }
         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
-            if (apnId != DctConstants.APN_INVALID_ID) error = true;
-            apnId = DctConstants.APN_CBS_ID;
+            if (apnType != ApnSetting.TYPE_NONE) error = true;
+            apnType = ApnSetting.TYPE_CBS;
         }
         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_IA)) {
-            if (apnId != DctConstants.APN_INVALID_ID) error = true;
-            apnId = DctConstants.APN_IA_ID;
-        }
-        if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_RCS)) {
-            if (apnId != DctConstants.APN_INVALID_ID) error = true;
-
-            Rlog.d(SLOG_TAG, "RCS APN type not yet supported");
-        }
-        if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_XCAP)) {
-            if (apnId != DctConstants.APN_INVALID_ID) error = true;
-
-            Rlog.d(SLOG_TAG, "XCAP APN type not yet supported");
+            if (apnType != ApnSetting.TYPE_NONE) error = true;
+            apnType = ApnSetting.TYPE_IA;
         }
         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)) {
-            if (apnId != DctConstants.APN_INVALID_ID) error = true;
-            apnId = DctConstants.APN_EMERGENCY_ID;
+            if (apnType != ApnSetting.TYPE_NONE) error = true;
+            apnType = ApnSetting.TYPE_EMERGENCY;
         }
         if (error) {
             // TODO: If this error condition is removed, the framework's handling of
@@ -630,66 +629,10 @@
             // NetworkCapabilities.maybeMarkCapabilitiesRestricted currently works.
             Rlog.d(SLOG_TAG, "Multiple apn types specified in request - result is unspecified!");
         }
-        if (apnId == DctConstants.APN_INVALID_ID) {
+        if (apnType == ApnSetting.TYPE_NONE) {
             Rlog.d(SLOG_TAG, "Unsupported NetworkRequest in Telephony: nr=" + nr);
         }
-        return apnId;
-    }
-
-    // TODO - kill The use of these strings
-    public static int apnIdForApnName(String type) {
-        switch (type) {
-            case PhoneConstants.APN_TYPE_DEFAULT:
-                return DctConstants.APN_DEFAULT_ID;
-            case PhoneConstants.APN_TYPE_MMS:
-                return DctConstants.APN_MMS_ID;
-            case PhoneConstants.APN_TYPE_SUPL:
-                return DctConstants.APN_SUPL_ID;
-            case PhoneConstants.APN_TYPE_DUN:
-                return DctConstants.APN_DUN_ID;
-            case PhoneConstants.APN_TYPE_HIPRI:
-                return DctConstants.APN_HIPRI_ID;
-            case PhoneConstants.APN_TYPE_IMS:
-                return DctConstants.APN_IMS_ID;
-            case PhoneConstants.APN_TYPE_FOTA:
-                return DctConstants.APN_FOTA_ID;
-            case PhoneConstants.APN_TYPE_CBS:
-                return DctConstants.APN_CBS_ID;
-            case PhoneConstants.APN_TYPE_IA:
-                return DctConstants.APN_IA_ID;
-            case PhoneConstants.APN_TYPE_EMERGENCY:
-                return DctConstants.APN_EMERGENCY_ID;
-            default:
-                return DctConstants.APN_INVALID_ID;
-        }
-    }
-
-    private static String apnNameForApnId(int id) {
-        switch (id) {
-            case DctConstants.APN_DEFAULT_ID:
-                return PhoneConstants.APN_TYPE_DEFAULT;
-            case DctConstants.APN_MMS_ID:
-                return PhoneConstants.APN_TYPE_MMS;
-            case DctConstants.APN_SUPL_ID:
-                return PhoneConstants.APN_TYPE_SUPL;
-            case DctConstants.APN_DUN_ID:
-                return PhoneConstants.APN_TYPE_DUN;
-            case DctConstants.APN_HIPRI_ID:
-                return PhoneConstants.APN_TYPE_HIPRI;
-            case DctConstants.APN_IMS_ID:
-                return PhoneConstants.APN_TYPE_IMS;
-            case DctConstants.APN_FOTA_ID:
-                return PhoneConstants.APN_TYPE_FOTA;
-            case DctConstants.APN_CBS_ID:
-                return PhoneConstants.APN_TYPE_CBS;
-            case DctConstants.APN_IA_ID:
-                return PhoneConstants.APN_TYPE_IA;
-            case DctConstants.APN_EMERGENCY_ID:
-                return PhoneConstants.APN_TYPE_EMERGENCY;
-            default:
-                Rlog.d(SLOG_TAG, "Unknown id (" + id + ") in apnIdToType");
-                return PhoneConstants.APN_TYPE_DEFAULT;
-        }
+        return apnType;
     }
 
     @Override
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java b/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java
deleted file mode 100644
index 6f1c8a6..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java
+++ /dev/null
@@ -1,832 +0,0 @@
-/*
- * Copyright (C) 2006 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 android.content.Context;
-import android.hardware.radio.V1_0.ApnTypes;
-import android.os.PersistableBundle;
-import android.provider.Telephony.Carriers;
-import android.telephony.CarrierConfigManager;
-import android.telephony.Rlog;
-import android.telephony.ServiceState;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-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;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * This class represents a apn setting for create PDP link
- */
-public class ApnSetting {
-
-    static final String LOG_TAG = "ApnSetting";
-
-    private static final boolean DBG = false;
-    private static final boolean VDBG = false;
-
-    static final String V2_FORMAT_REGEX = "^\\[ApnSettingV2\\]\\s*";
-    static final String V3_FORMAT_REGEX = "^\\[ApnSettingV3\\]\\s*";
-    static final String V4_FORMAT_REGEX = "^\\[ApnSettingV4\\]\\s*";
-    static final String V5_FORMAT_REGEX = "^\\[ApnSettingV5\\]\\s*";
-    static final String TAG = "ApnSetting";
-
-    public final String carrier;
-    public final String apn;
-    public final String proxy;
-    public final String port;
-    public final String mmsc;
-    public final String mmsProxy;
-    public final String mmsPort;
-    public final String user;
-    public final String password;
-    public final int authType;
-    public final String[] types;
-    public final int typesBitmap;
-    public final int id;
-    public final String numeric;
-    public final String protocol;
-    public final String roamingProtocol;
-    public final int mtu;
-
-    /**
-      * Current status of APN
-      * true : enabled APN, false : disabled APN.
-      */
-    public final boolean carrierEnabled;
-    /**
-     * Radio Access Technology info
-     * To check what values can hold, refer to ServiceState.java.
-     * This should be spread to other technologies,
-     * but currently only used for LTE(14) and EHRPD(13).
-     *
-     * @deprecated use {@code networkTypeBitmask} instead
-     */
-    @Deprecated
-    private final int bearer;
-    /**
-      * Radio Access Technology info
-      * To check what values can hold, refer to ServiceState.java. This is a bitmask of radio
-      * technologies in ServiceState.
-      * This should be spread to other technologies,
-      * but currently only used for LTE(14) and EHRPD(13).
-      *
-      * @deprecated use {@code networkTypeBitmask} instead
-      */
-    @Deprecated
-    public final int bearerBitmask;
-
-    /**
-     * Radio Technology (Network Type) info
-     * To check what values can hold, refer to TelephonyManager.java. This is a bitmask of radio
-     * technologies ({@code NETWORK_TYPE_} constants) in {@link TelephonyManager}.
-     */
-    public final int networkTypeBitmask;
-
-    /* ID of the profile in the modem */
-    public final int profileId;
-    public final boolean modemCognitive;
-    public final int maxConns;
-    public final int waitTime;
-    public final int maxConnsTime;
-
-    /**
-      * MVNO match type. Possible values:
-      *   "spn": Service provider name.
-      *   "imsi": IMSI.
-      *   "gid": Group identifier level 1.
-      *   "iccid": ICCID
-      */
-    public final String mvnoType;
-    /**
-      * MVNO data. Examples:
-      *   "spn": A MOBILE, BEN NL
-      *   "imsi": 302720x94, 2060188
-      *   "gid": 4E, 33
-      *   "iccid": 898603 etc.
-      */
-    public final String mvnoMatchData;
-
-    /**
-     * The APN set id.
-     *
-     * APNs that are part of the same set should be preferred together, e.g. if the
-     * user selects a default APN with apnSetId=1, then we will prefer all APNs with apnSetId=1.
-     *
-     * If the apnSetId=Carriers.NO_SET_SET (=0) then the APN is not part of a set.
-     */
-    public final int apnSetId;
-
-    /**
-     * Indicates this APN setting is permanently failed and cannot be
-     * retried by the retry manager anymore.
-     * */
-    public boolean permanentFailed = false;
-
-    /**
-     * @deprecated this constructor is no longer supported. Use the other constructor which takes
-     * a network type bitmask instead of the deprecated bearer bitmask and bearer field.
-     * */
-    @Deprecated
-    public ApnSetting(int id, String numeric, String carrier, String apn,
-                      String proxy, String port,
-                      String mmsc, String mmsProxy, String mmsPort,
-                      String user, String password, int authType, String[] types,
-                      String protocol, String roamingProtocol, boolean carrierEnabled, int bearer,
-                      int bearerBitmask, int profileId, boolean modemCognitive, int maxConns,
-                      int waitTime, int maxConnsTime, int mtu, String mvnoType,
-                      String mvnoMatchData) {
-        this.id = id;
-        this.numeric = numeric;
-        this.carrier = carrier;
-        this.apn = apn;
-        this.proxy = proxy;
-        this.port = port;
-        this.mmsc = mmsc;
-        this.mmsProxy = mmsProxy;
-        this.mmsPort = mmsPort;
-        this.user = user;
-        this.password = password;
-        this.authType = authType;
-        this.types = new String[types.length];
-        int apnBitmap = 0;
-        for (int i = 0; i < types.length; i++) {
-            this.types[i] = types[i].toLowerCase();
-            apnBitmap |= getApnBitmask(this.types[i]);
-        }
-        this.typesBitmap = apnBitmap;
-        this.protocol = protocol;
-        this.roamingProtocol = roamingProtocol;
-        this.carrierEnabled = carrierEnabled;
-        this.bearer = bearer;
-        this.bearerBitmask = (bearerBitmask | ServiceState.getBitmaskForTech(bearer));
-        this.profileId = profileId;
-        this.modemCognitive = modemCognitive;
-        this.maxConns = maxConns;
-        this.waitTime = waitTime;
-        this.maxConnsTime = maxConnsTime;
-        this.mtu = mtu;
-        this.mvnoType = mvnoType;
-        this.mvnoMatchData = mvnoMatchData;
-        this.apnSetId = Carriers.NO_SET_SET;
-        this.networkTypeBitmask = ServiceState.convertBearerBitmaskToNetworkTypeBitmask(
-                this.bearerBitmask);
-    }
-
-    // Constructor with default apn set id
-    public ApnSetting(int id, String numeric, String carrier, String apn,
-                      String proxy, String port,
-                      String mmsc, String mmsProxy, String mmsPort,
-                      String user, String password, int authType, String[] types,
-                      String protocol, String roamingProtocol, boolean carrierEnabled,
-                      int networkTypeBitmask, int profileId, boolean modemCognitive, int maxConns,
-                      int waitTime, int maxConnsTime, int mtu, String mvnoType,
-                      String mvnoMatchData) {
-        this(id, numeric, carrier, apn, proxy, port, mmsc, mmsProxy, mmsPort, user, password,
-                authType, types, protocol, roamingProtocol, carrierEnabled, networkTypeBitmask,
-                profileId, modemCognitive, maxConns, waitTime, maxConnsTime, mtu, mvnoType,
-                mvnoMatchData, Carriers.NO_SET_SET);
-    }
-
-    public ApnSetting(int id, String numeric, String carrier, String apn,
-                      String proxy, String port,
-                      String mmsc, String mmsProxy, String mmsPort,
-                      String user, String password, int authType, String[] types,
-                      String protocol, String roamingProtocol, boolean carrierEnabled,
-                      int networkTypeBitmask, int profileId, boolean modemCognitive, int maxConns,
-                      int waitTime, int maxConnsTime, int mtu, String mvnoType,
-                      String mvnoMatchData, int apnSetId) {
-        this.id = id;
-        this.numeric = numeric;
-        this.carrier = carrier;
-        this.apn = apn;
-        this.proxy = proxy;
-        this.port = port;
-        this.mmsc = mmsc;
-        this.mmsProxy = mmsProxy;
-        this.mmsPort = mmsPort;
-        this.user = user;
-        this.password = password;
-        this.authType = authType;
-        this.types = new String[types.length];
-        int apnBitmap = 0;
-        for (int i = 0; i < types.length; i++) {
-            this.types[i] = types[i].toLowerCase();
-            apnBitmap |= getApnBitmask(this.types[i]);
-        }
-        this.typesBitmap = apnBitmap;
-        this.protocol = protocol;
-        this.roamingProtocol = roamingProtocol;
-        this.carrierEnabled = carrierEnabled;
-        this.bearer = 0;
-        this.bearerBitmask =
-                ServiceState.convertNetworkTypeBitmaskToBearerBitmask(networkTypeBitmask);
-        this.networkTypeBitmask = networkTypeBitmask;
-        this.profileId = profileId;
-        this.modemCognitive = modemCognitive;
-        this.maxConns = maxConns;
-        this.waitTime = waitTime;
-        this.maxConnsTime = maxConnsTime;
-        this.mtu = mtu;
-        this.mvnoType = mvnoType;
-        this.mvnoMatchData = mvnoMatchData;
-        this.apnSetId = apnSetId;
-    }
-
-    public ApnSetting(ApnSetting apn) {
-        this(apn.id, apn.numeric, apn.carrier, apn.apn, apn.proxy, apn.port, apn.mmsc, apn.mmsProxy,
-                apn.mmsPort, apn.user, apn.password, apn.authType, apn.types, apn.protocol,
-                apn.roamingProtocol, apn.carrierEnabled, apn.networkTypeBitmask, apn.profileId,
-                apn.modemCognitive, apn.maxConns, apn.waitTime, apn.maxConnsTime,
-                apn.mtu, apn.mvnoType, apn.mvnoMatchData, apn.apnSetId);
-    }
-
-    /**
-     * Creates an ApnSetting object from a string.
-     *
-     * @param data the string to read.
-     *
-     * The string must be in one of two formats (newlines added for clarity,
-     * spaces are optional):
-     *
-     * v1 format:
-     *   <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>,
-     *   <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>,
-     *   <type>[| <type>...],
-     *
-     * v2 format:
-     *   [ApnSettingV2] <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>,
-     *   <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>,
-     *   <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearerBitmask>,
-     *
-     * v3 format:
-     *   [ApnSettingV3] <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>,
-     *   <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>,
-     *   <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearerBitmask>,
-     *   <profileId>, <modemCognitive>, <maxConns>, <waitTime>, <maxConnsTime>, <mtu>,
-     *   <mvnoType>, <mvnoMatchData>
-     *
-     * v4 format:
-     *   [ApnSettingV4] <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>,
-     *   <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>,
-     *   <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearerBitmask>,
-     *   <profileId>, <modemCognitive>, <maxConns>, <waitTime>, <maxConnsTime>, <mtu>,
-     *   <mvnoType>, <mvnoMatchData>, <networkTypeBitmask>
-     *
-     * v5 format:
-     *   [ApnSettingV5] <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>,
-     *   <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>,
-     *   <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearerBitmask>,
-     *   <profileId>, <modemCognitive>, <maxConns>, <waitTime>, <maxConnsTime>, <mtu>,
-     *   <mvnoType>, <mvnoMatchData>, <networkTypeBitmask>, <apnSetId>
-     *
-     * Note that the strings generated by toString() do not contain the username
-     * and password and thus cannot be read by this method.
-     */
-    public static ApnSetting fromString(String data) {
-        if (data == null) return null;
-
-        int version;
-        // matches() operates on the whole string, so append .* to the regex.
-        if (data.matches(V5_FORMAT_REGEX + ".*")) {
-            version = 5;
-            data = data.replaceFirst(V5_FORMAT_REGEX, "");
-        } else if (data.matches(V4_FORMAT_REGEX + ".*")) {
-            version = 4;
-            data = data.replaceFirst(V4_FORMAT_REGEX, "");
-        } else if (data.matches(V3_FORMAT_REGEX + ".*")) {
-            version = 3;
-            data = data.replaceFirst(V3_FORMAT_REGEX, "");
-        } else if (data.matches(V2_FORMAT_REGEX + ".*")) {
-            version = 2;
-            data = data.replaceFirst(V2_FORMAT_REGEX, "");
-        } else {
-            version = 1;
-        }
-
-        String[] a = data.split("\\s*,\\s*");
-        if (a.length < 14) {
-            return null;
-        }
-
-        int authType;
-        try {
-            authType = Integer.parseInt(a[12]);
-        } catch (NumberFormatException e) {
-            authType = 0;
-        }
-
-        String[] typeArray;
-        String protocol, roamingProtocol;
-        boolean carrierEnabled;
-        int bearerBitmask = 0;
-        int networkTypeBitmask = 0;
-        int profileId = 0;
-        boolean modemCognitive = false;
-        int maxConns = 0;
-        int waitTime = 0;
-        int maxConnsTime = 0;
-        int mtu = PhoneConstants.UNSET_MTU;
-        String mvnoType = "";
-        String mvnoMatchData = "";
-        int apnSetId = Carriers.NO_SET_SET;
-        if (version == 1) {
-            typeArray = new String[a.length - 13];
-            System.arraycopy(a, 13, typeArray, 0, a.length - 13);
-            protocol = RILConstants.SETUP_DATA_PROTOCOL_IP;
-            roamingProtocol = RILConstants.SETUP_DATA_PROTOCOL_IP;
-            carrierEnabled = true;
-        } else {
-            if (a.length < 18) {
-                return null;
-            }
-            typeArray = a[13].split("\\s*\\|\\s*");
-            protocol = a[14];
-            roamingProtocol = a[15];
-            carrierEnabled = Boolean.parseBoolean(a[16]);
-
-            bearerBitmask = ServiceState.getBitmaskFromString(a[17]);
-
-            if (a.length > 22) {
-                modemCognitive = Boolean.parseBoolean(a[19]);
-                try {
-                    profileId = Integer.parseInt(a[18]);
-                    maxConns = Integer.parseInt(a[20]);
-                    waitTime = Integer.parseInt(a[21]);
-                    maxConnsTime = Integer.parseInt(a[22]);
-                } catch (NumberFormatException e) {
-                }
-            }
-            if (a.length > 23) {
-                try {
-                    mtu = Integer.parseInt(a[23]);
-                } catch (NumberFormatException e) {
-                }
-            }
-            if (a.length > 25) {
-                mvnoType = a[24];
-                mvnoMatchData = a[25];
-            }
-            if (a.length > 26) {
-                networkTypeBitmask = ServiceState.getBitmaskFromString(a[26]);
-            }
-            if (a.length > 27) {
-                apnSetId = Integer.parseInt(a[27]);
-            }
-        }
-
-        // If both bearerBitmask and networkTypeBitmask were specified, bearerBitmask would be
-        // ignored.
-        if (networkTypeBitmask == 0) {
-            networkTypeBitmask =
-                    ServiceState.convertBearerBitmaskToNetworkTypeBitmask(bearerBitmask);
-        }
-        return new ApnSetting(-1, a[10] + a[11], a[0], a[1], a[2], a[3], a[7], a[8], a[9], a[4],
-                a[5], authType, typeArray, protocol, roamingProtocol, carrierEnabled,
-                networkTypeBitmask, profileId, modemCognitive, maxConns, waitTime, maxConnsTime,
-                mtu, mvnoType, mvnoMatchData, apnSetId);
-    }
-
-    /**
-     * Creates an array of ApnSetting objects from a string.
-     *
-     * @param data the string to read.
-     *
-     * Builds on top of the same format used by fromString, but allows for multiple entries
-     * separated by "; ".
-     */
-    public static List<ApnSetting> arrayFromString(String data) {
-        List<ApnSetting> retVal = new ArrayList<ApnSetting>();
-        if (TextUtils.isEmpty(data)) {
-            return retVal;
-        }
-        String[] apnStrings = data.split("\\s*;\\s*");
-        for (String apnString : apnStrings) {
-            ApnSetting apn = fromString(apnString);
-            if (apn != null) {
-                retVal.add(apn);
-            }
-        }
-        return retVal;
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        sb.append("[ApnSettingV5] ")
-        .append(carrier)
-        .append(", ").append(id)
-        .append(", ").append(numeric)
-        .append(", ").append(apn)
-        .append(", ").append(proxy)
-        .append(", ").append(mmsc)
-        .append(", ").append(mmsProxy)
-        .append(", ").append(mmsPort)
-        .append(", ").append(port)
-        .append(", ").append(authType).append(", ");
-        for (int i = 0; i < types.length; i++) {
-            sb.append(types[i]);
-            if (i < types.length - 1) {
-                sb.append(" | ");
-            }
-        }
-        sb.append(", ").append(protocol);
-        sb.append(", ").append(roamingProtocol);
-        sb.append(", ").append(carrierEnabled);
-        sb.append(", ").append(bearer);
-        sb.append(", ").append(bearerBitmask);
-        sb.append(", ").append(profileId);
-        sb.append(", ").append(modemCognitive);
-        sb.append(", ").append(maxConns);
-        sb.append(", ").append(waitTime);
-        sb.append(", ").append(maxConnsTime);
-        sb.append(", ").append(mtu);
-        sb.append(", ").append(mvnoType);
-        sb.append(", ").append(mvnoMatchData);
-        sb.append(", ").append(permanentFailed);
-        sb.append(", ").append(networkTypeBitmask);
-        sb.append(", ").append(apnSetId);
-        return sb.toString();
-    }
-
-    /**
-     * Returns true if there are MVNO params specified.
-     */
-    public boolean hasMvnoParams() {
-        return !TextUtils.isEmpty(mvnoType) && !TextUtils.isEmpty(mvnoMatchData);
-    }
-
-    public boolean canHandleType(String type) {
-        if (!carrierEnabled) return false;
-        boolean wildcardable = true;
-        if (PhoneConstants.APN_TYPE_IA.equalsIgnoreCase(type)) wildcardable = false;
-        for (String t : types) {
-            // DEFAULT handles all, and HIPRI is handled by DEFAULT
-            if (t.equalsIgnoreCase(type) ||
-                    (wildcardable && t.equalsIgnoreCase(PhoneConstants.APN_TYPE_ALL)) ||
-                    (t.equalsIgnoreCase(PhoneConstants.APN_TYPE_DEFAULT) &&
-                    type.equalsIgnoreCase(PhoneConstants.APN_TYPE_HIPRI))) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private static boolean iccidMatches(String mvnoData, String iccId) {
-        String[] mvnoIccidList = mvnoData.split(",");
-        for (String mvnoIccid : mvnoIccidList) {
-            if (iccId.startsWith(mvnoIccid)) {
-                Log.d(TAG, "mvno icc id match found");
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private static boolean imsiMatches(String imsiDB, String imsiSIM) {
-        // Note: imsiDB value has digit number or 'x' character for seperating USIM information
-        // for MVNO operator. And then digit number is matched at same order and 'x' character
-        // could replace by any digit number.
-        // ex) if imsiDB inserted '310260x10xxxxxx' for GG Operator,
-        //     that means first 6 digits, 8th and 9th digit
-        //     should be set in USIM for GG Operator.
-        int len = imsiDB.length();
-        int idxCompare = 0;
-
-        if (len <= 0) return false;
-        if (len > imsiSIM.length()) return false;
-
-        for (int idx=0; idx<len; idx++) {
-            char c = imsiDB.charAt(idx);
-            if ((c == 'x') || (c == 'X') || (c == imsiSIM.charAt(idx))) {
-                continue;
-            } else {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    public static boolean mvnoMatches(IccRecords r, String mvnoType, String mvnoMatchData) {
-        if (mvnoType.equalsIgnoreCase("spn")) {
-            if ((r.getServiceProviderName() != null) &&
-                    r.getServiceProviderName().equalsIgnoreCase(mvnoMatchData)) {
-                return true;
-            }
-        } else if (mvnoType.equalsIgnoreCase("imsi")) {
-            String imsiSIM = r.getIMSI();
-            if ((imsiSIM != null) && imsiMatches(mvnoMatchData, imsiSIM)) {
-                return true;
-            }
-        } else if (mvnoType.equalsIgnoreCase("gid")) {
-            String gid1 = r.getGid1();
-            int mvno_match_data_length = mvnoMatchData.length();
-            if ((gid1 != null) && (gid1.length() >= mvno_match_data_length) &&
-                    gid1.substring(0, mvno_match_data_length).equalsIgnoreCase(mvnoMatchData)) {
-                return true;
-            }
-        } else if (mvnoType.equalsIgnoreCase("iccid")) {
-            String iccId = r.getIccId();
-            if ((iccId != null) && iccidMatches(mvnoMatchData, iccId)) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * 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;
-        }
-
-        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)
-                phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        if (configManager == null) {
-            Rlog.e(LOG_TAG, "Carrier config service is not available");
-            return true;
-        }
-
-        PersistableBundle b = configManager.getConfigForSubId(subId);
-        if (b == null) {
-            Rlog.e(LOG_TAG, "Can't get the config. subId = " + subId);
-            return true;
-        }
-
-        String[] meteredApnTypes = b.getStringArray(carrierConfig);
-        if (meteredApnTypes == null) {
-            Rlog.e(LOG_TAG, carrierConfig +  " is not available. " + "subId = " + subId);
-            return true;
-        }
-
-        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()));
-        }
-
-        // 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.");
-            return true;
-        }
-
-        if (meteredApnSet.contains(type)) {
-            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.");
-                return true;
-            }
-        }
-
-        if (DBG) Rlog.d(LOG_TAG, type + " is not metered.");
-        return false;
-    }
-
-    /**
-     * 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, phone)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    // TODO - if we have this function we should also have hashCode.
-    // Also should handle changes in type order and perhaps case-insensitivity
-    @Override
-    public boolean equals(Object o) {
-        if (o instanceof ApnSetting == false) {
-            return false;
-        }
-
-        ApnSetting other = (ApnSetting) o;
-
-        return carrier.equals(other.carrier)
-                && id == other.id
-                && numeric.equals(other.numeric)
-                && apn.equals(other.apn)
-                && proxy.equals(other.proxy)
-                && mmsc.equals(other.mmsc)
-                && mmsProxy.equals(other.mmsProxy)
-                && TextUtils.equals(mmsPort, other.mmsPort)
-                && port.equals(other.port)
-                && TextUtils.equals(user, other.user)
-                && TextUtils.equals(password, other.password)
-                && authType == other.authType
-                && Arrays.deepEquals(types, other.types)
-                && typesBitmap == other.typesBitmap
-                && protocol.equals(other.protocol)
-                && roamingProtocol.equals(other.roamingProtocol)
-                && carrierEnabled == other.carrierEnabled
-                && bearer == other.bearer
-                && bearerBitmask == other.bearerBitmask
-                && profileId == other.profileId
-                && modemCognitive == other.modemCognitive
-                && maxConns == other.maxConns
-                && waitTime == other.waitTime
-                && maxConnsTime == other.maxConnsTime
-                && mtu == other.mtu
-                && mvnoType.equals(other.mvnoType)
-                && mvnoMatchData.equals(other.mvnoMatchData)
-                && networkTypeBitmask == other.networkTypeBitmask
-                && apnSetId == other.apnSetId;
-    }
-
-    /**
-     * Compare two APN settings
-     *
-     * Note: This method does not compare 'id', 'bearer', 'bearerBitmask', 'networkTypeBitmask'.
-     * We only use this for determining if tearing a data call is needed when conditions change. See
-     * cleanUpConnectionsOnUpdatedApns in DcTracker.
-     *
-     * @param o the other object to compare
-     * @param isDataRoaming True if the device is on data roaming
-     * @return True if the two APN settings are same
-     */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public boolean equals(Object o, boolean isDataRoaming) {
-        if (!(o instanceof ApnSetting)) {
-            return false;
-        }
-
-        ApnSetting other = (ApnSetting) o;
-
-        return carrier.equals(other.carrier)
-                && numeric.equals(other.numeric)
-                && apn.equals(other.apn)
-                && proxy.equals(other.proxy)
-                && mmsc.equals(other.mmsc)
-                && mmsProxy.equals(other.mmsProxy)
-                && TextUtils.equals(mmsPort, other.mmsPort)
-                && port.equals(other.port)
-                && TextUtils.equals(user, other.user)
-                && TextUtils.equals(password, other.password)
-                && authType == other.authType
-                && Arrays.deepEquals(types, other.types)
-                && typesBitmap == other.typesBitmap
-                && (isDataRoaming || protocol.equals(other.protocol))
-                && (!isDataRoaming || roamingProtocol.equals(other.roamingProtocol))
-                && carrierEnabled == other.carrierEnabled
-                && profileId == other.profileId
-                && modemCognitive == other.modemCognitive
-                && maxConns == other.maxConns
-                && waitTime == other.waitTime
-                && maxConnsTime == other.maxConnsTime
-                && mtu == other.mtu
-                && mvnoType.equals(other.mvnoType)
-                && mvnoMatchData.equals(other.mvnoMatchData)
-                && apnSetId == other.apnSetId;
-    }
-
-    /**
-     * Check if neither mention DUN and are substantially similar
-     *
-     * @param other The other APN settings to compare
-     * @return True if two APN settings are similar
-     */
-    public boolean similar(ApnSetting other) {
-        return (!this.canHandleType(PhoneConstants.APN_TYPE_DUN)
-                && !other.canHandleType(PhoneConstants.APN_TYPE_DUN)
-                && Objects.equals(this.apn, other.apn)
-                && !typeSameAny(this, other)
-                && xorEquals(this.proxy, other.proxy)
-                && xorEquals(this.port, other.port)
-                && xorEquals(this.protocol, other.protocol)
-                && xorEquals(this.roamingProtocol, other.roamingProtocol)
-                && this.carrierEnabled == other.carrierEnabled
-                && this.bearerBitmask == other.bearerBitmask
-                && this.profileId == other.profileId
-                && Objects.equals(this.mvnoType, other.mvnoType)
-                && Objects.equals(this.mvnoMatchData, other.mvnoMatchData)
-                && xorEquals(this.mmsc, other.mmsc)
-                && xorEquals(this.mmsProxy, other.mmsProxy)
-                && xorEquals(this.mmsPort, other.mmsPort))
-                && this.networkTypeBitmask == other.networkTypeBitmask
-                && this.apnSetId == other.apnSetId;
-    }
-
-    // check whether the types of two APN same (even only one type of each APN is same)
-    private boolean typeSameAny(ApnSetting first, ApnSetting second) {
-        if (VDBG) {
-            StringBuilder apnType1 = new StringBuilder(first.apn + ": ");
-            for (int index1 = 0; index1 < first.types.length; index1++) {
-                apnType1.append(first.types[index1]);
-                apnType1.append(",");
-            }
-
-            StringBuilder apnType2 = new StringBuilder(second.apn + ": ");
-            for (int index1 = 0; index1 < second.types.length; index1++) {
-                apnType2.append(second.types[index1]);
-                apnType2.append(",");
-            }
-            Rlog.d(LOG_TAG, "APN1: is " + apnType1);
-            Rlog.d(LOG_TAG, "APN2: is " + apnType2);
-        }
-
-        for (int index1 = 0; index1 < first.types.length; index1++) {
-            for (int index2 = 0; index2 < second.types.length; index2++) {
-                if (first.types[index1].equals(PhoneConstants.APN_TYPE_ALL)
-                        || second.types[index2].equals(PhoneConstants.APN_TYPE_ALL)
-                        || first.types[index1].equals(second.types[index2])) {
-                    if (VDBG) Rlog.d(LOG_TAG, "typeSameAny: return true");
-                    return true;
-                }
-            }
-        }
-
-        if (VDBG) Rlog.d(LOG_TAG, "typeSameAny: return false");
-        return false;
-    }
-
-    // equal or one is not specified
-    private boolean xorEquals(String first, String second) {
-        return (Objects.equals(first, second)
-                || TextUtils.isEmpty(first)
-                || TextUtils.isEmpty(second));
-    }
-
-    // Helper function to convert APN string into a 32-bit bitmask.
-    private static int getApnBitmask(String apn) {
-        switch (apn) {
-            case PhoneConstants.APN_TYPE_DEFAULT: return ApnTypes.DEFAULT;
-            case PhoneConstants.APN_TYPE_MMS: return ApnTypes.MMS;
-            case PhoneConstants.APN_TYPE_SUPL: return ApnTypes.SUPL;
-            case PhoneConstants.APN_TYPE_DUN: return ApnTypes.DUN;
-            case PhoneConstants.APN_TYPE_HIPRI: return ApnTypes.HIPRI;
-            case PhoneConstants.APN_TYPE_FOTA: return ApnTypes.FOTA;
-            case PhoneConstants.APN_TYPE_IMS: return ApnTypes.IMS;
-            case PhoneConstants.APN_TYPE_CBS: return ApnTypes.CBS;
-            case PhoneConstants.APN_TYPE_IA: return ApnTypes.IA;
-            case PhoneConstants.APN_TYPE_EMERGENCY: return ApnTypes.EMERGENCY;
-            case PhoneConstants.APN_TYPE_ALL: return ApnTypes.ALL;
-            default: return ApnTypes.NONE;
-        }
-    }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnSettingUtils.java b/src/java/com/android/internal/telephony/dataconnection/ApnSettingUtils.java
new file mode 100644
index 0000000..e9a6fc8
--- /dev/null
+++ b/src/java/com/android/internal/telephony/dataconnection/ApnSettingUtils.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2018 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 android.content.Context;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.Rlog;
+import android.telephony.ServiceState;
+import android.telephony.data.ApnSetting;
+import android.util.Log;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.uicc.IccRecords;
+
+import java.util.Arrays;
+import java.util.HashSet;
+
+/**
+ * This class represents a apn setting for create PDP link
+ */
+public class ApnSettingUtils {
+
+    static final String LOG_TAG = "ApnSetting";
+
+    private static final boolean DBG = false;
+
+    private static boolean iccidMatches(String mvnoData, String iccId) {
+        String[] mvnoIccidList = mvnoData.split(",");
+        for (String mvnoIccid : mvnoIccidList) {
+            if (iccId.startsWith(mvnoIccid)) {
+                Log.d(LOG_TAG, "mvno icc id match found");
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean imsiMatches(String imsiDB, String imsiSIM) {
+        // Note: imsiDB value has digit number or 'x' character for seperating USIM information
+        // for MVNO operator. And then digit number is matched at same order and 'x' character
+        // could replace by any digit number.
+        // ex) if imsiDB inserted '310260x10xxxxxx' for GG Operator,
+        //     that means first 6 digits, 8th and 9th digit
+        //     should be set in USIM for GG Operator.
+        int len = imsiDB.length();
+
+        if (len <= 0) return false;
+        if (len > imsiSIM.length()) return false;
+
+        for (int idx = 0; idx < len; idx++) {
+            char c = imsiDB.charAt(idx);
+            if ((c == 'x') || (c == 'X') || (c == imsiSIM.charAt(idx))) {
+                continue;
+            } else {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Check if MVNO type and data match IccRecords.
+     *
+     * @param r the IccRecords
+     * @param mvnoType the MVNO type
+     * @param mvnoMatchData the MVNO match data
+     * @return {@code true} if MVNO type and data match IccRecords, {@code false} otherwise.
+     */
+    public static boolean mvnoMatches(IccRecords r, int mvnoType, String mvnoMatchData) {
+        if (mvnoType == ApnSetting.MVNO_TYPE_SPN) {
+            if ((r.getServiceProviderName() != null)
+                    && r.getServiceProviderName().equalsIgnoreCase(mvnoMatchData)) {
+                return true;
+            }
+        } else if (mvnoType == ApnSetting.MVNO_TYPE_IMSI) {
+            String imsiSIM = r.getIMSI();
+            if ((imsiSIM != null) && imsiMatches(mvnoMatchData, imsiSIM)) {
+                return true;
+            }
+        } else if (mvnoType == ApnSetting.MVNO_TYPE_GID) {
+            String gid1 = r.getGid1();
+            int mvno_match_data_length = mvnoMatchData.length();
+            if ((gid1 != null) && (gid1.length() >= mvno_match_data_length)
+                    && gid1.substring(0, mvno_match_data_length).equalsIgnoreCase(mvnoMatchData)) {
+                return true;
+            }
+        } else if (mvnoType == ApnSetting.MVNO_TYPE_ICCID) {
+            String iccId = r.getIccId();
+            if ((iccId != null) && iccidMatches(mvnoMatchData, iccId)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Check if this APN type is metered.
+     *
+     * @param type the APN type
+     * @param phone the phone object
+     * @return {@code true} if the APN type is metered, {@code false} otherwise.
+     */
+    public static boolean isMeteredApnType(String type, Phone phone) {
+        if (phone == null) {
+            return true;
+        }
+
+        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)
+                phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        if (configManager == null) {
+            Rlog.e(LOG_TAG, "Carrier config service is not available");
+            return true;
+        }
+
+        PersistableBundle b = configManager.getConfigForSubId(subId);
+        if (b == null) {
+            Rlog.e(LOG_TAG, "Can't get the config. subId = " + subId);
+            return true;
+        }
+
+        String[] meteredApnTypes = b.getStringArray(carrierConfig);
+        if (meteredApnTypes == null) {
+            Rlog.e(LOG_TAG, carrierConfig +  " is not available. " + "subId = " + subId);
+            return true;
+        }
+
+        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()));
+        }
+
+        // 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.");
+            return true;
+        }
+
+        if (meteredApnSet.contains(type)) {
+            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.");
+                return true;
+            }
+        }
+
+        if (DBG) Rlog.d(LOG_TAG, type + " is not metered.");
+        return false;
+    }
+
+    /**
+     * Check if this APN setting is metered.
+     *
+     * @param phone The phone object
+     * @return True if this APN setting is metered, otherwise false.
+     */
+    public static boolean isMetered(ApnSetting apn, Phone phone) {
+        if (phone == null) {
+            return true;
+        }
+
+        String[] types = ApnSetting.getApnTypesStringFromBitmask(
+                apn.getApnTypeBitmask()).split(",");
+
+        for (String type : types) {
+            // If one of the APN type is metered, then this APN setting is metered.
+            if (isMeteredApnType(type, phone)) {
+                return true;
+            }
+        }
+        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 83ca909..00af9fe 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
@@ -43,6 +43,7 @@
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
 import android.telephony.data.DataCallResponse;
 import android.telephony.data.DataProfile;
 import android.telephony.data.DataService;
@@ -443,9 +444,9 @@
             return;
         }
 
-        if (apn != null && apn.mtu != PhoneConstants.UNSET_MTU) {
-            lp.setMtu(apn.mtu);
-            if (DBG) log("MTU set by APN to: " + apn.mtu);
+        if (apn != null && apn.getMtu() != PhoneConstants.UNSET_MTU) {
+            lp.setMtu(apn.getMtu());
+            if (DBG) log("MTU set by APN to: " + apn.getMtu());
             return;
         }
 
@@ -502,9 +503,12 @@
      * @param cp is the connection parameters
      */
     private void onConnect(ConnectionParams cp) {
-        if (DBG) log("onConnect: carrier='" + mApnSetting.carrier
-                + "' APN='" + mApnSetting.apn
-                + "' proxy='" + mApnSetting.proxy + "' port='" + mApnSetting.port + "'");
+        if (DBG) {
+            log("onConnect: carrier='" + mApnSetting.getEntryName()
+                    + "' APN='" + mApnSetting.getApnName()
+                    + "' proxy='" + mApnSetting.getProxyAddressAsString()
+                    + "' port='" + mApnSetting.getProxyPort() + "'");
+        }
         if (cp.mApnContext != null) cp.mApnContext.requestLog("DataConnection.onConnect");
 
         // Check if we should fake an error.
@@ -783,12 +787,12 @@
             // Do not apply the race condition workaround for MMS APN
             // if Proxy is an IP-address.
             // Otherwise, the default APN will not be restored anymore.
-            if (!mApnSetting.types[0].equals(PhoneConstants.APN_TYPE_MMS)
-                || !isIpAddress(mApnSetting.mmsProxy)) {
+            if (!isIpAddress(mApnSetting.getMmsProxyAddressAsString())) {
                 log(String.format(
-                        "isDnsOk: return false apn.types[0]=%s APN_TYPE_MMS=%s isIpAddress(%s)=%s",
-                        mApnSetting.types[0], PhoneConstants.APN_TYPE_MMS, mApnSetting.mmsProxy,
-                        isIpAddress(mApnSetting.mmsProxy)));
+                        "isDnsOk: return false apn.types=%d APN_TYPE_MMS=%s isIpAddress(%s)=%s",
+                        mApnSetting.getApnTypeBitmask(), PhoneConstants.APN_TYPE_MMS,
+                        mApnSetting.getMmsProxyAddressAsString(),
+                        isIpAddress(mApnSetting.getMmsProxyAddressAsString())));
                 return false;
             }
         }
@@ -922,7 +926,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)) {
+        if (!ApnSettingUtils.isMetered(mApnSetting, mPhone)) {
             return;
         }
 
@@ -935,10 +939,12 @@
         result.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
 
         if (mApnSetting != null) {
-            for (String type : mApnSetting.types) {
+            final String[] types = ApnSetting.getApnTypesStringFromBitmask(
+                mApnSetting.getApnTypeBitmask()).split(",");
+            for (String type : types) {
                 if (!mRestrictedNetworkOverride
                         && (mConnectionParams != null && mConnectionParams.mUnmeteredUseOnly)
-                        && ApnSetting.isMeteredApnType(type, mPhone)) {
+                        && ApnSettingUtils.isMeteredApnType(type, mPhone)) {
                     log("Dropped the metered " + type + " for the unmetered data call.");
                     continue;
                 }
@@ -999,7 +1005,7 @@
             // 2. The non-restricted data and is intended for unmetered use only.
             if (((mConnectionParams != null && mConnectionParams.mUnmeteredUseOnly)
                     && !mRestrictedNetworkOverride)
-                    || !mApnSetting.isMetered(mPhone)) {
+                    || !ApnSettingUtils.isMetered(mApnSetting, mPhone)) {
                 result.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
             } else {
                 result.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
@@ -1170,7 +1176,7 @@
             // only NOT be set only if we're in DcInactiveState.
             mApnSetting = apnContext.getApnSetting();
         }
-        if (mApnSetting == null || !mApnSetting.canHandleType(apnContext.getApnType())) {
+        if (mApnSetting == null || !mApnSetting.canHandleType(apnContext.getApnTypeBitmask())) {
             if (DBG) {
                 log("initConnection: incompatible apnSetting in ConnectionParams cp=" + cp
                         + " dc=" + DataConnection.this);
@@ -1683,7 +1689,7 @@
 
             mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED,
                     mNetworkInfo.getReason(), null);
-            mNetworkInfo.setExtraInfo(mApnSetting.apn);
+            mNetworkInfo.setExtraInfo(mApnSetting.getApnName());
             updateTcpBufferSizes(mRilRat);
 
             final NetworkMisc misc = new NetworkMisc();
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java b/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java
index ad0b4ed..06bb7de 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java
@@ -20,6 +20,7 @@
 import android.net.NetworkCapabilities;
 import android.net.ProxyInfo;
 import android.os.Message;
+import android.telephony.data.ApnSetting;
 
 import com.android.internal.telephony.dataconnection.DataConnection.ConnectionParams;
 import com.android.internal.telephony.dataconnection.DataConnection.DisconnectParams;
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcRequest.java b/src/java/com/android/internal/telephony/dataconnection/DcRequest.java
index 79cfcec..bf2a6af 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcRequest.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcRequest.java
@@ -15,22 +15,10 @@
  */
 package com.android.internal.telephony.dataconnection;
 
-import static com.android.internal.telephony.DctConstants.APN_CBS_ID;
-import static com.android.internal.telephony.DctConstants.APN_DEFAULT_ID;
-import static com.android.internal.telephony.DctConstants.APN_DUN_ID;
-import static com.android.internal.telephony.DctConstants.APN_EMERGENCY_ID;
-import static com.android.internal.telephony.DctConstants.APN_FOTA_ID;
-import static com.android.internal.telephony.DctConstants.APN_IA_ID;
-import static com.android.internal.telephony.DctConstants.APN_IMS_ID;
-import static com.android.internal.telephony.DctConstants.APN_INVALID_ID;
-import static com.android.internal.telephony.DctConstants.APN_MMS_ID;
-import static com.android.internal.telephony.DctConstants.APN_SUPL_ID;
-
 import android.content.Context;
-import android.net.NetworkCapabilities;
 import android.net.NetworkConfig;
 import android.net.NetworkRequest;
-import android.telephony.Rlog;
+import android.telephony.data.ApnSetting.ApnType;
 
 import java.util.HashMap;
 
@@ -39,17 +27,17 @@
 
     public final NetworkRequest networkRequest;
     public final int priority;
-    public final int apnId;
+    public final @ApnType int apnType;
 
     public DcRequest(NetworkRequest nr, Context context) {
         initApnPriorities(context);
         networkRequest = nr;
-        apnId = apnIdForNetworkRequest(networkRequest);
-        priority = priorityForApnId(apnId);
+        apnType = ApnContext.getApnTypeFromNetworkRequest(networkRequest);
+        priority = priorityForApnType(apnType);
     }
 
     public String toString() {
-        return networkRequest.toString() + ", priority=" + priority + ", apnId=" + apnId;
+        return networkRequest.toString() + ", priority=" + priority + ", apnType=" + apnType;
     }
 
     public int hashCode() {
@@ -67,78 +55,6 @@
         return o.priority - priority;
     }
 
-    private int apnIdForNetworkRequest(NetworkRequest nr) {
-        NetworkCapabilities nc = nr.networkCapabilities;
-        // For now, ignore the bandwidth stuff
-        if (nc.getTransportTypes().length > 0 &&
-                nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == false) {
-            return APN_INVALID_ID;
-        }
-
-        // in the near term just do 1-1 matches.
-        // TODO - actually try to match the set of capabilities
-        int apnId = APN_INVALID_ID;
-
-        boolean error = false;
-        if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
-            if (apnId != APN_INVALID_ID) error = true;
-            apnId = APN_DEFAULT_ID;
-        }
-        if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) {
-            if (apnId != APN_INVALID_ID) error = true;
-            apnId = APN_MMS_ID;
-        }
-        if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
-            if (apnId != APN_INVALID_ID) error = true;
-            apnId = APN_SUPL_ID;
-        }
-        if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) {
-            if (apnId != APN_INVALID_ID) error = true;
-            apnId = APN_DUN_ID;
-        }
-        if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) {
-            if (apnId != APN_INVALID_ID) error = true;
-            apnId = APN_FOTA_ID;
-        }
-        if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) {
-            if (apnId != APN_INVALID_ID) error = true;
-            apnId = APN_IMS_ID;
-        }
-        if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
-            if (apnId != APN_INVALID_ID) error = true;
-            apnId = APN_CBS_ID;
-        }
-        if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_IA)) {
-            if (apnId != APN_INVALID_ID) error = true;
-            apnId = APN_IA_ID;
-        }
-        if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_RCS)) {
-            if (apnId != APN_INVALID_ID) error = true;
-            apnId = APN_INVALID_ID;
-            loge("RCS APN type not yet supported");
-        }
-        if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_XCAP)) {
-            if (apnId != APN_INVALID_ID) error = true;
-            apnId = APN_INVALID_ID;
-            loge("XCAP APN type not yet supported");
-        }
-        if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)) {
-            if (apnId != APN_INVALID_ID) error = true;
-            apnId = APN_EMERGENCY_ID;
-        }
-        if (error) {
-            // TODO: If this error condition is removed, the framework's handling of
-            // NET_CAPABILITY_NOT_RESTRICTED will need to be updated so requests for
-            // say FOTA and INTERNET are marked as restricted.  This is not how
-            // NetworkCapabilities.maybeMarkCapabilitiesRestricted currently works.
-            loge("Multiple apn types specified in request - result is unspecified!");
-        }
-        if (apnId == APN_INVALID_ID) {
-            loge("Unsupported NetworkRequest in Telephony: nr=" + nr);
-        }
-        return apnId;
-    }
-
     private static final HashMap<Integer, Integer> sApnPriorityMap =
             new HashMap<Integer, Integer>();
 
@@ -149,19 +65,15 @@
                         com.android.internal.R.array.networkAttributes);
                 for (String networkConfigString : networkConfigStrings) {
                     NetworkConfig networkConfig = new NetworkConfig(networkConfigString);
-                    final int apnId = ApnContext.apnIdForType(networkConfig.type);
-                    sApnPriorityMap.put(apnId, networkConfig.priority);
+                    final int apnType = ApnContext.getApnTypeFromNetworkType(networkConfig.type);
+                    sApnPriorityMap.put(apnType, networkConfig.priority);
                 }
             }
         }
     }
 
-    private int priorityForApnId(int apnId) {
-        Integer priority = sApnPriorityMap.get(apnId);
+    private int priorityForApnType(int apnType) {
+        Integer priority = sApnPriorityMap.get(apnType);
         return (priority != null ? priority.intValue() : 0);
     }
-
-    private void loge(String s) {
-        Rlog.e(LOG_TAG, s);
-    }
 }
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
index a79e8f2..50130c8 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -36,7 +36,6 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkConfig;
 import android.net.NetworkRequest;
-import android.net.NetworkUtils;
 import android.net.ProxyInfo;
 import android.net.TrafficStats;
 import android.net.Uri;
@@ -65,6 +64,7 @@
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
 import android.telephony.TelephonyManager;
 import android.telephony.cdma.CdmaCellLocation;
+import android.telephony.data.ApnSetting;
 import android.telephony.data.DataProfile;
 import android.telephony.gsm.GsmCellLocation;
 import android.text.TextUtils;
@@ -98,7 +98,6 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.Map.Entry;
@@ -123,7 +122,7 @@
     private final AlarmManager mAlarmManager;
 
     /* Currently requested APN type (TODO: This should probably be a parameter not a member) */
-    private String mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT;
+    private int mRequestedApnType = ApnSetting.TYPE_DEFAULT;
 
     // All data enabling/disabling related settings
     private final DataEnabledSettings mDataEnabledSettings;
@@ -217,6 +216,7 @@
     private AsyncChannel mReplyAc = new AsyncChannel();
 
     private final LocalLog mDataRoamingLeakageLog = new LocalLog(50);
+    private final LocalLog mApnSettingsInitializationLog = new LocalLog(50);
 
     private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver () {
         @Override
@@ -354,10 +354,21 @@
             return "{txSum=" + txPkts + " rxSum=" + rxPkts + "}";
         }
 
-        public void updateTxRxSum() {
+        /**
+         * Get Tcp Tx/Rx packet count from TrafficStats
+         */
+        public void updateTcpTxRxSum() {
             this.txPkts = TrafficStats.getMobileTcpTxPackets();
             this.rxPkts = TrafficStats.getMobileTcpRxPackets();
         }
+
+        /**
+         * Get total Tx/Rx packet count from TrafficStats
+         */
+        public void updateTotalTxRxSum() {
+            this.txPkts = TrafficStats.getMobileTxPackets();
+            this.rxPkts = TrafficStats.getMobileRxPackets();
+        }
     }
 
     private void onActionIntentReconnectAlarm(Intent intent) {
@@ -504,7 +515,7 @@
     private final ConcurrentHashMap<String, ApnContext> mApnContexts =
             new ConcurrentHashMap<String, ApnContext>();
 
-    private final SparseArray<ApnContext> mApnContextsById = new SparseArray<ApnContext>();
+    private final SparseArray<ApnContext> mApnContextsByType = new SparseArray<ApnContext>();
 
     private int mDisconnectPendingCount = 0;
 
@@ -732,7 +743,7 @@
 
         mPhone.getContext().getContentResolver().unregisterContentObserver(mApnObserver);
         mApnContexts.clear();
-        mApnContextsById.clear();
+        mApnContextsByType.clear();
         mPrioritySortedApnContexts.clear();
         unregisterForAllEvents();
 
@@ -825,7 +836,7 @@
                             // data connection.
                             apnContext.setReason(Phone.REASON_DATA_ENABLED);
                             cleanUpConnection(true, apnContext);
-                        } else if (apnContext.getApnSetting().isMetered(mPhone)
+                        } else if (ApnSettingUtils.isMetered(apnContext.getApnSetting(), mPhone)
                                 && (netCaps != null && netCaps.hasCapability(
                                         NetworkCapabilities.NET_CAPABILITY_NOT_METERED))) {
                             if (DBG) {
@@ -869,40 +880,19 @@
     }
 
     public void requestNetwork(NetworkRequest networkRequest, LocalLog log) {
-        final int apnId = ApnContext.apnIdForNetworkRequest(networkRequest);
-        final ApnContext apnContext = mApnContextsById.get(apnId);
+        final int apnType = ApnContext.getApnTypeFromNetworkRequest(networkRequest);
+        final ApnContext apnContext = mApnContextsByType.get(apnType);
         log.log("DcTracker.requestNetwork for " + networkRequest + " found " + apnContext);
         if (apnContext != null) apnContext.requestNetwork(networkRequest, log);
     }
 
     public void releaseNetwork(NetworkRequest networkRequest, LocalLog log) {
-        final int apnId = ApnContext.apnIdForNetworkRequest(networkRequest);
-        final ApnContext apnContext = mApnContextsById.get(apnId);
+        final int apnType = ApnContext.getApnTypeFromNetworkRequest(networkRequest);
+        final ApnContext apnContext = mApnContextsByType.get(apnType);
         log.log("DcTracker.releaseNetwork for " + networkRequest + " found " + apnContext);
         if (apnContext != null) apnContext.releaseNetwork(networkRequest, log);
     }
 
-    public boolean isApnSupported(String name) {
-        if (name == null) {
-            loge("isApnSupported: name=null");
-            return false;
-        }
-        ApnContext apnContext = mApnContexts.get(name);
-        if (apnContext == null) {
-            loge("Request for unsupported mobile name: " + name);
-            return false;
-        }
-        return true;
-    }
-
-    public int getApnPriority(String name) {
-        ApnContext apnContext = mApnContexts.get(name);
-        if (apnContext == null) {
-            loge("Request for unsupported mobile name: " + name);
-        }
-        return apnContext.priority;
-    }
-
     // Turn telephony radio on or off.
     private void setRadio(boolean on) {
         final ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
@@ -973,7 +963,7 @@
     private ApnContext addApnContext(String type, NetworkConfig networkConfig) {
         ApnContext apnContext = new ApnContext(mPhone, type, LOG_TAG, networkConfig, this);
         mApnContexts.put(type, apnContext);
-        mApnContextsById.put(ApnContext.apnIdForApnName(type), apnContext);
+        mApnContextsByType.put(ApnSetting.getApnTypesBitmaskFromString(type), apnContext);
         mPrioritySortedApnContexts.add(apnContext);
         return apnContext;
     }
@@ -1077,7 +1067,7 @@
         if (apnContext != null) {
             ApnSetting apnSetting = apnContext.getApnSetting();
             if (apnSetting != null) {
-                return apnSetting.apn;
+                return apnSetting.getApnName();
             }
         }
         return null;
@@ -1091,9 +1081,10 @@
      * Assumes there is less than one {@link ApnSetting} can support the given apn type.
      */
     public DctConstants.State getState(String apnType) {
+        final int apnTypeBitmask = ApnSetting.getApnTypesBitmaskFromString(apnType);
         for (DataConnection dc : mDataConnections.values()) {
             ApnSetting apnSetting = dc.getApnSetting();
-            if (apnSetting != null && apnSetting.canHandleType(apnType)) {
+            if (apnSetting != null && apnSetting.canHandleType(apnTypeBitmask)) {
                 if (dc.isActive()) {
                     return DctConstants.State.CONNECTED;
                 } else if (dc.isActivating()) {
@@ -1258,7 +1249,7 @@
                 SubscriptionManager.getDefaultDataSubscriptionId());
 
         boolean isMeteredApnType = apnContext == null
-                || ApnSetting.isMeteredApnType(apnContext.getApnType(), mPhone);
+                || ApnSettingUtils.isMeteredApnType(apnContext.getApnType(), mPhone);
 
         PhoneConstants.State phoneState = PhoneConstants.State.IDLE;
         // Note this is explicitly not using mPhone.getState.  See b/19090488.
@@ -1562,7 +1553,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)) {
+                if (apnSetting != null && ApnSettingUtils.isMetered(apnSetting, mPhone)) {
                     if (apnContext.isDisconnected() == false) didDisconnect = true;
                     if (DBG) log("clean up metered ApnContext Type: " + apnContext.getApnType());
                     apnContext.setReason(reason);
@@ -1585,7 +1576,7 @@
         stopDataStallAlarm();
 
         // TODO: Do we need mRequestedApnType?
-        mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT;
+        mRequestedApnType = ApnSetting.TYPE_DEFAULT;
 
         log("cleanUpConnection: mDisconnectPendingCount = " + mDisconnectPendingCount);
         if (tearDown && mDisconnectPendingCount == 0) {
@@ -1749,7 +1740,7 @@
         if (dunCandidates.isEmpty()) {
             if (!ArrayUtils.isEmpty(mAllApnSettings)) {
                 for (ApnSetting apn : mAllApnSettings) {
-                    if (apn.canHandleType(PhoneConstants.APN_TYPE_DUN)) {
+                    if (apn.canHandleType(ApnSetting.TYPE_DUN)) {
                         dunCandidates.add(apn);
                     }
                 }
@@ -1758,14 +1749,14 @@
         }
 
         for (ApnSetting dunSetting : dunCandidates) {
-            if (!ServiceState.bitmaskHasTech(dunSetting.networkTypeBitmask,
+            if (!ServiceState.bitmaskHasTech(dunSetting.getNetworkTypeBitmask(),
                     ServiceState.rilRadioTechnologyToNetworkType(bearer))) {
                 continue;
             }
-            if (dunSetting.numeric.equals(operator)) {
+            if (dunSetting.getOperatorNumeric().equals(operator)) {
                 if (dunSetting.hasMvnoParams()) {
-                    if (r != null && ApnSetting.mvnoMatches(r, dunSetting.mvnoType,
-                            dunSetting.mvnoMatchData)) {
+                    if (r != null && ApnSettingUtils.mvnoMatches(r, dunSetting.getMvnoType(),
+                            dunSetting.getMvnoMatchData())) {
                         retDunSettings.add(dunSetting);
                     }
                 } else if (mMvnoMatched == false) {
@@ -1828,73 +1819,11 @@
         }
     }
 
-    /**
-     * @param types comma delimited list of APN types
-     * @return array of APN types
-     */
-    private String[] parseTypes(String types) {
-        String[] result;
-        // If unset, set to DEFAULT.
-        if (types == null || types.equals("")) {
-            result = new String[1];
-            result[0] = PhoneConstants.APN_TYPE_ALL;
-        } else {
-            result = types.split(",");
-        }
-        return result;
-    }
-
     boolean isPermanentFailure(DcFailCause dcFailCause) {
         return (dcFailCause.isPermanentFailure(mPhone.getContext(), mPhone.getSubId()) &&
                 (mAttached.get() == false || dcFailCause != DcFailCause.SIGNAL_LOST));
     }
 
-    private ApnSetting makeApnSetting(Cursor cursor) {
-        String[] types = parseTypes(
-                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE)));
-        int networkTypeBitmask = cursor.getInt(
-                cursor.getColumnIndexOrThrow(Telephony.Carriers.NETWORK_TYPE_BITMASK));
-
-        ApnSetting apn = new ApnSetting(
-                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)),
-                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NUMERIC)),
-                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NAME)),
-                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN)),
-                NetworkUtils.trimV4AddrZeros(
-                        cursor.getString(
-                        cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY))),
-                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT)),
-                NetworkUtils.trimV4AddrZeros(
-                        cursor.getString(
-                        cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC))),
-                NetworkUtils.trimV4AddrZeros(
-                        cursor.getString(
-                        cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY))),
-                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPORT)),
-                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)),
-                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)),
-                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)),
-                types,
-                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)),
-                cursor.getString(cursor.getColumnIndexOrThrow(
-                        Telephony.Carriers.ROAMING_PROTOCOL)),
-                cursor.getInt(cursor.getColumnIndexOrThrow(
-                        Telephony.Carriers.CARRIER_ENABLED)) == 1,
-                networkTypeBitmask,
-                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROFILE_ID)),
-                cursor.getInt(cursor.getColumnIndexOrThrow(
-                        Telephony.Carriers.MODEM_COGNITIVE)) == 1,
-                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MAX_CONNS)),
-                cursor.getInt(cursor.getColumnIndexOrThrow(
-                        Telephony.Carriers.WAIT_TIME)),
-                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MAX_CONNS_TIME)),
-                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU)),
-                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MVNO_TYPE)),
-                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MVNO_MATCH_DATA)),
-                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN_SET_ID)));
-        return apn;
-    }
-
     private ArrayList<ApnSetting> createApnList(Cursor cursor) {
         ArrayList<ApnSetting> mnoApns = new ArrayList<ApnSetting>();
         ArrayList<ApnSetting> mvnoApns = new ArrayList<ApnSetting>();
@@ -1902,13 +1831,14 @@
 
         if (cursor.moveToFirst()) {
             do {
-                ApnSetting apn = makeApnSetting(cursor);
+                ApnSetting apn = ApnSetting.makeApnSetting(cursor);
                 if (apn == null) {
                     continue;
                 }
 
                 if (apn.hasMvnoParams()) {
-                    if (r != null && ApnSetting.mvnoMatches(r, apn.mvnoType, apn.mvnoMatchData)) {
+                    if (r != null && ApnSettingUtils.mvnoMatches(r, apn.getMvnoType(),
+                            apn.getMvnoMatchData())) {
                         mvnoApns.add(apn);
                     }
                 } else {
@@ -1982,7 +1912,7 @@
             return false;
         }
 
-        int profileId = apnSetting.profileId;
+        int profileId = apnSetting.getProfileId();
         if (profileId == 0) {
             profileId = getApnProfileID(apnContext.getApnType());
         }
@@ -2067,7 +1997,7 @@
 
         log("setInitialApn: E mPreferredApn=" + mPreferredApn);
 
-        if (mPreferredApn != null && mPreferredApn.canHandleType(PhoneConstants.APN_TYPE_IA)) {
+        if (mPreferredApn != null && mPreferredApn.canHandleType(ApnSetting.TYPE_IA)) {
               iaApnSetting = mPreferredApn;
         } else if (mAllApnSettings != null && !mAllApnSettings.isEmpty()) {
             firstApnSetting = mAllApnSettings.get(0);
@@ -2075,13 +2005,13 @@
 
             // Search for Initial APN setting and the first apn that can handle default
             for (ApnSetting apn : mAllApnSettings) {
-                if (apn.canHandleType(PhoneConstants.APN_TYPE_IA)) {
+                if (apn.canHandleType(ApnSetting.TYPE_IA)) {
                     // The Initial Attach APN is highest priority so use it if there is one
                     log("setInitialApn: iaApnSetting=" + apn);
                     iaApnSetting = apn;
                     break;
                 } else if ((defaultApnSetting == null)
-                        && (apn.canHandleType(PhoneConstants.APN_TYPE_DEFAULT))) {
+                        && (apn.canHandleType(ApnSetting.TYPE_DEFAULT))) {
                     // Use the first default apn if no better choice
                     log("setInitialApn: defaultApnSetting=" + apn);
                     defaultApnSetting = apn;
@@ -2357,24 +2287,6 @@
         mAutoAttachOnCreation.set(false);
     }
 
-    private void onSetDependencyMet(String apnType, boolean met) {
-        // don't allow users to tweak hipri to work around default dependency not met
-        if (PhoneConstants.APN_TYPE_HIPRI.equals(apnType)) return;
-
-        ApnContext apnContext = mApnContexts.get(apnType);
-        if (apnContext == null) {
-            loge("onSetDependencyMet: ApnContext not found in onSetDependencyMet(" +
-                    apnType + ", " + met + ")");
-            return;
-        }
-        applyNewState(apnContext, apnContext.isEnabled(), met);
-        if (PhoneConstants.APN_TYPE_DEFAULT.equals(apnType)) {
-            // tie actions on default to similar actions on HIPRI regarding dependencyMet
-            apnContext = mApnContexts.get(PhoneConstants.APN_TYPE_HIPRI);
-            if (apnContext != null) applyNewState(apnContext, apnContext.isEnabled(), met);
-        }
-    }
-
     public void setPolicyDataEnabled(boolean enabled) {
         if (DBG) log("setPolicyDataEnabled: " + enabled);
         Message msg = obtainMessage(DctConstants.CMD_SET_POLICY_DATA_ENABLE);
@@ -2479,10 +2391,10 @@
     }
 
     private DcAsyncChannel checkForCompatibleConnectedApnContext(ApnContext apnContext) {
-        String apnType = apnContext.getApnType();
+        int apnType = apnContext.getApnTypeBitmask();
         ArrayList<ApnSetting> dunSettings = null;
 
-        if (PhoneConstants.APN_TYPE_DUN.equals(apnType)) {
+        if (ApnSetting.TYPE_DUN == apnType) {
             dunSettings = sortApnListByPreferred(fetchDunApns());
         }
         if (DBG) {
@@ -2555,17 +2467,17 @@
         return null;
     }
 
-    public void setEnabled(int id, boolean enable) {
+    public void setEnabled(int apnType, boolean enable) {
         Message msg = obtainMessage(DctConstants.EVENT_ENABLE_NEW_APN);
-        msg.arg1 = id;
+        msg.arg1 = apnType;
         msg.arg2 = (enable ? DctConstants.ENABLED : DctConstants.DISABLED);
         sendMessage(msg);
     }
 
-    private void onEnableApn(int apnId, int enabled) {
-        ApnContext apnContext = mApnContextsById.get(apnId);
+    private void onEnableApn(int apnType, int enabled) {
+        ApnContext apnContext = mApnContextsByType.get(apnType);
         if (apnContext == null) {
-            loge("onEnableApn(" + apnId + ", " + enabled + "): NO ApnContext");
+            loge("onEnableApn(" + apnType + ", " + enabled + "): NO ApnContext");
             return;
         }
         // TODO change our retry manager to use the appropriate numbers for the new APN
@@ -2910,18 +2822,20 @@
             } else {
                 ApnSetting apn = apnContext.getApnSetting();
                 if (DBG) {
-                    log("onDataSetupComplete: success apn=" + (apn == null ? "unknown" : apn.apn));
+                    log("onDataSetupComplete: success apn=" + (apn == null ? "unknown"
+                            : apn.getApnName()));
                 }
-                if (apn != null && apn.proxy != null && apn.proxy.length() != 0) {
+                if (apn != null && !TextUtils.isEmpty(apn.getProxyAddressAsString())) {
                     try {
-                        String port = apn.port;
-                        if (TextUtils.isEmpty(port)) port = "8080";
-                        ProxyInfo proxy = new ProxyInfo(apn.proxy,
-                                Integer.parseInt(port), null);
+                        int port = apn.getProxyPort();
+                        if (port == -1) {
+                            port = 8080;
+                        }
+                        ProxyInfo proxy = new ProxyInfo(apn.getProxyAddressAsString(), port, null);
                         dcac.setLinkPropertiesHttpProxySync(proxy);
                     } catch (NumberFormatException e) {
-                        loge("onDataSetupComplete: NumberFormatException making ProxyProperties (" +
-                                apn.port + "): " + e);
+                        loge("onDataSetupComplete: NumberFormatException making ProxyProperties ("
+                                + apn.getProxyPort() + "): " + e);
                     }
                 }
 
@@ -2936,7 +2850,7 @@
                         if (DBG) log("onDataSetupComplete: PREFERRED APN is null");
                         mPreferredApn = apn;
                         if (mPreferredApn != null) {
-                            setPreferredApn(mPreferredApn.id);
+                            setPreferredApn(mPreferredApn.getId());
                         }
                     }
                 } else {
@@ -3019,7 +2933,7 @@
             if (DBG) {
                 ApnSetting apn = apnContext.getApnSetting();
                 log(String.format("onDataSetupComplete: error apn=%s cause=%s",
-                        (apn == null ? "unknown" : apn.apn), cause));
+                        (apn == null ? "unknown" : apn.getApnName()), cause));
             }
             if (cause.isEventLoggable()) {
                 // Log this failure to the Event Logs.
@@ -3029,7 +2943,8 @@
             }
             ApnSetting apn = apnContext.getApnSetting();
             mPhone.notifyPreciseDataConnectionFailed(apnContext.getReason(),
-                    apnContext.getApnType(), apn != null ? apn.apn : "unknown", cause.toString());
+                    apnContext.getApnType(), apn != null ? apn.getApnName()
+                    : "unknown", cause.toString());
 
             // Compose broadcast intent send to the specific carrier signaling receivers
             Intent intent = new Intent(TelephonyIntents
@@ -3257,9 +3172,9 @@
         setupDataOnConnectableApns(Phone.REASON_VOICE_CALL_ENDED);
     }
 
-    private void onCleanUpConnection(boolean tearDown, int apnId, String reason) {
+    private void onCleanUpConnection(boolean tearDown, int apnType, String reason) {
         if (DBG) log("onCleanUpConnection");
-        ApnContext apnContext = mApnContextsById.get(apnId);
+        ApnContext apnContext = mApnContextsByType.get(apnType);
         if (apnContext != null) {
             apnContext.setReason(reason);
             cleanUpConnection(tearDown, apnContext);
@@ -3305,7 +3220,7 @@
         if (mAllApnSettings != null && !mAllApnSettings.isEmpty()) {
             ArrayList<DataProfile> dps = new ArrayList<DataProfile>();
             for (ApnSetting apn : mAllApnSettings) {
-                if (apn.modemCognitive) {
+                if (apn.getModemCognitive()) {
                     DataProfile dp = createDataProfile(apn);
                     if (!dps.contains(dp)) {
                         dps.add(dp);
@@ -3343,8 +3258,14 @@
             if (cursor != null) {
                 if (cursor.getCount() > 0) {
                     mAllApnSettings = createApnList(cursor);
+                } else {
+                    if (DBG) log("createAllApnList: cursor count is 0");
+                    mApnSettingsInitializationLog.log("no APN in db for carrier: " + operator);
                 }
                 cursor.close();
+            } else {
+                if (DBG) log("createAllApnList: cursor is null");
+                mApnSettingsInitializationLog.log("cursor is null for carrier: " + operator);
             }
         }
 
@@ -3354,12 +3275,13 @@
 
         if (mAllApnSettings.isEmpty()) {
             if (DBG) log("createAllApnList: No APN found for carrier: " + operator);
+            mApnSettingsInitializationLog.log("no APN found for carrier: " + operator);
             mPreferredApn = null;
             // TODO: What is the right behavior?
             //notifyNoData(DataConnection.FailCause.MISSING_UNKNOWN_APN);
         } else {
             mPreferredApn = getPreferredApn();
-            if (mPreferredApn != null && !mPreferredApn.numeric.equals(operator)) {
+            if (mPreferredApn != null && !mPreferredApn.getOperatorNumeric().equals(operator)) {
                 mPreferredApn = null;
                 setPreferredApn(-1);
             }
@@ -3396,36 +3318,33 @@
     }
 
     private ApnSetting mergeApns(ApnSetting dest, ApnSetting src) {
-        int id = dest.id;
-        ArrayList<String> resultTypes = new ArrayList<String>();
-        resultTypes.addAll(Arrays.asList(dest.types));
-        for (String srcType : src.types) {
-            if (resultTypes.contains(srcType) == false) resultTypes.add(srcType);
-            if (srcType.equals(PhoneConstants.APN_TYPE_DEFAULT)) id = src.id;
+        int id = dest.getId();
+        if ((src.getApnTypeBitmask() & ApnSetting.TYPE_DEFAULT) == ApnSetting.TYPE_DEFAULT) {
+            id = src.getId();
         }
-        String mmsc = (TextUtils.isEmpty(dest.mmsc) ? src.mmsc : dest.mmsc);
-        String mmsProxy = (TextUtils.isEmpty(dest.mmsProxy) ? src.mmsProxy : dest.mmsProxy);
-        String mmsPort = (TextUtils.isEmpty(dest.mmsPort) ? src.mmsPort : dest.mmsPort);
-        String proxy = (TextUtils.isEmpty(dest.proxy) ? src.proxy : dest.proxy);
-        String port = (TextUtils.isEmpty(dest.port) ? src.port : dest.port);
-        String protocol = src.protocol.equals("IPV4V6") ? src.protocol : dest.protocol;
-        String roamingProtocol = src.roamingProtocol.equals("IPV4V6") ? src.roamingProtocol :
-                dest.roamingProtocol;
-        int networkTypeBitmask = (dest.networkTypeBitmask == 0 || src.networkTypeBitmask == 0)
-                ? 0 : (dest.networkTypeBitmask | src.networkTypeBitmask);
-        if (networkTypeBitmask == 0) {
-            int bearerBitmask = (dest.bearerBitmask == 0 || src.bearerBitmask == 0)
-                    ? 0 : (dest.bearerBitmask | src.bearerBitmask);
-            networkTypeBitmask = ServiceState.convertBearerBitmaskToNetworkTypeBitmask(
-                    bearerBitmask);
-        }
+        final int resultApnType = src.getApnTypeBitmask() | dest.getApnTypeBitmask();
+        Uri mmsc = (dest.getMmsc() == null ? src.getMmsc() : dest.getMmsc());
+        String mmsProxy = TextUtils.isEmpty(dest.getMmsProxyAddressAsString())
+                ? src.getMmsProxyAddressAsString() : dest.getMmsProxyAddressAsString();
+        int mmsPort = dest.getMmsProxyPort() == -1 ? src.getMmsProxyPort() : dest.getMmsProxyPort();
+        String proxy = TextUtils.isEmpty(dest.getProxyAddressAsString())
+                ? src.getProxyAddressAsString() : dest.getProxyAddressAsString();
+        int port = dest.getProxyPort() == -1 ? src.getProxyPort() : dest.getProxyPort();
+        int protocol = src.getProtocol() == ApnSetting.PROTOCOL_IPV4V6 ? src.getProtocol()
+                : dest.getProtocol();
+        int roamingProtocol = src.getRoamingProtocol() == ApnSetting.PROTOCOL_IPV4V6
+                ? src.getRoamingProtocol() : dest.getRoamingProtocol();
+        int networkTypeBitmask = (dest.getNetworkTypeBitmask() == 0
+                || src.getNetworkTypeBitmask() == 0)
+                ? 0 : (dest.getNetworkTypeBitmask() | src.getNetworkTypeBitmask());
 
-        return new ApnSetting(id, dest.numeric, dest.carrier, dest.apn,
-                proxy, port, mmsc, mmsProxy, mmsPort, dest.user, dest.password,
-                dest.authType, resultTypes.toArray(new String[0]), protocol,
-                roamingProtocol, dest.carrierEnabled, networkTypeBitmask, dest.profileId,
-                (dest.modemCognitive || src.modemCognitive), dest.maxConns, dest.waitTime,
-                dest.maxConnsTime, dest.mtu, dest.mvnoType, dest.mvnoMatchData, dest.apnSetId);
+        return ApnSetting.makeApnSetting(id, dest.getOperatorNumeric(), dest.getEntryName(),
+            dest.getApnName(), proxy, port, mmsc, mmsProxy, mmsPort, dest.getUser(),
+            dest.getPassword(), dest.getAuthType(), resultApnType, protocol, roamingProtocol,
+            dest.isEnabled(), networkTypeBitmask, dest.getProfileId(),
+            (dest.getModemCognitive() || src.getModemCognitive()), dest.getMaxConns(),
+            dest.getWaitTime(), dest.getMaxConnsTime(), dest.getMtu(), dest.getMvnoType(),
+            dest.getMvnoMatchData(), dest.getApnSetId());
     }
 
     /** Return the DC AsyncChannel for the new data connection */
@@ -3468,7 +3387,8 @@
         if (DBG) log("buildWaitingApns: E requestedApnType=" + requestedApnType);
         ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>();
 
-        if (requestedApnType.equals(PhoneConstants.APN_TYPE_DUN)) {
+        int requestedApnTypeBitmask = ApnSetting.getApnTypesBitmaskFromString(requestedApnType);
+        if (requestedApnTypeBitmask == ApnSetting.TYPE_DUN) {
             ArrayList<ApnSetting> dunApns = fetchDunApns();
             if (dunApns.size() > 0) {
                 for (ApnSetting dun : dunApns) {
@@ -3507,13 +3427,13 @@
         }
 
         if (usePreferred && mCanSetPreferApn && mPreferredApn != null &&
-                mPreferredApn.canHandleType(requestedApnType)) {
+                mPreferredApn.canHandleType(requestedApnTypeBitmask)) {
             if (DBG) {
                 log("buildWaitingApns: Preferred APN:" + operator + ":"
-                        + mPreferredApn.numeric + ":" + mPreferredApn);
+                        + mPreferredApn.getOperatorNumeric() + ":" + mPreferredApn);
             }
-            if (mPreferredApn.numeric.equals(operator)) {
-                if (ServiceState.bitmaskHasTech(mPreferredApn.networkTypeBitmask,
+            if (mPreferredApn.getOperatorNumeric().equals(operator)) {
+                if (ServiceState.bitmaskHasTech(mPreferredApn.getNetworkTypeBitmask(),
                         ServiceState.rilRadioTechnologyToNetworkType(radioTech))) {
                     apnList.add(mPreferredApn);
                     apnList = sortApnListByPreferred(apnList);
@@ -3533,15 +3453,15 @@
         if (mAllApnSettings != null) {
             if (DBG) log("buildWaitingApns: mAllApnSettings=" + mAllApnSettings);
             for (ApnSetting apn : mAllApnSettings) {
-                if (apn.canHandleType(requestedApnType)) {
-                    if (ServiceState.bitmaskHasTech(apn.networkTypeBitmask,
+                if (apn.canHandleType(requestedApnTypeBitmask)) {
+                    if (ServiceState.bitmaskHasTech(apn.getNetworkTypeBitmask(),
                             ServiceState.rilRadioTechnologyToNetworkType(radioTech))) {
                         if (DBG) log("buildWaitingApns: adding apn=" + apn);
                         apnList.add(apn);
                     } else {
                         if (DBG) {
-                            log("buildWaitingApns: bearerBitmask:" + apn.bearerBitmask
-                                    + " or " + "networkTypeBitmask:" + apn.networkTypeBitmask
+                            log("buildWaitingApns: networkTypeBitmask:"
+                                    + apn.getNetworkTypeBitmask()
                                     + "do not include radioTech:" + radioTech);
                         }
                     }
@@ -3579,8 +3499,12 @@
             list.sort(new Comparator<ApnSetting>() {
                 @Override
                 public int compare(ApnSetting apn1, ApnSetting apn2) {
-                    if (apn1.apnSetId == preferredApnSetId) return -1;
-                    if (apn2.apnSetId == preferredApnSetId) return 1;
+                    if (apn1.getApnSetId() == preferredApnSetId) {
+                        return -1;
+                    }
+                    if (apn2.getApnSetId() == preferredApnSetId) {
+                        return 1;
+                    }
                     return 0;
                 }
             });
@@ -3644,7 +3568,7 @@
             pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID));
             for(ApnSetting p : mAllApnSettings) {
                 log("getPreferredApn: apnSetting=" + p);
-                if (p.id == pos && p.canHandleType(mRequestedApnType)) {
+                if (p.getId() == pos && p.canHandleType(mRequestedApnType)) {
                     log("getPreferredApn: X found apnSetting" + p);
                     cursor.close();
                     return p;
@@ -3721,7 +3645,7 @@
                         cleanUpAllConnections(false, Phone.REASON_PS_RESTRICT_ENABLED);
                         mReregisterOnReconnectFailure = false;
                     }
-                    ApnContext apnContext = mApnContextsById.get(DctConstants.APN_DEFAULT_ID);
+                    ApnContext apnContext = mApnContextsByType.get(ApnSetting.TYPE_DEFAULT);
                     if (apnContext != null) {
                         apnContext.setReason(Phone.REASON_PS_RESTRICT_ENABLED);
                         trySetupData(apnContext);
@@ -3859,19 +3783,6 @@
                 onSetUserDataEnabled(enabled);
                 break;
             }
-            // TODO - remove
-            case DctConstants.CMD_SET_DEPENDENCY_MET: {
-                boolean met = (msg.arg1 == DctConstants.ENABLED) ? true : false;
-                if (DBG) log("CMD_SET_DEPENDENCY_MET met=" + met);
-                Bundle bundle = msg.getData();
-                if (bundle != null) {
-                    String apnType = (String)bundle.get(DctConstants.APN_TYPE_KEY);
-                    if (apnType != null) {
-                        onSetDependencyMet(apnType, met);
-                    }
-                }
-                break;
-            }
             case DctConstants.CMD_SET_POLICY_DATA_ENABLE: {
                 final boolean enabled = (msg.arg1 == DctConstants.ENABLED) ? true : false;
                 onSetPolicyDataEnabled(enabled);
@@ -3937,7 +3848,7 @@
             }
             case DctConstants.EVENT_PROVISIONING_APN_ALARM: {
                 if (DBG) log("EVENT_PROVISIONING_APN_ALARM");
-                ApnContext apnCtx = mApnContextsById.get(DctConstants.APN_DEFAULT_ID);
+                ApnContext apnCtx = mApnContextsByType.get(ApnSetting.TYPE_DEFAULT);
                 if (apnCtx.isProvisioningApn() && apnCtx.isConnectedOrConnecting()) {
                     if (mProvisioningApnAlarmTag == msg.arg1) {
                         if (DBG) log("EVENT_PROVISIONING_APN_ALARM: Disconnecting");
@@ -4212,6 +4123,8 @@
         pw.println(" mUniqueIdGenerator=" + mUniqueIdGenerator);
         pw.println(" mDataRoamingLeakageLog= ");
         mDataRoamingLeakageLog.dump(fd, pw, args);
+        pw.println(" mApnSettingsInitializationLog= ");
+        mApnSettingsInitializationLog.dump(fd, pw, args);
         pw.flush();
         pw.println(" ***************************************");
         DcController dcc = mDcc;
@@ -4292,9 +4205,9 @@
         }
 
         if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_EMERGENCY)) {
-            apnContext = mApnContextsById.get(DctConstants.APN_EMERGENCY_ID);
+            apnContext = mApnContextsByType.get(ApnSetting.TYPE_EMERGENCY);
         } else if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_IMS)) {
-            apnContext = mApnContextsById.get(DctConstants.APN_IMS_ID);
+            apnContext = mApnContextsByType.get(ApnSetting.TYPE_IMS);
         } else {
             log("apnType is invalid, return null");
             return null;
@@ -4339,7 +4252,7 @@
         if (cursor != null) {
             if (cursor.getCount() > 0) {
                 if (cursor.moveToFirst()) {
-                    mEmergencyApn = makeApnSetting(cursor);
+                    mEmergencyApn = ApnSetting.makeApnSetting(cursor);
                 }
             }
             cursor.close();
@@ -4356,7 +4269,7 @@
             } else {
                 boolean hasEmergencyApn = false;
                 for (ApnSetting apn : mAllApnSettings) {
-                    if (ArrayUtils.contains(apn.types, PhoneConstants.APN_TYPE_EMERGENCY)) {
+                    if ((apn.getApnTypeBitmask() & ApnSetting.TYPE_EMERGENCY) > 0) {
                         hasEmergencyApn = true;
                         break;
                     }
@@ -4428,7 +4341,7 @@
             stopDataStallAlarm();
         }
 
-        mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT;
+        mRequestedApnType = ApnSetting.TYPE_DEFAULT;
 
         if (DBG) log("mDisconnectPendingCount = " + mDisconnectPendingCount);
         if (tearDown && mDisconnectPendingCount == 0) {
@@ -4507,7 +4420,7 @@
 
         TxRxSum preTxRxSum = new TxRxSum(mTxPkts, mRxPkts);
         TxRxSum curTxRxSum = new TxRxSum();
-        curTxRxSum.updateTxRxSum();
+        curTxRxSum.updateTotalTxRxSum();
         mTxPkts = curTxRxSum.txPkts;
         mRxPkts = curTxRxSum.rxPkts;
 
@@ -4671,7 +4584,7 @@
         long sent, received;
 
         TxRxSum preTxRxSum = new TxRxSum(mDataStallTxRxSum);
-        mDataStallTxRxSum.updateTxRxSum();
+        mDataStallTxRxSum.updateTcpTxRxSum();
 
         if (VDBG_STALL) {
             log("updateDataStallInfo: mDataStallTxRxSum=" + mDataStallTxRxSum +
@@ -4863,7 +4776,7 @@
     }
 
     private static DataProfile createDataProfile(ApnSetting apn) {
-        return createDataProfile(apn, apn.profileId);
+        return createDataProfile(apn, apn.getProfileId());
     }
 
     @VisibleForTesting
@@ -4872,7 +4785,7 @@
 
         int bearerBitmap = 0;
         bearerBitmap = ServiceState.convertNetworkTypeBitmaskToBearerBitmask(
-                apn.networkTypeBitmask);
+                apn.getNetworkTypeBitmask());
 
         if (bearerBitmap == 0) {
             profileType = DataProfile.TYPE_COMMON;
@@ -4882,11 +4795,13 @@
             profileType = DataProfile.TYPE_3GPP;
         }
 
-        return new DataProfile(profileId, apn.apn, apn.protocol,
-                apn.authType, apn.user, apn.password, profileType,
-                apn.maxConnsTime, apn.maxConns, apn.waitTime, apn.carrierEnabled, apn.typesBitmap,
-                apn.roamingProtocol, bearerBitmap, apn.mtu, apn.mvnoType, apn.mvnoMatchData,
-                apn.modemCognitive);
+        return new DataProfile(profileId, apn.getApnName(),
+            ApnSetting.getProtocolStringFromInt(apn.getProtocol()), apn.getAuthType(),
+            apn.getUser(), apn.getPassword(), profileType, apn.getMaxConnsTime(),
+            apn.getMaxConns(), apn.getWaitTime(), apn.isEnabled(), apn.getApnTypeBitmask(),
+            ApnSetting.getProtocolStringFromInt(apn.getRoamingProtocol()), bearerBitmap,
+            apn.getMtu(), ApnSetting.getMvnoTypeStringFromInt(apn.getMvnoType()),
+            apn.getMvnoMatchData(), apn.getModemCognitive());
     }
 
     private void onDataServiceBindingChanged(boolean bound) {
diff --git a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index 8c4e9aa..99082ee 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -16,13 +16,8 @@
 
 package com.android.internal.telephony.gsm;
 
-import android.app.Activity;
-import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
-import android.content.Intent;
 import android.os.AsyncResult;
 import android.os.Message;
-import android.provider.Telephony.Sms;
 import android.provider.Telephony.Sms.Intents;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
@@ -106,8 +101,9 @@
     }
 
     @Override
-    protected boolean shouldBlockSms() {
-        return SMSDispatcherUtil.shouldBlockSms(isCdmaMo(), mPhone);
+    protected boolean shouldBlockSmsForEcbm() {
+        // There is no such thing as ECBM for GSM. This only applies to CDMA.
+        return false;
     }
 
     @Override
@@ -187,6 +183,7 @@
                 + " mRetryCount=" + tracker.mRetryCount
                 + " mImsRetry=" + tracker.mImsRetry
                 + " mMessageRef=" + tracker.mMessageRef
+                + " mUsesImsServiceForIms=" + tracker.mUsesImsServiceForIms
                 + " SS=" + mPhone.getServiceState().getState());
 
         int ss = mPhone.getServiceState().getState();
@@ -202,8 +199,11 @@
         // sms over gsm is used:
         //   if sms over IMS is not supported AND
         //   this is not a retry case after sms over IMS failed
-        //     indicated by mImsRetry > 0
-        if (0 == tracker.mImsRetry && !isIms()) {
+        //     indicated by mImsRetry > 0 OR
+        //   this tracker uses ImsSmsDispatcher to handle SMS over IMS. This dispatcher has received
+        //     this message because the ImsSmsDispatcher has indicated that the message needs to
+        //     fall back to sending over CS.
+        if (0 == tracker.mImsRetry && !isIms() || tracker.mUsesImsServiceForIms) {
             if (tracker.mRetryCount == 0 && tracker.mExpectMore) {
                 mCi.sendSMSExpectMore(IccUtils.bytesToHexString(smsc),
                         IccUtils.bytesToHexString(pdu), reply);
diff --git a/src/java/com/android/internal/telephony/ims/ImsResolver.java b/src/java/com/android/internal/telephony/ims/ImsResolver.java
index c6a7efa..f0aee01 100644
--- a/src/java/com/android/internal/telephony/ims/ImsResolver.java
+++ b/src/java/com/android/internal/telephony/ims/ImsResolver.java
@@ -29,6 +29,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.telephony.CarrierConfigManager;
@@ -79,6 +80,10 @@
             "android.telephony.ims.EMERGENCY_MMTEL_FEATURE";
     public static final String METADATA_MMTEL_FEATURE = "android.telephony.ims.MMTEL_FEATURE";
     public static final String METADATA_RCS_FEATURE = "android.telephony.ims.RCS_FEATURE";
+    // Overrides the sanity permission check of android.permission.BIND_IMS_SERVICE for any
+    // ImsService that is connecting to the platform.
+    // This should ONLY be used for testing and should not be used in production ImsServices.
+    private static final String METADATA_OVERRIDE_PERM_CHECK = "override_bind_check";
 
     // Based on updates from PackageManager
     private static final int HANDLER_ADD_PACKAGE = 0;
@@ -88,8 +93,10 @@
     private static final int HANDLER_CONFIG_CHANGED = 2;
     // A query has been started for an ImsService to relay the features they support.
     private static final int HANDLER_START_DYNAMIC_FEATURE_QUERY = 3;
-    // A query to request ImsService features has completed.
-    private static final int HANDLER_DYNAMIC_FEATURE_QUERY_COMPLETE = 4;
+    // A query to request ImsService features has completed or the ImsService has updated features.
+    private static final int HANDLER_DYNAMIC_FEATURE_CHANGE = 4;
+    // Testing: Overrides the current configuration for ImsService binding
+    private static final int HANDLER_OVERRIDE_IMS_SERVICE_CONFIG = 5;
 
     // Delay between dynamic ImsService queries.
     private static final int DELAY_DYNAMIC_QUERY_MS = 5000;
@@ -328,6 +335,8 @@
     private final Object mBoundServicesLock = new Object();
     private final int mNumSlots;
     private final boolean mIsDynamicBinding;
+    // Package name of the default device service.
+    private String mDeviceService;
 
     // Synchronize all messages on a handler to ensure that the cache includes the most recent
     // version of the installed ImsServices.
@@ -345,7 +354,7 @@
             }
             case HANDLER_CONFIG_CHANGED: {
                 int slotId = (Integer) msg.obj;
-                maybeRebindService(slotId);
+                carrierConfigChanged(slotId);
                 break;
             }
             case HANDLER_START_DYNAMIC_FEATURE_QUERY: {
@@ -353,7 +362,7 @@
                 startDynamicQuery(info);
                 break;
             }
-            case HANDLER_DYNAMIC_FEATURE_QUERY_COMPLETE: {
+            case HANDLER_DYNAMIC_FEATURE_CHANGE: {
                 SomeArgs args = (SomeArgs) msg.obj;
                 ComponentName name = (ComponentName) args.arg1;
                 Set<ImsFeatureConfiguration.FeatureSlotPair> features =
@@ -362,6 +371,36 @@
                 dynamicQueryComplete(name, features);
                 break;
             }
+            case HANDLER_OVERRIDE_IMS_SERVICE_CONFIG: {
+                int slotId = msg.arg1;
+                // arg2 will be equal to 1 if it is a carrier service.
+                boolean isCarrierImsService = (msg.arg2 == 1);
+                String packageName = (String) msg.obj;
+                if (isCarrierImsService) {
+                    Log.i(TAG, "overriding carrier ImsService - slot=" + slotId + " packageName="
+                            + packageName);
+                    maybeRebindService(slotId, packageName);
+                } else {
+                    Log.i(TAG, "overriding device ImsService -  packageName=" + packageName);
+                    if (packageName == null || packageName.isEmpty()) {
+                        unbindImsService(getImsServiceInfoFromCache(mDeviceService));
+                    }
+                    mDeviceService = packageName;
+                    ImsServiceInfo deviceInfo = getImsServiceInfoFromCache(mDeviceService);
+                    if (deviceInfo == null) {
+                        // The package name is either "" or does not exist on the device.
+                        break;
+                    }
+                    if (deviceInfo.featureFromMetadata) {
+                        bindImsService(deviceInfo);
+                    } else {
+                        // newly added ImsServiceInfo that has not had features queried yet. Start
+                        // async bind and query features.
+                        scheduleQueryForFeatures(deviceInfo);
+                    }
+                }
+                break;
+            }
             default:
                 return false;
         }
@@ -377,7 +416,7 @@
                         Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
                     Log.d(TAG, "onComplete called for name: " + name + "features:"
                             + printFeatures(features));
-                    handleFeatureQueryComplete(name, features);
+                    handleFeaturesChanged(name, features);
                 }
 
                 @Override
@@ -387,8 +426,6 @@
                 }
             };
 
-    // Package name of the default device service.
-    private String mDeviceService;
     // Array index corresponds to slot Id associated with the service package name.
     private String[] mCarrierServices;
     // List index corresponds to Slot Id, Maps ImsFeature.FEATURE->bound ImsServiceController
@@ -594,6 +631,36 @@
         return null;
     }
 
+    // Used for testing only.
+    public boolean overrideImsServiceConfiguration(int slotId, boolean isCarrierService,
+            String packageName) {
+        if (slotId < 0 || slotId >= mNumSlots) {
+            Log.w(TAG, "overrideImsServiceConfiguration: invalid slotId!");
+            return false;
+        }
+
+        if (packageName == null) {
+            Log.w(TAG, "overrideImsServiceConfiguration: null packageName!");
+            return false;
+        }
+
+        // encode boolean to int for Message.
+        int carrierService = isCarrierService ? 1 : 0;
+        Message.obtain(mHandler, HANDLER_OVERRIDE_IMS_SERVICE_CONFIG, slotId, carrierService,
+                packageName).sendToTarget();
+        return true;
+    }
+
+    // used for testing only.
+    public String getImsServiceConfiguration(int slotId, boolean isCarrierService) {
+        if (slotId < 0 || slotId >= mNumSlots) {
+            Log.w(TAG, "getImsServiceConfiguration: invalid slotId!");
+            return "";
+        }
+
+        return isCarrierService ? mCarrierServices[slotId] : mDeviceService;
+    }
+
     private void putImsController(int slotId, int feature, ImsServiceController controller) {
         if (slotId < 0 || slotId >= mNumSlots || feature <= ImsFeature.FEATURE_INVALID
                 || feature >= ImsFeature.FEATURE_MAX) {
@@ -901,6 +968,21 @@
     }
 
     /**
+     * Implementation of
+     * {@link ImsServiceController.ImsServiceControllerCallbacks#imsServiceFeaturesChanged, which
+     * notify the ImsResolver of a change to the supported ImsFeatures of a connected ImsService.
+     */
+    public void imsServiceFeaturesChanged(ImsFeatureConfiguration config,
+            ImsServiceController controller) {
+        if (controller == null || config == null) {
+            return;
+        }
+        Log.i(TAG, "imsServiceFeaturesChanged: config=" + config.getServiceFeatures()
+                + ", ComponentName=" + controller.getComponentName());
+        handleFeaturesChanged(controller.getComponentName(), config.getServiceFeatures());
+    }
+
+    /**
      * Determines if the features specified should cause a bind or keep a binding active to an
      * ImsService.
      * @return true if MMTEL or RCS features are present, false if they are not or only
@@ -917,22 +999,31 @@
     // Possibly rebind to another ImsService if currently installed ImsServices were changed or if
     // the SIM card has changed.
     // Called from the handler ONLY
-    private void maybeRebindService(int slotId) {
+    private void maybeRebindService(int slotId, String newPackageName) {
         if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
             // not specified, replace package on all slots.
             for (int i = 0; i < mNumSlots; i++) {
-                updateBoundCarrierServices(i);
+                updateBoundCarrierServices(i, newPackageName);
             }
         } else {
-            updateBoundCarrierServices(slotId);
+            updateBoundCarrierServices(slotId, newPackageName);
         }
 
     }
 
-    private void updateBoundCarrierServices(int slotId) {
+    private void carrierConfigChanged(int slotId) {
         int subId = mSubscriptionManagerProxy.getSubId(slotId);
-        String newPackageName = mCarrierConfigManager.getConfigForSubId(subId).getString(
-                CarrierConfigManager.KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING, null);
+        PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId);
+        if (config != null) {
+            String newPackageName = config.getString(
+                    CarrierConfigManager.KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING, null);
+            maybeRebindService(slotId, newPackageName);
+        } else {
+            Log.w(TAG, "carrierConfigChanged: CarrierConfig is null!");
+        }
+    }
+
+    private void updateBoundCarrierServices(int slotId, String newPackageName) {
         if (slotId > SubscriptionManager.INVALID_SIM_SLOT_INDEX && slotId < mNumSlots) {
             String oldPackageName = mCarrierServices[slotId];
             mCarrierServices[slotId] = newPackageName;
@@ -997,12 +1088,12 @@
     /**
      * Schedules the processing of a completed query.
      */
-    private void handleFeatureQueryComplete(ComponentName name,
+    private void handleFeaturesChanged(ComponentName name,
             Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
         SomeArgs args = SomeArgs.obtain();
         args.arg1 = name;
         args.arg2 = features;
-        mHandler.obtainMessage(HANDLER_DYNAMIC_FEATURE_QUERY_COMPLETE, args).sendToTarget();
+        mHandler.obtainMessage(HANDLER_DYNAMIC_FEATURE_CHANGE, args).sendToTarget();
     }
 
     // Starts a dynamic query. Called from handler ONLY.
@@ -1022,7 +1113,7 @@
             Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
         ImsServiceInfo service = getImsServiceInfoFromCache(name.getPackageName());
         if (service == null) {
-            Log.w(TAG, "handleFeatureQueryComplete: Couldn't find cached info for name: "
+            Log.w(TAG, "handleFeaturesChanged: Couldn't find cached info for name: "
                     + name);
             return;
         }
@@ -1047,7 +1138,7 @@
     public boolean isResolvingBinding() {
         return mHandler.hasMessages(HANDLER_START_DYNAMIC_FEATURE_QUERY)
                 // We haven't processed this message yet, so it is still resolving.
-                || mHandler.hasMessages(HANDLER_DYNAMIC_FEATURE_QUERY_COMPLETE)
+                || mHandler.hasMessages(HANDLER_DYNAMIC_FEATURE_CHANGE)
                 || mFeatureQueryManager.isQueryInProgress();
     }
 
@@ -1164,9 +1255,11 @@
                 Log.i(TAG, "service name: " + info.name + ", manifest query: "
                         + info.featureFromMetadata);
                 // Check manifest permission to be sure that the service declares the correct
-                // permissions.
-                if (TextUtils.equals(serviceInfo.permission,
-                        Manifest.permission.BIND_IMS_SERVICE)) {
+                // permissions. Overridden if the METADATA_OVERRIDE_PERM_CHECK metadata is set to
+                // true.
+                // NOTE: METADATA_OVERRIDE_PERM_CHECK should only be set for testing.
+                if (TextUtils.equals(serviceInfo.permission, Manifest.permission.BIND_IMS_SERVICE)
+                        || serviceInfo.metaData.getBoolean(METADATA_OVERRIDE_PERM_CHECK, false)) {
                     infos.add(info);
                 } else {
                     Log.w(TAG, "ImsService is not protected with BIND_IMS_SERVICE permission: "
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceController.java b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
index e13a8ef..e2a0f43 100644
--- a/src/java/com/android/internal/telephony/ims/ImsServiceController.java
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
@@ -149,6 +149,16 @@
         }
     }
 
+    private ImsService.Listener mFeatureChangedListener = new ImsService.Listener() {
+        @Override
+        public void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) {
+            if (mCallbacks == null) {
+                return;
+            }
+            mCallbacks.imsServiceFeaturesChanged(c, ImsServiceController.this);
+        }
+    };
+
     /**
      * Defines callbacks that are used by the ImsServiceController to notify when an ImsService
      * has created or removed a new feature as well as the associated ImsServiceController.
@@ -162,6 +172,13 @@
          * Called by ImsServiceController when a new MMTEL or RCS feature has been removed.
          */
         void imsServiceFeatureRemoved(int slotId, int feature, ImsServiceController controller);
+
+        /**
+         * Called by the ImsServiceController when the ImsService has notified the framework that
+         * its features have changed.
+         */
+        void imsServiceFeaturesChanged(ImsFeatureConfiguration config,
+                ImsServiceController controller);
     }
 
     /**
@@ -552,6 +569,7 @@
         synchronized (mLock) {
             if (isServiceControllerAvailable()) {
                 Log.d(LOG_TAG, "notifyImsServiceReady");
+                mIImsServiceController.setListener(mFeatureChangedListener);
                 mIImsServiceController.notifyImsServiceReadyForFeatureCreation();
             }
         }
diff --git a/src/java/com/android/internal/telephony/ims/MmTelFeatureCompatAdapter.java b/src/java/com/android/internal/telephony/ims/MmTelFeatureCompatAdapter.java
index 8eec9db..7b0619b 100644
--- a/src/java/com/android/internal/telephony/ims/MmTelFeatureCompatAdapter.java
+++ b/src/java/com/android/internal/telephony/ims/MmTelFeatureCompatAdapter.java
@@ -155,6 +155,10 @@
 
         @Override
         public void registrationDisconnected(ImsReasonInfo imsReasonInfo) throws RemoteException {
+            // At de-registration, notify the framework that no IMS capabilities are currently
+            // available.
+            Log.i(TAG, "registrationDisconnected: resetting MMTEL capabilities.");
+            notifyCapabilitiesStatusChanged(new MmTelCapabilities());
             // Implemented in the Registration Adapter
         }
 
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index 1fec867..0a86867 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -1279,12 +1279,12 @@
 
     private CallForwardInfo getCallForwardInfo(ImsCallForwardInfo info) {
         CallForwardInfo cfInfo = new CallForwardInfo();
-        cfInfo.status = info.mStatus;
-        cfInfo.reason = getCFReasonFromCondition(info.mCondition);
+        cfInfo.status = info.getStatus();
+        cfInfo.reason = getCFReasonFromCondition(info.getCondition());
         cfInfo.serviceClass = SERVICE_CLASS_VOICE;
-        cfInfo.toa = info.mToA;
-        cfInfo.number = info.mNumber;
-        cfInfo.timeSeconds = info.mTimeSeconds;
+        cfInfo.toa = info.getToA();
+        cfInfo.number = info.getNumber();
+        cfInfo.timeSeconds = info.getTimeSeconds();
         return cfInfo;
     }
 
@@ -1308,10 +1308,10 @@
             }
         } else {
             for (int i = 0, s = infos.length; i < s; i++) {
-                if (infos[i].mCondition == ImsUtInterface.CDIV_CF_UNCONDITIONAL) {
+                if (infos[i].getCondition() == ImsUtInterface.CDIV_CF_UNCONDITIONAL) {
                     if (r != null) {
-                        setVoiceCallForwardingFlag(r, 1, (infos[i].mStatus == 1),
-                            infos[i].mNumber);
+                        setVoiceCallForwardingFlag(r, 1, (infos[i].getStatus() == 1),
+                                infos[i].getNumber());
                     }
                 }
                 cfInfos[i] = getCallForwardInfo(infos[i]);
@@ -1325,7 +1325,7 @@
         int[] cbInfos = new int[1];
         cbInfos[0] = SERVICE_CLASS_NONE;
 
-        if (infos[0].mStatus == 1) {
+        if (infos[0].getStatus() == 1) {
             cbInfos[0] = SERVICE_CLASS_VOICE;
         }
 
@@ -1336,7 +1336,7 @@
         int[] cwInfos = new int[2];
         cwInfos[0] = 0;
 
-        if (infos[0].mStatus == 1) {
+        if (infos[0].getStatus() == 1) {
             cwInfos[0] = 1;
             cwInfos[1] = SERVICE_CLASS_VOICE;
         }
@@ -1432,8 +1432,14 @@
                 ServiceState newServiceState = (ServiceState) ar.result;
                 // only update if roaming status changed
                 if (mRoaming != newServiceState.getRoaming()) {
-                    if (DBG) logd("Roaming state changed");
-                    updateRoamingState(newServiceState.getRoaming());
+                    if (DBG) logd("Roaming state changed - " + mRoaming);
+                    // Update WFC mode only if voice or data is in service.
+                    // The STATE_IN_SERVICE is checked to prevent wifi calling mode change
+                    // when phone moves from roaming to no service.
+                    boolean isInService =
+                            (newServiceState.getVoiceRegState() == ServiceState.STATE_IN_SERVICE ||
+                            newServiceState.getDataRegState() == ServiceState.STATE_IN_SERVICE);
+                    updateRoamingState(newServiceState.getRoaming(), isInService);
                 }
                 break;
             case EVENT_VOICE_CALL_ENDED:
@@ -1442,7 +1448,7 @@
                 // only update if roaming status changed
                 boolean newRoaming = getCurrentRoaming();
                 if (mRoaming != newRoaming) {
-                    updateRoamingState(newRoaming);
+                    updateRoamingState(newRoaming, true);
                 }
                 break;
 
@@ -1804,12 +1810,16 @@
         return mCT.getVtDataUsage(perUidStats);
     }
 
-    private void updateRoamingState(boolean newRoaming) {
+    private void updateRoamingState(boolean newRoaming, boolean isInService) {
         if (mCT.getState() == PhoneConstants.State.IDLE) {
             if (DBG) logd("updateRoamingState now: " + newRoaming);
             mRoaming = newRoaming;
-            ImsManager imsManager = ImsManager.getInstance(mContext, mPhoneId);
-            imsManager.setWfcMode(imsManager.getWfcMode(newRoaming), newRoaming);
+            if (isInService) {
+                ImsManager imsManager = ImsManager.getInstance(mContext, mPhoneId);
+                imsManager.setWfcMode(imsManager.getWfcMode(newRoaming), newRoaming);
+            } else {
+                if (DBG) Rlog.d(LOG_TAG, "updateRoamingState service state is OUT_OF_SERVICE");
+            }
         } else {
             if (DBG) logd("updateRoamingState postponed: " + newRoaming);
             mCT.registerForVoiceCallEnded(this,
@@ -1820,7 +1830,7 @@
     private boolean getCurrentRoaming() {
         TelephonyManager tm = (TelephonyManager) mContext
                 .getSystemService(Context.TELEPHONY_SERVICE);
-        return tm.isNetworkRoaming();
+        return tm.isNetworkRoaming(getSubId());
     }
 
     @Override
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index 541c74c..239f10a 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -1537,6 +1537,16 @@
         return mDesiredMute;
     }
 
+    /**
+     * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+     * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+     * and event flash to 16. Currently, event flash is not supported.
+     *
+     * @param c that represents the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+     * @param result the result message to send when done. If non-null, the {@link Message} must
+     *         contain a valid {@link android.os.Messenger} in the {@link Message#replyTo} field,
+     *         since this can be used across IPC boundaries.
+     */
     public void sendDtmf(char c, Message result) {
         if (DBG) log("sendDtmf");
 
@@ -2373,6 +2383,9 @@
                         mPendingMO.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED);
                         sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO);
                     }
+                    if (imsCall != mCallExpectedToResume) {
+                        mCallExpectedToResume = null;
+                    }
                 }
                 mPhone.notifySuppServiceFailed(Phone.SuppService.HOLD);
             }
@@ -2789,7 +2802,6 @@
                 @Override
                 public void onDeregistered(ImsReasonInfo imsReasonInfo) {
                     if (DBG) log("onImsDisconnected imsReasonInfo=" + imsReasonInfo);
-                    resetImsCapabilities();
                     mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
                     mPhone.setImsRegistered(false);
                     mPhone.processDisconnectReason(imsReasonInfo);
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
index 58af015..d63b9c2 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
@@ -23,6 +23,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Messenger;
 import android.os.PersistableBundle;
 import android.os.PowerManager;
 import android.os.Registrant;
@@ -79,6 +80,7 @@
 
     private UUSInfo mUusInfo;
     private Handler mHandler;
+    private Messenger mHandlerMessenger;
 
     private PowerManager.WakeLock mPartialWakeLock;
 
@@ -171,6 +173,7 @@
 
         mOwner = ct;
         mHandler = new MyHandler(mOwner.getLooper());
+        mHandlerMessenger = new Messenger(mHandler);
         mImsCall = imsCall;
 
         if ((imsCall != null) && (imsCall.getCallProfile() != null)) {
@@ -495,7 +498,9 @@
     private boolean
     processPostDialChar(char c) {
         if (PhoneNumberUtils.is12Key(c)) {
-            mOwner.sendDtmf(c, mHandler.obtainMessage(EVENT_DTMF_DONE));
+            Message dtmfComplete = mHandler.obtainMessage(EVENT_DTMF_DONE);
+            dtmfComplete.replyTo = mHandlerMessenger;
+            mOwner.sendDtmf(c, dtmfComplete);
         } else if (c == PhoneNumberUtils.PAUSE) {
             // From TS 22.101:
             // It continues...
@@ -991,11 +996,29 @@
     }
 
     public void onRttMessageReceived(String message) {
-        getOrCreateRttTextHandler().sendToInCall(message);
+        synchronized (this) {
+            if (mRttTextHandler == null) {
+                Rlog.w(LOG_TAG, "onRttMessageReceived: RTT text handler not available."
+                        + " Attempting to create one.");
+                if (mRttTextStream == null) {
+                    Rlog.e(LOG_TAG, "onRttMessageReceived:"
+                            + " Unable to process incoming message. No textstream available");
+                    return;
+                }
+                createRttTextHandler();
+            }
+        }
+        mRttTextHandler.sendToInCall(message);
     }
 
     public void setCurrentRttTextStream(android.telecom.Connection.RttTextStream rttTextStream) {
-        mRttTextStream = rttTextStream;
+        synchronized (this) {
+            mRttTextStream = rttTextStream;
+            if (mRttTextHandler == null && mIsRttEnabledForCall) {
+                Rlog.i(LOG_TAG, "setCurrentRttTextStream: Creating a text handler");
+                createRttTextHandler();
+            }
+        }
     }
 
     public boolean hasRttTextStream() {
@@ -1007,20 +1030,24 @@
     }
 
     public void startRttTextProcessing() {
-        if (mRttTextStream == null) {
-            Rlog.w(LOG_TAG, "startRttTextProcessing: no RTT text stream. Ignoring.");
-            return;
+        synchronized (this) {
+            if (mRttTextStream == null) {
+                Rlog.w(LOG_TAG, "startRttTextProcessing: no RTT text stream. Ignoring.");
+                return;
+            }
+            if (mRttTextHandler != null) {
+                Rlog.w(LOG_TAG, "startRttTextProcessing: RTT text handler already exists");
+                return;
+            }
+            createRttTextHandler();
         }
-        getOrCreateRttTextHandler().initialize(mRttTextStream);
     }
 
-    private ImsRttTextHandler getOrCreateRttTextHandler() {
-        if (mRttTextHandler != null) {
-            return mRttTextHandler;
-        }
+    // Make sure to synchronize on ImsPhoneConnection.this before calling.
+    private void createRttTextHandler() {
         mRttTextHandler = new ImsRttTextHandler(Looper.getMainLooper(),
                 (message) -> getImsCall().sendRttMessage(message));
-        return mRttTextHandler;
+        mRttTextHandler.initialize(mRttTextStream);
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
index 811d30e..87e51ec 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
@@ -36,13 +36,13 @@
 import android.os.ResultReceiver;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.Rlog;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsSsData;
+import android.telephony.ims.ImsSsInfo;
 import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
 
 import com.android.ims.ImsException;
-import android.telephony.ims.ImsReasonInfo;
-import android.telephony.ims.ImsSsData;
-import android.telephony.ims.ImsSsInfo;
 import com.android.ims.ImsUtInterface;
 import com.android.internal.telephony.CallForwardInfo;
 import com.android.internal.telephony.CallStateException;
@@ -50,7 +50,6 @@
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.MmiCode;
 import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.uicc.IccRecords;
 
 import java.util.regex.Matcher;
@@ -1480,11 +1479,11 @@
                 ssInfo = (ImsSsInfo) ssInfoResp.getParcelable(UT_BUNDLE_KEY_SSINFO);
                 if (ssInfo != null) {
                     Rlog.d(LOG_TAG,
-                            "onSuppSvcQueryComplete: ImsSsInfo mStatus = " + ssInfo.mStatus);
-                    if (ssInfo.mStatus == ImsSsInfo.DISABLED) {
+                            "onSuppSvcQueryComplete: ImsSsInfo mStatus = " + ssInfo.getStatus());
+                    if (ssInfo.getStatus() == ImsSsInfo.DISABLED) {
                         sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
                         mState = State.COMPLETE;
-                    } else if (ssInfo.mStatus == ImsSsInfo.ENABLED) {
+                    } else if (ssInfo.getStatus() == ImsSsInfo.ENABLED) {
                         sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled));
                         mState = State.COMPLETE;
                     } else {
@@ -1532,10 +1531,10 @@
                 sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
             } else {
                 for (int i = 0, s = infos.length; i < s ; i++) {
-                    if (infos[i].mIcbNum !=null) {
-                        sb.append("Num: " + infos[i].mIcbNum + " status: "
-                                + infos[i].mStatus + "\n");
-                    } else if (infos[i].mStatus == 1) {
+                    if (infos[i].getIcbNum() != null) {
+                        sb.append("Num: " + infos[i].getIcbNum() + " status: "
+                                + infos[i].getStatus() + "\n");
+                    } else if (infos[i].getStatus() == 1) {
                         sb.append(mContext.getText(com.android.internal
                                 .R.string.serviceEnabled));
                     } else {
@@ -1726,7 +1725,7 @@
     }
 
     void parseSsData(ImsSsData ssData) {
-        ImsException ex = (ssData.result != RILConstants.SUCCESS)
+        ImsException ex = (ssData.result != ImsSsData.RESULT_SUCCESS)
                 ? new ImsException(null, ssData.result) : null;
         mSc = getScStringFromScType(ssData.serviceType);
         mAction = getActionStringFromReqType(ssData.requestType);
@@ -1737,7 +1736,7 @@
             case ImsSsData.SS_DEACTIVATION:
             case ImsSsData.SS_REGISTRATION:
             case ImsSsData.SS_ERASURE:
-                if ((ssData.result == RILConstants.SUCCESS)
+                if ((ssData.result == ImsSsData.RESULT_SUCCESS)
                         && ssData.isTypeUnConditional()) {
                     /*
                      * When ssData.serviceType is unconditional (SS_CFU or SS_CF_ALL) and
@@ -1758,30 +1757,31 @@
                         Rlog.e(LOG_TAG, "setCallForwardingFlag aborted. sim records is null.");
                     }
                 }
-                onSetComplete(null, new AsyncResult(null, ssData.cfInfo, ex));
+                onSetComplete(null, new AsyncResult(null, ssData.getCallForwardInfo(), ex));
                 break;
             case ImsSsData.SS_INTERROGATION:
                 if (ssData.isTypeClir()) {
                     Rlog.d(LOG_TAG, "CLIR INTERROGATION");
                     Bundle clirInfo = new Bundle();
-                    clirInfo.putIntArray(UT_BUNDLE_KEY_CLIR, ssData.ssInfo);
+                    clirInfo.putIntArray(UT_BUNDLE_KEY_CLIR, ssData.getSuppServiceInfo());
                     onQueryClirComplete(new AsyncResult(null, clirInfo, ex));
                 } else if (ssData.isTypeCF()) {
                     Rlog.d(LOG_TAG, "CALL FORWARD INTERROGATION");
                     onQueryCfComplete(new AsyncResult(null, mPhone
-                            .handleCfQueryResult(ssData.cfInfo), ex));
+                            .handleCfQueryResult(ssData.getCallForwardInfo()), ex));
                 } else if (ssData.isTypeBarring()) {
-                    onSuppSvcQueryComplete(new AsyncResult(null, ssData.ssInfo, ex));
+                    onSuppSvcQueryComplete(new AsyncResult(null, ssData.getSuppServiceInfo(), ex));
                 } else if (ssData.isTypeColr() || ssData.isTypeClip() || ssData.isTypeColp()) {
-                    ImsSsInfo ssInfo = new ImsSsInfo();
-                    ssInfo.mStatus = ssData.ssInfo[0];
+                    int[] suppServiceInfo = ssData.getSuppServiceInfo();
+                    ImsSsInfo ssInfo = new ImsSsInfo(suppServiceInfo[0], null);
                     Bundle clInfo = new Bundle();
                     clInfo.putParcelable(UT_BUNDLE_KEY_SSINFO, ssInfo);
                     onSuppSvcQueryComplete(new AsyncResult(null, clInfo, ex));
                 } else if (ssData.isTypeIcb()) {
-                    onIcbQueryComplete(new AsyncResult(null, ssData.imsSsInfo, ex));
+                    onIcbQueryComplete(new AsyncResult(null, ssData.getImsSpecificSuppServiceInfo(),
+                            ex));
                 } else {
-                    onQueryComplete(new AsyncResult(null, ssData.ssInfo, ex));
+                    onQueryComplete(new AsyncResult(null, ssData.getSuppServiceInfo(), ex));
                 }
                 break;
             default:
diff --git a/src/java/com/android/internal/telephony/metrics/SmsSessionEventBuilder.java b/src/java/com/android/internal/telephony/metrics/SmsSessionEventBuilder.java
index 530be1d..5004ce7 100644
--- a/src/java/com/android/internal/telephony/metrics/SmsSessionEventBuilder.java
+++ b/src/java/com/android/internal/telephony/metrics/SmsSessionEventBuilder.java
@@ -88,4 +88,9 @@
         mEvent.format = format;
         return this;
     }
+
+    public SmsSessionEventBuilder setCellBroadcastMessage(SmsSession.Event.CBMessage msg) {
+        mEvent.cellBroadcastMessage = msg;
+        return this;
+    }
 }
diff --git a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
index 21f8309..8b9d580 100644
--- a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
+++ b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
@@ -1705,7 +1705,7 @@
      * @param tech SMS RAT
      * @param format SMS format. Either 3GPP or 3GPP2.
      */
-    public void writeRilSendSms(int phoneId, int rilSerial, int tech, int format) {
+    public synchronized void writeRilSendSms(int phoneId, int rilSerial, int tech, int format) {
         InProgressSmsSession smsSession = startNewSmsSessionIfNeeded(phoneId);
 
         smsSession.addEvent(new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_SEND)
@@ -1724,7 +1724,7 @@
      * @param tech SMS RAT
      * @param format SMS format. Either 3GPP or 3GPP2.
      */
-    public void writeRilNewSms(int phoneId, int tech, int format) {
+    public synchronized void writeRilNewSms(int phoneId, int tech, int format) {
         InProgressSmsSession smsSession = startNewSmsSessionIfNeeded(phoneId);
 
         smsSession.addEvent(new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_RECEIVED)
@@ -1736,6 +1736,42 @@
     }
 
     /**
+     * Write incoming Broadcast SMS event
+     *
+     * @param phoneId Phone id
+     * @param format CB msg format
+     * @param priority CB msg priority
+     * @param isCMAS true if msg is CMAS
+     * @param isETWS true if msg is ETWS
+     * @param serviceCategory Service category of CB msg
+     */
+    public synchronized void writeNewCBSms(int phoneId, int format, int priority, boolean isCMAS,
+                                           boolean isETWS, int serviceCategory) {
+        InProgressSmsSession smsSession = startNewSmsSessionIfNeeded(phoneId);
+
+        int type;
+        if (isCMAS) {
+            type = SmsSession.Event.CBMessageType.CMAS;
+        } else if (isETWS) {
+            type = SmsSession.Event.CBMessageType.ETWS;
+        } else {
+            type = SmsSession.Event.CBMessageType.OTHER;
+        }
+
+        SmsSession.Event.CBMessage cbm = new SmsSession.Event.CBMessage();
+        cbm.msgFormat = format;
+        cbm.msgPriority = priority + 1;
+        cbm.msgType = type;
+        cbm.serviceCategory = serviceCategory;
+
+        smsSession.addEvent(new SmsSessionEventBuilder(SmsSession.Event.Type.CB_SMS_RECEIVED)
+                .setCellBroadcastMessage(cbm)
+        );
+
+        finishSmsSessionIfNeeded(smsSession);
+    }
+
+    /**
      * Write NITZ event
      *
      * @param phoneId Phone id
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCard.java b/src/java/com/android/internal/telephony/uicc/UiccCard.java
index a59e186..361f70b 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCard.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCard.java
@@ -46,7 +46,9 @@
     public static final String EXTRA_ICC_CARD_ADDED =
             "com.android.internal.telephony.uicc.ICC_CARD_ADDED";
 
-    private final Object mLock = new Object();
+    // The lock object is created by UiccSlot that owns this UiccCard - this is to share the lock
+    // between UiccSlot, UiccCard and UiccProfile for now.
+    private final Object mLock;
     private CardState mCardState;
     private String mIccid;
     protected String mCardId;
@@ -55,10 +57,11 @@
     private CommandsInterface mCi;
     private final int mPhoneId;
 
-    public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId) {
+    public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId, Object lock) {
         if (DBG) log("Creating");
         mCardState = ics.mCardState;
         mPhoneId = phoneId;
+        mLock = lock;
         update(c, ci, ics);
     }
 
@@ -83,7 +86,7 @@
             if (mCardState != CardState.CARDSTATE_ABSENT) {
                 if (mUiccProfile == null) {
                     mUiccProfile = TelephonyComponentFactory.getInstance().makeUiccProfile(
-                            mContext, mCi, ics, mPhoneId, this);
+                            mContext, mCi, ics, mPhoneId, this, mLock);
                 } else {
                     mUiccProfile.update(mContext, mCi, ics);
                 }
@@ -227,13 +230,13 @@
      *
      * A null aid implies a card level reset - all applications must be reset.
      *
-     * @deprecated Please use {@link UiccProfile#resetAppWithAid(String)} instead.
+     * @deprecated Please use {@link UiccProfile#resetAppWithAid(String, boolean)} instead.
      */
     @Deprecated
-    public boolean resetAppWithAid(String aid) {
+    public boolean resetAppWithAid(String aid, boolean reset) {
         synchronized (mLock) {
             if (mUiccProfile != null) {
-                return mUiccProfile.resetAppWithAid(aid);
+                return mUiccProfile.resetAppWithAid(aid, reset);
             } else {
                 return false;
             }
diff --git a/src/java/com/android/internal/telephony/uicc/UiccController.java b/src/java/com/android/internal/telephony/uicc/UiccController.java
index 033f0a1..e79329a 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccController.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccController.java
@@ -642,14 +642,17 @@
 
         boolean changed = false;
         switch(resp.refreshResult) {
+            // Reset the required apps when we know about the refresh so that
+            // anyone interested does not get stale state.
             case IccRefreshResponse.REFRESH_RESULT_RESET:
+                changed = uiccCard.resetAppWithAid(resp.aid, true /* reset */);
+                break;
             case IccRefreshResponse.REFRESH_RESULT_INIT:
-                 // Reset the required apps when we know about the refresh so that
-                 // anyone interested does not get stale state.
-                 changed = uiccCard.resetAppWithAid(resp.aid);
-                 break;
+                // don't dispose CatService on SIM REFRESH of type INIT
+                changed = uiccCard.resetAppWithAid(resp.aid, false /* initialize */);
+                break;
             default:
-                 return;
+                return;
         }
 
         if (changed && resp.refreshResult == IccRefreshResponse.REFRESH_RESULT_RESET) {
diff --git a/src/java/com/android/internal/telephony/uicc/UiccProfile.java b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
index f967d80..e5ee6f9 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccProfile.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
@@ -88,7 +88,9 @@
 
     private static final String OPERATOR_BRAND_OVERRIDE_PREFIX = "operator_branding_";
 
-    private final Object mLock = new Object();
+    // The lock object is created by UiccSlot that owns the UiccCard that owns this UiccProfile.
+    // This is to share the lock between UiccSlot, UiccCard and UiccProfile for now.
+    private final Object mLock;
     private PinState mUniversalPinState;
     private int mGsmUmtsSubscriptionAppIndex;
     private int mCdmaSubscriptionAppIndex;
@@ -214,8 +216,9 @@
     };
 
     public UiccProfile(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId,
-            UiccCard uiccCard) {
+            UiccCard uiccCard, Object lock) {
         if (DBG) log("Creating profile");
+        mLock = lock;
         mUiccCard = uiccCard;
         mPhoneId = phoneId;
         // set current app type based on phone type - do this before calling update() as that
@@ -1237,8 +1240,12 @@
      * Resets the application with the input AID. Returns true if any changes were made.
      *
      * A null aid implies a card level reset - all applications must be reset.
+     *
+     * @param aid aid of the application which should be reset; null imples all applications
+     * @param reset true if reset is required. false for initialization.
+     * @return boolean indicating if there was any change made as part of the reset
      */
-    public boolean resetAppWithAid(String aid) {
+    public boolean resetAppWithAid(String aid, boolean reset) {
         synchronized (mLock) {
             boolean changed = false;
             for (int i = 0; i < mUiccApplications.length; i++) {
@@ -1250,11 +1257,12 @@
                     changed = true;
                 }
             }
-            if (TextUtils.isEmpty(aid)) {
+            if (reset && TextUtils.isEmpty(aid)) {
                 if (mCarrierPrivilegeRules != null) {
                     mCarrierPrivilegeRules = null;
                     changed = true;
                 }
+                // CatService shall be disposed only when a card level reset happens.
                 if (mCatService != null) {
                     mCatService.dispose();
                     mCatService = null;
diff --git a/src/java/com/android/internal/telephony/uicc/UiccSlot.java b/src/java/com/android/internal/telephony/uicc/UiccSlot.java
index ffaad60..25ef637 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccSlot.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccSlot.java
@@ -112,9 +112,9 @@
                 }
 
                 if (!mIsEuicc) {
-                    mUiccCard = new UiccCard(mContext, mCi, ics, mPhoneId);
+                    mUiccCard = new UiccCard(mContext, mCi, ics, mPhoneId, mLock);
                 } else {
-                    mUiccCard = new EuiccCard(mContext, mCi, ics, phoneId);
+                    mUiccCard = new EuiccCard(mContext, mCi, ics, phoneId, mLock);
                 }
             } else {
                 if (mUiccCard != null) {
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java b/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java
index 6acfb80..103c75b 100644
--- a/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java
+++ b/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java
@@ -116,8 +116,8 @@
     private EuiccSpecVersion mSpecVersion;
     private volatile String mEid;
 
-    public EuiccCard(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId) {
-        super(c, ci, ics, phoneId);
+    public EuiccCard(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId, Object lock) {
+        super(c, ci, ics, phoneId, lock);
         // TODO: Set supportExtendedApdu based on ATR.
         mApduSender = new ApduSender(ci, ISD_R_AID, false /* supportExtendedApdu */);
 
diff --git a/src/java/com/android/internal/telephony/util/SMSDispatcherUtil.java b/src/java/com/android/internal/telephony/util/SMSDispatcherUtil.java
index a3c7926..17f2611 100644
--- a/src/java/com/android/internal/telephony/util/SMSDispatcherUtil.java
+++ b/src/java/com/android/internal/telephony/util/SMSDispatcherUtil.java
@@ -36,17 +36,6 @@
     private SMSDispatcherUtil() {}
 
     /**
-     * Whether to block SMS or not.
-     *
-     * @param isCdma true if cdma format should be used.
-     * @param phone the Phone to use
-     * @return true to block sms; false otherwise.
-     */
-    public static boolean shouldBlockSms(boolean isCdma, Phone phone) {
-        return isCdma && phone.isInEcm();
-    }
-
-    /**
      * Trigger the proper implementation for getting submit pdu for text sms based on format.
      *
      * @param isCdma true if cdma format should be used.
diff --git a/src/java/com/android/internal/telephony/util/TimeStampedValue.java b/src/java/com/android/internal/telephony/util/TimeStampedValue.java
deleted file mode 100644
index e2628f6..0000000
--- a/src/java/com/android/internal/telephony/util/TimeStampedValue.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 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.util;
-
-import android.os.SystemClock;
-
-/**
- * A pair containing a value and an associated time stamp.
- *
- * @param <T> The type of the value.
- */
-public final class TimeStampedValue<T> {
-
-    /** The value. */
-    public final T mValue;
-
-    /**
-     * The value of {@link SystemClock#elapsedRealtime} or equivalent when value was
-     * determined.
-     */
-    public final long mElapsedRealtime;
-
-    public TimeStampedValue(T value, long elapsedRealtime) {
-        this.mValue = value;
-        this.mElapsedRealtime = elapsedRealtime;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        TimeStampedValue<?> that = (TimeStampedValue<?>) o;
-
-        if (mElapsedRealtime != that.mElapsedRealtime) {
-            return false;
-        }
-        return mValue != null ? mValue.equals(that.mValue) : that.mValue == null;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = mValue != null ? mValue.hashCode() : 0;
-        result = 31 * result + (int) (mElapsedRealtime ^ (mElapsedRealtime >>> 32));
-        return result;
-    }
-
-    @Override
-    public String toString() {
-        return "TimeStampedValue{"
-                + "mValue=" + mValue
-                + ", elapsedRealtime=" + mElapsedRealtime
-                + '}';
-    }
-}
diff --git a/tests/telephonytests/Android.mk b/tests/telephonytests/Android.mk
index 3356e68..6709d46 100644
--- a/tests/telephonytests/Android.mk
+++ b/tests/telephonytests/Android.mk
@@ -20,7 +20,4 @@
 
 LOCAL_COMPATIBILITY_SUITE := device-tests
 
-# b/72575505
-LOCAL_ERROR_PRONE_FLAGS := -Xep:JUnit4TestNotRun:WARN
-
 include $(BUILD_PACKAGE)
diff --git a/tests/telephonytests/src/android/telephony/ims/MmTelFeatureTests.java b/tests/telephonytests/src/android/telephony/ims/MmTelFeatureTests.java
index ccbc251..6be2fb0 100644
--- a/tests/telephonytests/src/android/telephony/ims/MmTelFeatureTests.java
+++ b/tests/telephonytests/src/android/telephony/ims/MmTelFeatureTests.java
@@ -17,14 +17,19 @@
 package android.telephony.ims;
 
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
-import android.os.RemoteException;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
 import android.support.test.runner.AndroidJUnit4;
+import android.telecom.TelecomManager;
 import android.telephony.ims.aidl.IImsMmTelFeature;
 import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.feature.MmTelFeature;
@@ -32,6 +37,7 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.ims.internal.IImsCallSession;
+import com.android.internal.telephony.ims.ImsTestBase;
 
 import org.junit.After;
 import org.junit.Before;
@@ -41,23 +47,55 @@
 import org.mockito.Mockito;
 
 @RunWith(AndroidJUnit4.class)
-public class MmTelFeatureTests {
+public class MmTelFeatureTests extends ImsTestBase {
 
     private static final int TEST_CAPABILITY = 1;
     private static final int TEST_RADIO_TECH = 0;
 
+    private static final int TEST_TTY_RESULT = 0;
+    private static final int TEST_SEND_DTMF_RESULT = 1;
+    private static final int TEST_RESULT_MAX = 2;
+
+    private static final int TEST_RESULT_DELAY_MS = 5000;
+
     private android.telephony.ims.TestMmTelFeature mFeature;
     private IImsMmTelFeature mFeatureBinder;
     private ImsFeature.CapabilityCallback mCapabilityCallback;
     private MmTelFeature.Listener mListener;
 
+    // set to true when the handler receives a message back from the Feature.
+    private boolean[] mHandlerResults;
+
+    private class TestHandler extends Handler {
+
+        TestHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case TEST_TTY_RESULT:
+                    mHandlerResults[TEST_TTY_RESULT] = true;
+                    break;
+                case TEST_SEND_DTMF_RESULT:
+                    mHandlerResults[TEST_SEND_DTMF_RESULT] = true;
+                    break;
+            }
+        }
+    }
+    private final Handler mHandler = new TestHandler(Looper.getMainLooper());
+    private final Messenger mHandlerMessenger = new Messenger(mHandler);
+
     @Before
-    public void setup() throws RemoteException {
+    public void setup() throws Exception {
+        super.setUp();
         mFeature = new TestMmTelFeature();
         mFeatureBinder = mFeature.getBinder();
         mCapabilityCallback = spy(new ImsFeature.CapabilityCallback());
         mListener = spy(new MmTelFeature.Listener());
         mFeatureBinder.setListener(mListener);
+        mHandlerResults = new boolean[TEST_RESULT_MAX];
     }
 
     @After
@@ -91,4 +129,25 @@
 
         assertEquals(sessionBinder, captor.getValue());
     }
+
+    @SmallTest
+    @Test
+    public void testSetTtyMessageMessenger() throws Exception {
+        Message resultMessage = Message.obtain(mHandler, TEST_TTY_RESULT);
+        resultMessage.replyTo = mHandlerMessenger;
+        mFeatureBinder.setUiTtyMode(TelecomManager.TTY_MODE_FULL, resultMessage);
+        waitForHandlerAction(mHandler, TEST_RESULT_DELAY_MS);
+        assertTrue(mHandlerResults[TEST_TTY_RESULT]);
+    }
+
+    @SmallTest
+    @Test
+    public void testSendDtmfMessageMessenger() throws Exception {
+        Message resultMessage = Message.obtain(mHandler, TEST_SEND_DTMF_RESULT);
+        resultMessage.replyTo = mHandlerMessenger;
+        IImsCallSession callSession = mFeatureBinder.createCallSession(null);
+        callSession.sendDtmf('0', resultMessage);
+        waitForHandlerAction(mHandler, TEST_RESULT_DELAY_MS);
+        assertTrue(mHandlerResults[TEST_SEND_DTMF_RESULT]);
+    }
 }
diff --git a/tests/telephonytests/src/android/telephony/ims/TestMmTelFeature.java b/tests/telephonytests/src/android/telephony/ims/TestMmTelFeature.java
index 795d1c0..6414403 100644
--- a/tests/telephonytests/src/android/telephony/ims/TestMmTelFeature.java
+++ b/tests/telephonytests/src/android/telephony/ims/TestMmTelFeature.java
@@ -16,6 +16,7 @@
 
 package android.telephony.ims;
 
+import android.os.Message;
 import android.os.RemoteException;
 import android.telephony.ims.feature.CapabilityChangeRequest;
 import android.telephony.ims.feature.ImsFeature;
@@ -33,6 +34,22 @@
     public CapabilityChangeRequest lastRequest;
     public boolean isUtInterfaceCalled = false;
 
+    private final TestImsCallSession mCallSession = new TestImsCallSession();
+    private class TestImsCallSession extends ImsCallSessionImplBase {
+
+        @Override
+        public void sendDtmf(char c, Message result) {
+            // Just call result to signify complete for test.
+            if (result.replyTo != null) {
+                try {
+                    result.replyTo.send(result);
+                } catch (RemoteException e) {
+                    // eat error, test will fail.
+                }
+            }
+        }
+    }
+
     public void incomingCall(ImsCallSessionImplBase c) throws RemoteException {
         notifyIncomingCall(c, null);
     }
@@ -44,7 +61,7 @@
 
     @Override
     public ImsCallSessionImplBase createCallSession(ImsCallProfile profile) {
-        return super.createCallSession(profile);
+        return mCallSession;
     }
 
     @Override
@@ -64,6 +81,16 @@
     }
 
     @Override
+    public void setUiTtyMode(int mode, Message onCompleteMessage) {
+        try {
+            // just send complete message.
+            onCompleteMessage.replyTo.send(onCompleteMessage);
+        } catch (RemoteException e) {
+            // do nothing, test will fail.
+        }
+    }
+
+    @Override
     public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability,
             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
         // Base implementation - Override to provide functionality
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
index 8be2f60..bd20e4a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
@@ -16,22 +16,10 @@
 
 package com.android.internal.telephony;
 
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.res.Resources;
-import android.os.HandlerThread;
-import android.os.Message;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
 
 import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.isA;
@@ -39,6 +27,28 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.provider.Settings;
+import android.telephony.CarrierConfigManager;
+import android.telephony.ServiceState;
+import android.test.mock.MockContentResolver;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Map;
+
 /**
  * Unit tests for {@link com.android.internal.telephony.CarrierServiceStateTracker}.
  */
@@ -47,25 +57,34 @@
     public static final String LOG_TAG = "CSST";
     public static final int TEST_TIMEOUT = 5000;
 
+    private CarrierServiceStateTracker mSpyCarrierSST;
     private CarrierServiceStateTracker mCarrierSST;
     private CarrierServiceStateTrackerTestHandler mCarrierServiceStateTrackerTestHandler;
-    private  CarrierServiceStateTracker.PrefNetworkNotification mPrefNetworkNotification;
-    private  CarrierServiceStateTracker.EmergencyNetworkNotification mEmergencyNetworkNotification;
+    private FakeContentResolver mFakeContentResolver;
 
-    @Mock Context mContext;
-    @Mock ServiceStateTracker mServiceStateTracker;
-    @Mock NotificationManager mNotificationManager;
-    @Mock Resources mResources;
+    NotificationManager mNotificationManager;
+    PersistableBundle mBundle;
+
+    private class FakeContentResolver extends MockContentResolver {
+        @Override
+        public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
+            super.notifyChange(uri, observer, syncToNetwork);
+            logd("onChanged(uri=" + uri + ")" + observer);
+            if (observer != null) {
+                observer.dispatchChange(false, uri);
+            }
+        }
+    }
 
     private class CarrierServiceStateTrackerTestHandler extends HandlerThread {
-
         private CarrierServiceStateTrackerTestHandler(String name) {
             super(name);
         }
 
         @Override
         public void onLooperPrepared() {
-            mCarrierSST = spy(new CarrierServiceStateTracker(mPhone, mServiceStateTracker));
+            mCarrierSST = new CarrierServiceStateTracker(mPhone, mSST);
+            mSpyCarrierSST = spy(mCarrierSST);
             setReady(true);
         }
     }
@@ -75,15 +94,29 @@
         MockitoAnnotations.initMocks(this);
         logd(LOG_TAG + "Setup!");
         super.setUp(getClass().getSimpleName());
+        mBundle = mContextFixture.getCarrierConfigBundle();
+        when(mPhone.getSubId()).thenReturn(1);
         mCarrierServiceStateTrackerTestHandler =
                 new CarrierServiceStateTrackerTestHandler(getClass().getSimpleName());
         mCarrierServiceStateTrackerTestHandler.start();
-        when(mContext.getResources()).thenReturn(mResources);
-        when(mContext.getPackageManager()).thenReturn(mPackageManager);
-        when(mContext.getApplicationInfo()).thenReturn(new ApplicationInfo());
+        mFakeContentResolver = new CarrierServiceStateTrackerTest.FakeContentResolver();
+
+        when(mPhone.getContext().getContentResolver()).thenReturn(mFakeContentResolver);
+
+        mNotificationManager = (NotificationManager) mContext.getSystemService(
+                Context.NOTIFICATION_SERVICE);
+
+        setDefaultValues();
         waitUntilReady();
     }
 
+    private void setDefaultValues() {
+        mBundle.putInt(CarrierConfigManager.KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT,
+                0);
+        mBundle.putInt(CarrierConfigManager.KEY_EMERGENCY_NOTIFICATION_DELAY_INT,
+                0);
+    }
+
     @After
     public void tearDown() throws Exception {
         mCarrierServiceStateTrackerTestHandler.quit();
@@ -94,12 +127,12 @@
     @SmallTest
     public void testCancelBothNotifications() {
         logd(LOG_TAG + ":testCancelBothNotifications()");
-        Message notificationMsg = mCarrierSST.obtainMessage(
+        Message notificationMsg = mSpyCarrierSST.obtainMessage(
                 CarrierServiceStateTracker.CARRIER_EVENT_DATA_REGISTRATION, null);
-        doReturn(false).when(mCarrierSST).evaluateSendingMessage(any());
-        doReturn(mNotificationManager).when(mCarrierSST).getNotificationManager(any());
-        mCarrierSST.handleMessage(notificationMsg);
-        waitForHandlerAction(mCarrierSST, TEST_TIMEOUT);
+        doReturn(false).when(mSpyCarrierSST).evaluateSendingMessage(any());
+        doReturn(mNotificationManager).when(mSpyCarrierSST).getNotificationManager(any());
+        mSpyCarrierSST.handleMessage(notificationMsg);
+        waitForHandlerAction(mSpyCarrierSST, TEST_TIMEOUT);
         verify(mNotificationManager).cancel(
                 CarrierServiceStateTracker.NOTIFICATION_EMERGENCY_NETWORK);
         verify(mNotificationManager).cancel(
@@ -111,18 +144,58 @@
     public void testSendBothNotifications() {
         logd(LOG_TAG + ":testSendBothNotifications()");
         Notification.Builder mNotificationBuilder = new Notification.Builder(mContext);
-        Message notificationMsg = mCarrierSST.obtainMessage(
+        Message notificationMsg = mSpyCarrierSST.obtainMessage(
                 CarrierServiceStateTracker.CARRIER_EVENT_DATA_DEREGISTRATION, null);
-        doReturn(true).when(mCarrierSST).evaluateSendingMessage(any());
-        doReturn(false).when(mCarrierSST).isRadioOffOrAirplaneMode();
-        doReturn(0).when(mCarrierSST).getDelay(any());
-        doReturn(mNotificationBuilder).when(mCarrierSST).getNotificationBuilder(any());
-        doReturn(mNotificationManager).when(mCarrierSST).getNotificationManager(any());
-        mCarrierSST.handleMessage(notificationMsg);
-        waitForHandlerAction(mCarrierSST, TEST_TIMEOUT);
+        doReturn(true).when(mSpyCarrierSST).evaluateSendingMessage(any());
+        doReturn(false).when(mSpyCarrierSST).isRadioOffOrAirplaneMode();
+        doReturn(0).when(mSpyCarrierSST).getDelay(any());
+        doReturn(mNotificationBuilder).when(mSpyCarrierSST).getNotificationBuilder(any());
+        doReturn(mNotificationManager).when(mSpyCarrierSST).getNotificationManager(any());
+        mSpyCarrierSST.handleMessage(notificationMsg);
+        waitForHandlerAction(mSpyCarrierSST, TEST_TIMEOUT);
         verify(mNotificationManager).notify(
                 eq(CarrierServiceStateTracker.NOTIFICATION_PREF_NETWORK), isA(Notification.class));
         verify(mNotificationManager).notify(
                 eq(CarrierServiceStateTracker.NOTIFICATION_EMERGENCY_NETWORK), any());
     }
+
+    @Test
+    @SmallTest
+    public void testSendPrefNetworkNotification() {
+        logd(LOG_TAG + ":testSendPrefNetworkNotification()");
+        Intent intent = new Intent().setAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+        mContext.sendBroadcast(intent);
+        waitForMs(300);
+
+        Map<Integer, CarrierServiceStateTracker.NotificationType> notificationTypeMap =
+                mCarrierSST.getNotificationTypeMap();
+        CarrierServiceStateTracker.NotificationType prefNetworkNotification =
+                notificationTypeMap.get(CarrierServiceStateTracker.NOTIFICATION_PREF_NETWORK);
+        CarrierServiceStateTracker.NotificationType spyPrefNetworkNotification = spy(
+                prefNetworkNotification);
+        notificationTypeMap.put(CarrierServiceStateTracker.NOTIFICATION_PREF_NETWORK,
+                spyPrefNetworkNotification);
+        Notification.Builder mNotificationBuilder = new Notification.Builder(mContext);
+        doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mSST.mSS).getVoiceRegState();
+        doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mSST.mSS).getDataRegState();
+        doReturn(true).when(mSST).isRadioOn();
+        doReturn(mNotificationBuilder).when(spyPrefNetworkNotification).getNotificationBuilder();
+
+        String prefNetworkMode = Settings.Global.PREFERRED_NETWORK_MODE + mPhone.getSubId();
+        Settings.Global.putInt(mFakeContentResolver, prefNetworkMode,
+                RILConstants.NETWORK_MODE_LTE_CDMA_EVDO);
+        mFakeContentResolver.notifyChange(
+                Settings.Global.getUriFor(prefNetworkMode), mSpyCarrierSST.getContentObserver());
+        waitForMs(500);
+        verify(mNotificationManager).notify(
+                eq(CarrierServiceStateTracker.NOTIFICATION_PREF_NETWORK), isA(Notification.class));
+
+        Settings.Global.putInt(mFakeContentResolver, prefNetworkMode,
+                RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA);
+        mFakeContentResolver.notifyChange(
+                Settings.Global.getUriFor(prefNetworkMode), mSpyCarrierSST.getContentObserver());
+        waitForMs(500);
+        verify(mNotificationManager, atLeast(1)).cancel(
+                CarrierServiceStateTracker.NOTIFICATION_PREF_NETWORK);
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellIdentityTdscdmaTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityTdscdmaTest.java
index 1cdf18ca..5915062 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CellIdentityTdscdmaTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityTdscdmaTest.java
@@ -33,7 +33,7 @@
     // Tracking area code ranges from 0 to 65535.
     private static final int TAC = 65535;
     // Absolute RF Channel Number ranges from 0 to 262140.
-    private static final int EARFCN = 262140;
+    private static final int UARFCN = 262140;
     private static final int MCC = 120;
     private static final int MNC = 260;
     private static final String MCC_STR = "120";
@@ -52,33 +52,38 @@
     @SmallTest
     public void testDefaultConstructor() {
         CellIdentityTdscdma ci =
-                new CellIdentityTdscdma(MCC_STR, MNC_STR, LAC, CID, CPID);
+                new CellIdentityTdscdma(
+                        MCC_STR, MNC_STR, LAC, CID, CPID, UARFCN, ALPHA_LONG, ALPHA_SHORT);
 
         assertEquals(MCC_STR, ci.getMccString());
         assertEquals(MNC_STR, ci.getMncString());
         assertEquals(LAC, ci.getLac());
         assertEquals(CID, ci.getCid());
         assertEquals(CPID, ci.getCpid());
+        assertEquals(UARFCN, ci.getChannelNumber());
+        assertEquals(ALPHA_LONG, ci.getOperatorAlphaLong());
+        assertEquals(ALPHA_SHORT, ci.getOperatorAlphaShort());
     }
 
     @SmallTest
     public void testConstructorWithEmptyMccMnc() {
-        CellIdentityTdscdma ci = new CellIdentityTdscdma(null, null, LAC, CID, CPID);
+        CellIdentityTdscdma ci = new CellIdentityTdscdma(
+                null, null, LAC, CID, CPID, UARFCN, "", "");
 
         assertNull(ci.getMccString());
         assertNull(ci.getMncString());
 
-        ci = new CellIdentityTdscdma(MCC_STR, null, LAC, CID, CPID);
+        ci = new CellIdentityTdscdma(MCC_STR, null, LAC, CID, CPID, UARFCN, "", "");
 
         assertEquals(MCC_STR, ci.getMccString());
         assertNull(ci.getMncString());
 
-        ci = new CellIdentityTdscdma(null, MNC_STR, LAC, CID, CPID);
+        ci = new CellIdentityTdscdma(null, MNC_STR, LAC, CID, CPID, UARFCN, "", "");
 
         assertEquals(MNC_STR, ci.getMncString());
         assertNull(ci.getMccString());
 
-        ci = new CellIdentityTdscdma("", "", LAC, CID, CPID);
+        ci = new CellIdentityTdscdma("", "", LAC, CID, CPID, UARFCN, "", "");
 
         assertNull(ci.getMccString());
         assertNull(ci.getMncString());
@@ -86,7 +91,8 @@
 
     @SmallTest
     public void testParcel() {
-        CellIdentityTdscdma ci = new CellIdentityTdscdma(MCC_STR, MNC_STR, LAC, CID, CPID);
+        CellIdentityTdscdma ci = new CellIdentityTdscdma(
+                MCC_STR, MNC_STR, LAC, CID, UARFCN, CPID, ALPHA_LONG, ALPHA_SHORT);
 
         Parcel p = Parcel.obtain();
         ci.writeToParcel(p, 0);
@@ -99,7 +105,8 @@
     @SmallTest
     public void testParcelWithUnknowMccMnc() {
         CellIdentityTdscdma ci =
-                new CellIdentityTdscdma(null, null, LAC, CID, CPID, ALPHA_LONG, ALPHA_SHORT);
+                new CellIdentityTdscdma(
+                        null, null, LAC, CID, CPID, UARFCN, ALPHA_LONG, ALPHA_SHORT);
 
         Parcel p = Parcel.obtain();
         p.writeInt(CellIdentity.TYPE_TDSCDMA);
@@ -110,6 +117,7 @@
         p.writeInt(LAC);
         p.writeInt(CID);
         p.writeInt(CPID);
+        p.writeInt(UARFCN);
         p.setDataPosition(0);
 
         CellIdentityTdscdma newCi = CellIdentityTdscdma.CREATOR.createFromParcel(p);
@@ -121,7 +129,8 @@
         final String invalidMcc = "randomStuff";
         final String invalidMnc = "randomStuff";
         CellIdentityTdscdma ci =
-                new CellIdentityTdscdma(null, null, LAC, CID, CPID, ALPHA_LONG, ALPHA_SHORT);
+                new CellIdentityTdscdma(
+                        null, null, LAC, CID, CPID, UARFCN, ALPHA_LONG, ALPHA_SHORT);
 
         Parcel p = Parcel.obtain();
         p.writeInt(CellIdentity.TYPE_TDSCDMA);
@@ -132,6 +141,7 @@
         p.writeInt(LAC);
         p.writeInt(CID);
         p.writeInt(CPID);
+        p.writeInt(UARFCN);
         p.setDataPosition(0);
 
         CellIdentityTdscdma newCi = CellIdentityTdscdma.CREATOR.createFromParcel(p);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkServiceTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkServiceTest.java
index 16bc535..a595c36 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkServiceTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkServiceTest.java
@@ -54,7 +54,7 @@
         mCellularNetworkService = new CellularNetworkService();
         ServiceInfo serviceInfo =  new ServiceInfo();
         serviceInfo.packageName = "com.android.phone";
-        serviceInfo.permission = "android.permission.BIND_NETWORK_SERVICE";
+        serviceInfo.permission = "android.permission.BIND_TELEPHONY_NETWORK_SERVICE";
         IntentFilter filter = new IntentFilter();
         mContextFixture.addService(
                 NetworkService.NETWORK_SERVICE_INTERFACE,
@@ -132,7 +132,7 @@
         waitForMs(1000);
 
         NetworkRegistrationState expectedState = new NetworkRegistrationState(
-                AccessNetworkConstants.TransportType.WWAN, domain, voiceRegState,
+                domain, AccessNetworkConstants.TransportType.WWAN, voiceRegState,
                 ServiceState.rilRadioTechnologyToNetworkType(voiceRadioTech), reasonForDenial,
                 false, availableServices, null, cssSupported,
                 roamingIndicator, systemIsInPrl, defaultRoamingIndicator);
@@ -155,7 +155,7 @@
         waitForMs(1000);
 
         expectedState = new NetworkRegistrationState(
-                AccessNetworkConstants.TransportType.WWAN, domain, voiceRegState,
+                domain, AccessNetworkConstants.TransportType.WWAN, voiceRegState,
                 ServiceState.rilRadioTechnologyToNetworkType(voiceRadioTech), reasonForDenial,
                 false, availableServices, null, maxDataCalls);
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
index 1d08c42..d2e28ca 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
@@ -40,6 +40,7 @@
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
@@ -278,6 +279,11 @@
         }
 
         @Override
+        public ApplicationInfo getApplicationInfo() {
+            return mApplicationInfo;
+        }
+
+        @Override
         public String getOpPackageName() {
             return "com.android.internal.telephony";
         }
@@ -526,6 +532,7 @@
     // when(...) logic to be used to add specific little responses where needed.
 
     private final Resources mResources = mock(Resources.class);
+    private final ApplicationInfo mApplicationInfo = mock(ApplicationInfo.class);
     private final PackageManager mPackageManager = mock(PackageManager.class);
     private final TelephonyManager mTelephonyManager = mock(TelephonyManager.class);
     private final DownloadManager mDownloadManager = mock(DownloadManager.class);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
index 00383a5..77eb7dc 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
@@ -403,7 +403,7 @@
         // voicemail number from sharedPreference
         mPhoneUT.setVoiceMailNumber("alphaTag", voiceMailNumber, null);
         ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
-        verify(mRuimRecords).setVoiceMailNumber(eq("alphaTag"), eq(voiceMailNumber),
+        verify(mSimRecords).setVoiceMailNumber(eq("alphaTag"), eq(voiceMailNumber),
                 messageArgumentCaptor.capture());
 
         Message msg = messageArgumentCaptor.getValue();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java
new file mode 100644
index 0000000..bcc1730
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2018 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;
+
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.net.wifi.WifiManager;
+import android.os.AsyncResult;
+import android.os.HandlerThread;
+import android.telephony.CellIdentityGsm;
+import android.telephony.CellInfoGsm;
+import android.telephony.ServiceState;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+public class LocaleTrackerTest extends TelephonyTest {
+
+    private static final String US_MCC = "310";
+    private static final String FAKE_MNC = "123";
+    private static final String US_COUNTRY_CODE = "us";
+    private static final String COUNTRY_CODE_UNAVAILABLE = "";
+
+    private LocaleTracker mLocaleTracker;
+    private LocaleTrackerTestHandler mLocaleTrackerTestHandler;
+
+    private CellInfoGsm mCellInfo;
+    private WifiManager mWifiManager;
+
+    private class LocaleTrackerTestHandler extends HandlerThread {
+
+        private LocaleTrackerTestHandler(String name) {
+            super(name);
+        }
+
+        @Override
+        public void onLooperPrepared() {
+            mLocaleTracker = new LocaleTracker(mPhone, this.getLooper());
+            setReady(true);
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        logd("LocaleTrackerTest +Setup!");
+        super.setUp(getClass().getSimpleName());
+
+        // This is a workaround to bypass setting system properties, which causes access violation.
+        doReturn(-1).when(mPhone).getPhoneId();
+        mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+
+        mCellInfo = new CellInfoGsm();
+        mCellInfo.setCellIdentity(new CellIdentityGsm(Integer.parseInt(US_MCC),
+                Integer.parseInt(FAKE_MNC), 0, 0));
+        doReturn(Arrays.asList(mCellInfo)).when(mPhone).getAllCellInfo(isNull());
+        doReturn(true).when(mSST).getDesiredPowerState();
+
+        mLocaleTrackerTestHandler = new LocaleTrackerTestHandler(getClass().getSimpleName());
+        mLocaleTrackerTestHandler.start();
+        waitUntilReady();
+        logd("LocaleTrackerTest -Setup!");
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mLocaleTracker.removeCallbacksAndMessages(null);
+        mLocaleTrackerTestHandler.quit();
+        super.tearDown();
+    }
+
+    @Test
+    @SmallTest
+    public void testUpdateOperatorNumericSync() throws Exception {
+        mLocaleTracker.updateOperatorNumericSync(US_MCC + FAKE_MNC);
+        assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
+        verify(mWifiManager).setCountryCode(US_COUNTRY_CODE, false);
+    }
+
+    @Test
+    @SmallTest
+    public void testUpdateOperatorNumericAsync() throws Exception {
+        mLocaleTracker.updateOperatorNumericAsync(US_MCC + FAKE_MNC);
+        waitForMs(100);
+        assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
+        verify(mWifiManager).setCountryCode(US_COUNTRY_CODE, false);
+    }
+
+    @Test
+    @SmallTest
+    public void testNoSim() throws Exception {
+        mLocaleTracker.updateOperatorNumericAsync("");
+        waitForHandlerAction(mLocaleTracker, 100);
+        assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
+        verify(mWifiManager).setCountryCode(US_COUNTRY_CODE, false);
+    }
+
+    @Test
+    @SmallTest
+    public void testBootupInAirplaneModeOn() throws Exception {
+        doReturn(false).when(mSST).getDesiredPowerState();
+        mLocaleTracker.updateOperatorNumericAsync("");
+        waitForHandlerAction(mLocaleTracker, 100);
+        assertEquals(COUNTRY_CODE_UNAVAILABLE, mLocaleTracker.getCurrentCountry());
+        verify(mWifiManager).setCountryCode(COUNTRY_CODE_UNAVAILABLE, false);
+    }
+
+    @Test
+    @SmallTest
+    public void testTogglingAirplaneMode() throws Exception {
+        mLocaleTracker.updateOperatorNumericSync(US_MCC + FAKE_MNC);
+        assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
+        verify(mWifiManager).setCountryCode(US_COUNTRY_CODE, false);
+
+        doReturn(false).when(mSST).getDesiredPowerState();
+        mLocaleTracker.updateOperatorNumericAsync("");
+        waitForHandlerAction(mLocaleTracker, 100);
+        assertEquals(COUNTRY_CODE_UNAVAILABLE, mLocaleTracker.getCurrentCountry());
+        verify(mWifiManager).setCountryCode(COUNTRY_CODE_UNAVAILABLE, false);
+
+        doReturn(true).when(mSST).getDesiredPowerState();
+        mLocaleTracker.updateOperatorNumericSync(US_MCC + FAKE_MNC);
+        assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
+        verify(mWifiManager, times(2)).setCountryCode(US_COUNTRY_CODE, false);
+    }
+
+    @Test
+    @SmallTest
+    public void testCellInfoUnavailableRetry() throws Exception {
+        doReturn(null).when(mPhone).getAllCellInfo(isNull());
+        mLocaleTracker.updateOperatorNumericAsync("");
+        waitForHandlerAction(mLocaleTracker, 100);
+        assertEquals(COUNTRY_CODE_UNAVAILABLE, mLocaleTracker.getCurrentCountry());
+        verify(mWifiManager).setCountryCode(COUNTRY_CODE_UNAVAILABLE, false);
+
+        doReturn(Arrays.asList(mCellInfo)).when(mPhone).getAllCellInfo(isNull());
+        waitForHandlerActionDelayed(mLocaleTracker, 100, 2500);
+        assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
+        verify(mWifiManager).setCountryCode(US_COUNTRY_CODE, false);
+    }
+
+    @Test
+    @SmallTest
+    public void testOutOfAirplaneMode() throws Exception {
+        doReturn(null).when(mPhone).getAllCellInfo(isNull());
+        mLocaleTracker.updateOperatorNumericAsync("");
+        waitForHandlerAction(mLocaleTracker, 100);
+        assertEquals(COUNTRY_CODE_UNAVAILABLE, mLocaleTracker.getCurrentCountry());
+        verify(mWifiManager).setCountryCode(COUNTRY_CODE_UNAVAILABLE, false);
+
+        doReturn(Arrays.asList(mCellInfo)).when(mPhone).getAllCellInfo(isNull());
+        ServiceState ss = new ServiceState();
+        ss.setState(ServiceState.STATE_IN_SERVICE);
+        AsyncResult ar = new AsyncResult(null, ss, null);
+        mLocaleTracker.sendMessage(mLocaleTracker.obtainMessage(3, ar));
+        waitForHandlerAction(mLocaleTracker, 100);
+        assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
+        verify(mWifiManager).setCountryCode(US_COUNTRY_CODE, false);
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkRegistrationStateTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkRegistrationStateTest.java
new file mode 100644
index 0000000..eb52e7c
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkRegistrationStateTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018 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;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.telephony.AccessNetworkConstants.TransportType;
+import android.telephony.CellIdentityLte;
+import android.telephony.NetworkRegistrationState;
+import android.telephony.TelephonyManager;
+
+import org.junit.Test;
+
+/** Unit tests for {@link NetworkRegistrationState}. */
+
+public class NetworkRegistrationStateTest {
+
+    @Test
+    @SmallTest
+    public void testParcel() {
+        NetworkRegistrationState nrs = new NetworkRegistrationState(
+                NetworkRegistrationState.DOMAIN_CS,
+                TransportType.WWAN,
+                NetworkRegistrationState.REG_STATE_HOME,
+                TelephonyManager.NETWORK_TYPE_LTE,
+                0,
+                false,
+                new int[]{NetworkRegistrationState.SERVICE_TYPE_DATA},
+                new CellIdentityLte());
+
+        Parcel p = Parcel.obtain();
+        nrs.writeToParcel(p, 0);
+        p.setDataPosition(0);
+
+        NetworkRegistrationState newNrs = NetworkRegistrationState.CREATOR.createFromParcel(p);
+        assertEquals(nrs, newNrs);
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NewNitzStateMachineTest.java b/tests/telephonytests/src/com/android/internal/telephony/NewNitzStateMachineTest.java
new file mode 100644
index 0000000..028cbcc
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/NewNitzStateMachineTest.java
@@ -0,0 +1,970 @@
+/*
+ * Copyright 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.icu.util.Calendar;
+import android.icu.util.GregorianCalendar;
+import android.icu.util.TimeZone;
+import android.util.TimestampedValue;
+
+import com.android.internal.telephony.TimeZoneLookupHelper.CountryResult;
+import com.android.internal.telephony.TimeZoneLookupHelper.OffsetResult;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+public class NewNitzStateMachineTest extends TelephonyTest {
+
+    // A country with a single zone : the zone can be guessed from the country.
+    // The UK uses UTC for part of the year so it is not good for detecting bogus NITZ signals.
+    private static final Scenario UNITED_KINGDOM_SCENARIO = new Scenario.Builder()
+            .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0)
+            .setInitialDeviceRealtimeMillis(123456789L)
+            .setTimeZone("Europe/London")
+            .setActualTimeUtc(2018, 1, 1, 12, 0, 0)
+            .setCountryIso("gb")
+            .build();
+
+    // A country that has multiple zones, but there is only one matching time zone at the time :
+    // the zone cannot be guessed from the country alone, but can be guessed from the country +
+    // NITZ. The US never uses UTC so it can be used for testing bogus NITZ signal handling.
+    private static final Scenario UNIQUE_US_ZONE_SCENARIO = new Scenario.Builder()
+            .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0)
+            .setInitialDeviceRealtimeMillis(123456789L)
+            .setTimeZone("America/Los_Angeles")
+            .setActualTimeUtc(2018, 1, 1, 12, 0, 0)
+            .setCountryIso("us")
+            .build();
+
+    // A country with a single zone: the zone can be guessed from the country alone. CZ never uses
+    // UTC so it can be used for testing bogus NITZ signal handling.
+    private static final Scenario CZECHIA_SCENARIO = new Scenario.Builder()
+            .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0)
+            .setInitialDeviceRealtimeMillis(123456789L)
+            .setTimeZone("Europe/Prague")
+            .setActualTimeUtc(2018, 1, 1, 12, 0, 0)
+            .setCountryIso("cz")
+            .build();
+
+    @Mock
+    private NewNitzStateMachine.DeviceState mDeviceState;
+
+    @Mock
+    private NewTimeServiceHelper mTimeServiceHelper;
+
+    private TimeZoneLookupHelper mRealTimeZoneLookupHelper;
+
+    private NewNitzStateMachine mNitzStateMachine;
+
+    @Before
+    public void setUp() throws Exception {
+        logd("NitzStateMachineTest +Setup!");
+        super.setUp("NitzStateMachineTest");
+
+        // In tests we use the real TimeZoneLookupHelper.
+        mRealTimeZoneLookupHelper = new TimeZoneLookupHelper();
+        mNitzStateMachine = new NewNitzStateMachine(
+                mPhone, mTimeServiceHelper, mDeviceState, mRealTimeZoneLookupHelper);
+
+        logd("ServiceStateTrackerTest -Setup!");
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        checkNoUnverifiedSetOperations(mTimeServiceHelper);
+
+        super.tearDown();
+    }
+
+    @Test
+    public void test_uniqueUsZone_Assumptions() {
+        // Check we'll get the expected behavior from TimeZoneLookupHelper.
+
+        // allZonesHaveSameOffset == false, so we shouldn't pick an arbitrary zone.
+        CountryResult expectedCountryLookupResult = new CountryResult(
+                "America/New_York", false /* allZonesHaveSameOffset */,
+                UNIQUE_US_ZONE_SCENARIO.getInitialSystemClockMillis());
+        CountryResult actualCountryLookupResult =
+                mRealTimeZoneLookupHelper.lookupByCountry(
+                        UNIQUE_US_ZONE_SCENARIO.getNetworkCountryIsoCode(),
+                        UNIQUE_US_ZONE_SCENARIO.getInitialSystemClockMillis());
+        assertEquals(expectedCountryLookupResult, actualCountryLookupResult);
+
+        // isOnlyMatch == true, so the combination of country + NITZ should be enough.
+        OffsetResult expectedLookupResult =
+                new OffsetResult("America/Los_Angeles", true /* isOnlyMatch */);
+        OffsetResult actualLookupResult = mRealTimeZoneLookupHelper.lookupByNitzCountry(
+                UNIQUE_US_ZONE_SCENARIO.getNitzSignal().getValue(),
+                UNIQUE_US_ZONE_SCENARIO.getNetworkCountryIsoCode());
+        assertEquals(expectedLookupResult, actualLookupResult);
+    }
+
+    @Test
+    public void test_unitedKingdom_Assumptions() {
+        // Check we'll get the expected behavior from TimeZoneLookupHelper.
+
+        // allZonesHaveSameOffset == true (not only that, there is only one zone), so we can pick
+        // the zone knowing only the country.
+        CountryResult expectedCountryLookupResult = new CountryResult(
+                "Europe/London", true /* allZonesHaveSameOffset */,
+                UNITED_KINGDOM_SCENARIO.getInitialSystemClockMillis());
+        CountryResult actualCountryLookupResult =
+                mRealTimeZoneLookupHelper.lookupByCountry(
+                        UNITED_KINGDOM_SCENARIO.getNetworkCountryIsoCode(),
+                        UNITED_KINGDOM_SCENARIO.getInitialSystemClockMillis());
+        assertEquals(expectedCountryLookupResult, actualCountryLookupResult);
+
+        OffsetResult expectedLookupResult =
+                new OffsetResult("Europe/London", true /* isOnlyMatch */);
+        OffsetResult actualLookupResult = mRealTimeZoneLookupHelper.lookupByNitzCountry(
+                UNITED_KINGDOM_SCENARIO.getNitzSignal().getValue(),
+                UNITED_KINGDOM_SCENARIO.getNetworkCountryIsoCode());
+        assertEquals(expectedLookupResult, actualLookupResult);
+    }
+
+    @Test
+    public void test_uniqueUsZone_timeZoneEnabled_countryThenNitz() throws Exception {
+        Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+        Device device = new DeviceBuilder()
+                .setClocksFromScenario(scenario)
+                .setTimeZoneDetectionEnabled(true)
+                .setTimeZoneSettingInitialized(false)
+                .initialize();
+        Script script = new Script(device);
+
+        script.countryReceived(scenario.getNetworkCountryIsoCode())
+                // Country won't be enough for time zone detection.
+                .verifyNothingWasSetAndReset()
+                .nitzReceived(scenario.getNitzSignal())
+                // Country + NITZ is enough for both time + time zone detection.
+                .verifyTimeSuggestedAndZoneSetAndReset(
+                        scenario.getNitzSignal(), scenario.getTimeZoneId());
+
+        // Check NitzStateMachine state.
+        assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+        assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+        assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+    }
+
+    @Test
+    public void test_unitedKingdom_timeZoneEnabled_countryThenNitz() throws Exception {
+        Scenario scenario = UNITED_KINGDOM_SCENARIO;
+        Device device = new DeviceBuilder()
+                .setClocksFromScenario(scenario)
+                .setTimeZoneDetectionEnabled(true)
+                .setTimeZoneSettingInitialized(false)
+                .initialize();
+        Script script = new Script(device);
+
+        script.countryReceived(scenario.getNetworkCountryIsoCode())
+                // Country alone is enough to guess the time zone.
+                .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId())
+                .nitzReceived(scenario.getNitzSignal())
+                // Country + NITZ is enough for both time + time zone detection.
+                .verifyTimeSuggestedAndZoneSetAndReset(
+                        scenario.getNitzSignal(), scenario.getTimeZoneId());
+
+        // Check NitzStateMachine state.
+        assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+        assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+        assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+    }
+
+    @Test
+    public void test_uniqueUsZone_timeZoneDisabled_countryThenNitz() throws Exception {
+        Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+        Device device = new DeviceBuilder()
+                .setClocksFromScenario(scenario)
+                .setTimeZoneDetectionEnabled(false)
+                .setTimeZoneSettingInitialized(false)
+                .initialize();
+        Script script = new Script(device);
+
+        script.countryReceived(scenario.getNetworkCountryIsoCode())
+                // Country is not enough to guess the time zone and time zone detection is disabled.
+                .verifyNothingWasSetAndReset()
+                .nitzReceived(scenario.getNitzSignal())
+                // Time zone detection is disabled, but time should be suggested from NITZ.
+                .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+        // Check NitzStateMachine state.
+        assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+        assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+        assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+    }
+
+    @Test
+    public void test_unitedKingdom_timeZoneDisabled_countryThenNitz() throws Exception {
+        Scenario scenario = UNITED_KINGDOM_SCENARIO;
+        Device device = new DeviceBuilder()
+                .setClocksFromScenario(scenario)
+                .setTimeZoneDetectionEnabled(false)
+                .setTimeZoneSettingInitialized(false)
+                .initialize();
+        Script script = new Script(device);
+
+        script.countryReceived(scenario.getNetworkCountryIsoCode())
+                // Country alone would be enough for time zone detection, but it's disabled.
+                .verifyNothingWasSetAndReset()
+                .nitzReceived(scenario.getNitzSignal())
+                // Time zone detection is disabled, but time should be suggested from NITZ.
+                .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+        // Check NitzStateMachine state.
+        assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+        assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+        assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+    }
+
+    @Test
+    public void test_uniqueUsZone_timeZoneEnabled_nitzThenCountry() throws Exception {
+        Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+        Device device = new DeviceBuilder()
+                .setClocksFromScenario(scenario)
+                .setTimeZoneDetectionEnabled(true)
+                .setTimeZoneSettingInitialized(false)
+                .initialize();
+        Script script = new Script(device);
+
+        // Simulate receiving an NITZ signal.
+        script.nitzReceived(scenario.getNitzSignal())
+                // The NITZ alone isn't enough to detect a time zone.
+                .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+        // Check NitzStateMachine state.
+        assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+        assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+        assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+        // Simulate the country code becoming known.
+        script.countryReceived(scenario.getNetworkCountryIsoCode())
+                // The NITZ + country is enough to detect the time zone.
+                .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId());
+
+        // Check NitzStateMachine state.
+        assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+        assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+        assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+    }
+
+    @Test
+    public void test_unitedKingdom_timeZoneEnabled_nitzThenCountry() throws Exception {
+        Scenario scenario = UNITED_KINGDOM_SCENARIO;
+        Device device = new DeviceBuilder()
+                .setClocksFromScenario(scenario)
+                .setTimeZoneDetectionEnabled(true)
+                .setTimeZoneSettingInitialized(false)
+                .initialize();
+        Script script = new Script(device);
+
+        // Simulate receiving an NITZ signal.
+        script.nitzReceived(scenario.getNitzSignal())
+                // The NITZ alone isn't enough to detect a time zone.
+                .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+        // Check NitzStateMachine state.
+        assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+        assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+        assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+        // Simulate the country code becoming known.
+        script.countryReceived(scenario.getNetworkCountryIsoCode());
+
+        // The NITZ + country is enough to detect the time zone.
+        // NOTE: setting the time zone happens twice because of a quirk in NitzStateMachine: it
+        // handles the country lookup / set, then combines the country with the NITZ state and does
+        // another lookup / set. We shouldn't require it is set twice but we do for simplicity.
+        script.verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId(), 2 /* times */);
+
+        // Check NitzStateMachine state.
+        assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+        assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+        assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+    }
+
+    @Test
+    public void test_validCzNitzSignal_nitzReceivedFirst() throws Exception {
+        Scenario scenario = CZECHIA_SCENARIO;
+        Device device = new DeviceBuilder()
+                .setClocksFromScenario(scenario)
+                .setTimeZoneDetectionEnabled(true)
+                .setTimeZoneSettingInitialized(true)
+                .initialize();
+        Script script = new Script(device);
+
+        TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+        // Simulate receiving an NITZ signal.
+        script.nitzReceived(goodNitzSignal)
+                // The NITZ alone isn't enough to detect a time zone.
+                .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+        // Check NitzStateMachine state.
+        assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+        assertEquals(goodNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+        assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+        // Simulate the country code becoming known.
+        script.countryReceived(scenario.getNetworkCountryIsoCode())
+                // The NITZ country is enough to detect the time zone, but the NITZ + country is
+                // also sufficient so we expect the time zone to be set twice.
+                .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId(), 2);
+
+        // Check NitzStateMachine state.
+        assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+        assertEquals(goodNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+        assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+    }
+
+    @Test
+    public void test_validCzNitzSignal_countryReceivedFirst() throws Exception {
+        Scenario scenario = CZECHIA_SCENARIO;
+        Device device = new DeviceBuilder()
+                .setClocksFromScenario(scenario)
+                .setTimeZoneDetectionEnabled(true)
+                .setTimeZoneSettingInitialized(true)
+                .initialize();
+        Script script = new Script(device);
+
+        TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+        // Simulate the country code becoming known.
+        script.countryReceived(scenario.getNetworkCountryIsoCode())
+                // The NITZ country is enough to detect the time zone.
+                .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId(), 1);
+
+        // Check NitzStateMachine state.
+        assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+        assertNull(mNitzStateMachine.getCachedNitzData());
+        assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+
+        // Simulate receiving an NITZ signal.
+        script.nitzReceived(goodNitzSignal)
+                // The time will be suggested from the NITZ signal.
+                // The combination of NITZ + country will cause the time zone to be set.
+                .verifyTimeSuggestedAndZoneSetAndReset(
+                        scenario.getNitzSignal(), scenario.getTimeZoneId());
+
+        // Check NitzStateMachine state.
+        assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+        assertEquals(goodNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+        assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+    }
+
+    @Test
+    public void test_bogusCzNitzSignal_nitzReceivedFirst() throws Exception {
+        Scenario scenario = CZECHIA_SCENARIO;
+        Device device = new DeviceBuilder()
+                .setClocksFromScenario(scenario)
+                .setTimeZoneDetectionEnabled(true)
+                .setTimeZoneSettingInitialized(true)
+                .initialize();
+        Script script = new Script(device);
+
+        TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+        // Create a corrupted NITZ signal, where the offset information has been lost.
+        NitzData bogusNitzData = NitzData.createForTests(
+                0 /* UTC! */, null /* dstOffsetMillis */,
+                goodNitzSignal.getValue().getCurrentTimeInMillis(),
+                null /* emulatorHostTimeZone */);
+        TimestampedValue<NitzData> badNitzSignal = new TimestampedValue<>(
+                goodNitzSignal.getReferenceTimeMillis(), bogusNitzData);
+
+        // Simulate receiving an NITZ signal.
+        script.nitzReceived(badNitzSignal)
+                // The NITZ alone isn't enough to detect a time zone, but there isn't enough
+                // information to work out it is bogus.
+                .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+        // Check NitzStateMachine state.
+        assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+        assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+        assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+        // Simulate the country code becoming known.
+        script.countryReceived(scenario.getNetworkCountryIsoCode())
+                // The country is enough to detect the time zone for CZ. If the NITZ signal
+                // wasn't obviously bogus we'd try to set it twice.
+                .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId(), 1);
+
+        // Check NitzStateMachine state.
+        assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+        assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+        assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+    }
+
+    @Test
+    public void test_bogusCzNitzSignal_countryReceivedFirst() throws Exception {
+        Scenario scenario = CZECHIA_SCENARIO;
+        Device device = new DeviceBuilder()
+                .setClocksFromScenario(scenario)
+                .setTimeZoneDetectionEnabled(true)
+                .setTimeZoneSettingInitialized(true)
+                .initialize();
+        Script script = new Script(device);
+
+        TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+        // Create a corrupted NITZ signal, where the offset information has been lost.
+        NitzData bogusNitzData = NitzData.createForTests(
+                0 /* UTC! */, null /* dstOffsetMillis */,
+                goodNitzSignal.getValue().getCurrentTimeInMillis(),
+                null /* emulatorHostTimeZone */);
+        TimestampedValue<NitzData> badNitzSignal = new TimestampedValue<>(
+                goodNitzSignal.getReferenceTimeMillis(), bogusNitzData);
+
+        // Simulate the country code becoming known.
+        script.countryReceived(scenario.getNetworkCountryIsoCode())
+                // The country is enough to detect the time zone for CZ.
+                .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId());
+
+        // Check NitzStateMachine state.
+        assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+        assertNull(mNitzStateMachine.getCachedNitzData());
+        assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+
+        // Simulate receiving an NITZ signal.
+        script.nitzReceived(badNitzSignal)
+                // The NITZ should be detected as bogus so only the time will be suggested.
+                .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+        // Check NitzStateMachine state.
+        assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+        assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+        assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+    }
+
+    @Test
+    public void test_bogusUniqueUsNitzSignal_nitzReceivedFirst() throws Exception {
+        Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+        Device device = new DeviceBuilder()
+                .setClocksFromScenario(scenario)
+                .setTimeZoneDetectionEnabled(true)
+                .setTimeZoneSettingInitialized(true)
+                .initialize();
+        Script script = new Script(device);
+
+        TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+        // Create a corrupted NITZ signal, where the offset information has been lost.
+        NitzData bogusNitzData = NitzData.createForTests(
+                0 /* UTC! */, null /* dstOffsetMillis */,
+                goodNitzSignal.getValue().getCurrentTimeInMillis(),
+                null /* emulatorHostTimeZone */);
+        TimestampedValue<NitzData> badNitzSignal = new TimestampedValue<>(
+                goodNitzSignal.getReferenceTimeMillis(), bogusNitzData);
+
+        // Simulate receiving an NITZ signal.
+        script.nitzReceived(badNitzSignal)
+                // The NITZ alone isn't enough to detect a time zone, but there isn't enough
+                // information to work out its bogus.
+                .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+        // Check NitzStateMachine state.
+        assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+        assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+        assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+        // Simulate the country code becoming known.
+        script.countryReceived(scenario.getNetworkCountryIsoCode())
+                // The country isn't enough to detect the time zone for US so we will leave the time
+                // zone unset.
+                .verifyNothingWasSetAndReset();
+
+        // Check NitzStateMachine state.
+        assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+        assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+        assertNull(mNitzStateMachine.getSavedTimeZoneId());
+    }
+
+    @Test
+    public void test_bogusUsUniqueNitzSignal_countryReceivedFirst() throws Exception {
+        Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+        Device device = new DeviceBuilder()
+                .setClocksFromScenario(scenario)
+                .setTimeZoneDetectionEnabled(true)
+                .setTimeZoneSettingInitialized(true)
+                .initialize();
+        Script script = new Script(device);
+
+        TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+        // Create a corrupted NITZ signal, where the offset information has been lost.
+        NitzData bogusNitzData = NitzData.createForTests(
+                0 /* UTC! */, null /* dstOffsetMillis */,
+                goodNitzSignal.getValue().getCurrentTimeInMillis(),
+                null /* emulatorHostTimeZone */);
+        TimestampedValue<NitzData> badNitzSignal = new TimestampedValue<>(
+                goodNitzSignal.getReferenceTimeMillis(), bogusNitzData);
+
+        // Simulate the country code becoming known.
+        script.countryReceived(scenario.getNetworkCountryIsoCode())
+                // The country isn't enough to detect the time zone for US so we will leave the time
+                // zone unset.
+                .verifyNothingWasSetAndReset();
+
+        // Check NitzStateMachine state.
+        assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+        assertNull(mNitzStateMachine.getCachedNitzData());
+        assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+        // Simulate receiving an NITZ signal.
+        script.nitzReceived(badNitzSignal)
+                // The NITZ should be detected as bogus so only the time will be suggested.
+                .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+        // Check NitzStateMachine state.
+        assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+        assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+        assertNull(mNitzStateMachine.getSavedTimeZoneId());
+    }
+
+    @Test
+    public void test_emulatorNitzExtensionUsedForTimeZone() throws Exception {
+        Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+        Device device = new DeviceBuilder()
+                .setClocksFromScenario(scenario)
+                .setTimeZoneDetectionEnabled(true)
+                .setTimeZoneSettingInitialized(true)
+                .initialize();
+        Script script = new Script(device);
+
+        TimestampedValue<NitzData> originalNitzSignal = scenario.getNitzSignal();
+
+        // Create an NITZ signal with an explicit time zone (as can happen on emulators)
+        NitzData originalNitzData = originalNitzSignal.getValue();
+        // A time zone that is obviously not in the US, but it should not be questioned.
+        String emulatorTimeZoneId = "Europe/London";
+        NitzData emulatorNitzData = NitzData.createForTests(
+                originalNitzData.getLocalOffsetMillis(),
+                originalNitzData.getDstAdjustmentMillis(),
+                originalNitzData.getCurrentTimeInMillis(),
+                java.util.TimeZone.getTimeZone(emulatorTimeZoneId) /* emulatorHostTimeZone */);
+        TimestampedValue<NitzData> emulatorNitzSignal = new TimestampedValue<>(
+                originalNitzSignal.getReferenceTimeMillis(), emulatorNitzData);
+
+        // Simulate receiving the emulator NITZ signal.
+        script.nitzReceived(emulatorNitzSignal)
+                .verifyTimeSuggestedAndZoneSetAndReset(
+                        scenario.getNitzSignal(), emulatorTimeZoneId);
+
+        // Check NitzStateMachine state.
+        assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+        assertEquals(emulatorNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+        assertEquals(emulatorTimeZoneId, mNitzStateMachine.getSavedTimeZoneId());
+    }
+
+    @Test
+    public void test_emptyCountryStringUsTime_countryReceivedFirst() throws Exception {
+        Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+        Device device = new DeviceBuilder()
+                .setClocksFromScenario(scenario)
+                .setTimeZoneDetectionEnabled(true)
+                .setTimeZoneSettingInitialized(true)
+                .initialize();
+        Script script = new Script(device);
+
+        String expectedZoneId = checkNitzOnlyLookupIsAmbiguousAndReturnZoneId(scenario);
+
+        // Nothing should be set. The country is not valid.
+        script.countryReceived("").verifyNothingWasSetAndReset();
+
+        // Check NitzStateMachine state.
+        assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+        assertNull(mNitzStateMachine.getCachedNitzData());
+        assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+        // Simulate receiving the NITZ signal.
+        script.nitzReceived(scenario.getNitzSignal())
+                .verifyTimeSuggestedAndZoneSetAndReset(scenario.getNitzSignal(), expectedZoneId);
+
+        // Check NitzStateMachine state.
+        assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+        assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+        assertEquals(expectedZoneId, mNitzStateMachine.getSavedTimeZoneId());
+    }
+
+    @Test
+    public void test_emptyCountryStringUsTime_nitzReceivedFirst() throws Exception {
+        Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+        Device device = new DeviceBuilder()
+                .setClocksFromScenario(scenario)
+                .setTimeZoneDetectionEnabled(true)
+                .setTimeZoneSettingInitialized(true)
+                .initialize();
+        Script script = new Script(device);
+
+        String expectedZoneId = checkNitzOnlyLookupIsAmbiguousAndReturnZoneId(scenario);
+
+        // Simulate receiving the NITZ signal.
+        script.nitzReceived(scenario.getNitzSignal())
+                .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+        // Check NitzStateMachine state.
+        assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+        assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+        assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+        // The time zone should be set (but the country is not valid so it's unlikely to be
+        // correct).
+        script.countryReceived("").verifyOnlyTimeZoneWasSetAndReset(expectedZoneId);
+
+        // Check NitzStateMachine state.
+        assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+        assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+        assertEquals(expectedZoneId, mNitzStateMachine.getSavedTimeZoneId());
+    }
+
+    /**
+     * Asserts a test scenario has the properties we expect for NITZ-only lookup. There are
+     * usually multiple zones that will share the same UTC offset so we get a low quality / low
+     * confidence answer, but the zone we find should at least have the correct offset.
+     */
+    private String checkNitzOnlyLookupIsAmbiguousAndReturnZoneId(Scenario scenario) {
+        OffsetResult result =
+                mRealTimeZoneLookupHelper.lookupByNitz(scenario.getNitzSignal().getValue());
+        String expectedZoneId = result.zoneId;
+        // All our scenarios should return multiple matches. The only cases where this wouldn't be
+        // true are places that use offsets like XX:15, XX:30 and XX:45.
+        assertFalse(result.isOnlyMatch);
+        assertSameOffset(scenario.getActualTimeMillis(), expectedZoneId, scenario.getTimeZoneId());
+        return expectedZoneId;
+    }
+
+    private static void assertSameOffset(long timeMillis, String zoneId1, String zoneId2) {
+        assertEquals(TimeZone.getTimeZone(zoneId1).getOffset(timeMillis),
+                TimeZone.getTimeZone(zoneId2).getOffset(timeMillis));
+    }
+
+    private static long createUtcTime(int year, int monthInYear, int day, int hourOfDay, int minute,
+            int second) {
+        Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("Etc/UTC"));
+        cal.clear();
+        cal.set(year, monthInYear - 1, day, hourOfDay, minute, second);
+        return cal.getTimeInMillis();
+    }
+
+    /**
+     * A helper class for common test operations involving a device.
+     */
+    class Script {
+        private final Device mDevice;
+
+        Script(Device device) {
+            this.mDevice = device;
+        }
+
+        Script countryReceived(String countryIsoCode) {
+            mDevice.networkCountryKnown(countryIsoCode);
+            return this;
+        }
+
+        Script nitzReceived(TimestampedValue<NitzData> nitzSignal) {
+            mDevice.nitzSignalReceived(nitzSignal);
+            return this;
+        }
+
+        Script verifyNothingWasSetAndReset() {
+            mDevice.verifyTimeZoneWasNotSet();
+            mDevice.verifyTimeWasNotSuggested();
+            mDevice.checkNoUnverifiedSetOperations();
+            mDevice.resetInvocations();
+            return this;
+        }
+
+        Script verifyOnlyTimeZoneWasSetAndReset(String timeZoneId, int times) {
+            mDevice.verifyTimeZoneWasSet(timeZoneId, times);
+            mDevice.verifyTimeWasNotSuggested();
+            mDevice.checkNoUnverifiedSetOperations();
+            mDevice.resetInvocations();
+            return this;
+        }
+
+        Script verifyOnlyTimeZoneWasSetAndReset(String timeZoneId) {
+            return verifyOnlyTimeZoneWasSetAndReset(timeZoneId, 1);
+        }
+
+        Script verifyOnlyTimeWasSuggestedAndReset(TimestampedValue<NitzData> nitzSignal) {
+            mDevice.verifyTimeZoneWasNotSet();
+
+            TimestampedValue<Long> time = new TimestampedValue<>(
+                    nitzSignal.getReferenceTimeMillis(),
+                    nitzSignal.getValue().getCurrentTimeInMillis());
+            mDevice.verifyTimeWasSuggested(time);
+            mDevice.checkNoUnverifiedSetOperations();
+            mDevice.resetInvocations();
+            return this;
+        }
+
+        Script verifyTimeSuggestedAndZoneSetAndReset(
+                TimestampedValue<NitzData> nitzSignal, String timeZoneId) {
+            mDevice.verifyTimeZoneWasSet(timeZoneId);
+
+            TimestampedValue<Long> time = new TimestampedValue<>(
+                    nitzSignal.getReferenceTimeMillis(),
+                    nitzSignal.getValue().getCurrentTimeInMillis());
+            mDevice.verifyTimeWasSuggested(time);
+            mDevice.checkNoUnverifiedSetOperations();
+            mDevice.resetInvocations();
+            return this;
+        }
+
+        Script reset() {
+            mDevice.checkNoUnverifiedSetOperations();
+            mDevice.resetInvocations();
+            return this;
+        }
+    }
+
+    /**
+     * An abstraction of a device for use in telephony time zone detection tests. It can be used to
+     * retrieve device state, modify device state and verify changes.
+     */
+    class Device {
+
+        private final long mInitialSystemClockMillis;
+        private final long mInitialRealtimeMillis;
+        private final boolean mTimeZoneDetectionEnabled;
+        private final boolean mTimeZoneSettingInitialized;
+
+        Device(long initialSystemClockMillis, long initialRealtimeMillis,
+                boolean timeZoneDetectionEnabled, boolean timeZoneSettingInitialized) {
+            mInitialSystemClockMillis = initialSystemClockMillis;
+            mInitialRealtimeMillis = initialRealtimeMillis;
+            mTimeZoneDetectionEnabled = timeZoneDetectionEnabled;
+            mTimeZoneSettingInitialized = timeZoneSettingInitialized;
+        }
+
+        void initialize() {
+            // Set initial configuration.
+            when(mDeviceState.getIgnoreNitz()).thenReturn(false);
+            when(mDeviceState.getNitzUpdateDiffMillis()).thenReturn(2000);
+            when(mDeviceState.getNitzUpdateSpacingMillis()).thenReturn(1000 * 60 * 10);
+
+            // Simulate the country not being known.
+            when(mDeviceState.getNetworkCountryIsoForPhone()).thenReturn("");
+
+            when(mTimeServiceHelper.elapsedRealtime()).thenReturn(mInitialRealtimeMillis);
+            when(mTimeServiceHelper.currentTimeMillis()).thenReturn(mInitialSystemClockMillis);
+            when(mTimeServiceHelper.isTimeZoneDetectionEnabled())
+                    .thenReturn(mTimeZoneDetectionEnabled);
+            when(mTimeServiceHelper.isTimeZoneSettingInitialized())
+                    .thenReturn(mTimeZoneSettingInitialized);
+        }
+
+        void networkCountryKnown(String countryIsoCode) {
+            when(mDeviceState.getNetworkCountryIsoForPhone()).thenReturn(countryIsoCode);
+            mNitzStateMachine.handleNetworkCountryCodeSet(true);
+        }
+
+        void nitzSignalReceived(TimestampedValue<NitzData> nitzSignal) {
+            mNitzStateMachine.handleNitzReceived(nitzSignal);
+        }
+
+        void verifyTimeZoneWasNotSet() {
+            verify(mTimeServiceHelper, times(0)).setDeviceTimeZone(any(String.class));
+        }
+
+        void verifyTimeZoneWasSet(String timeZoneId) {
+            verifyTimeZoneWasSet(timeZoneId, 1 /* times */);
+        }
+
+        void verifyTimeZoneWasSet(String timeZoneId, int times) {
+            verify(mTimeServiceHelper, times(times)).setDeviceTimeZone(timeZoneId);
+        }
+
+        void verifyTimeWasNotSuggested() {
+            verify(mTimeServiceHelper, times(0)).suggestDeviceTime(any());
+        }
+
+        void verifyTimeWasSuggested(TimestampedValue<Long> expectedTime) {
+            verify(mTimeServiceHelper, times(1)).suggestDeviceTime(eq(expectedTime));
+        }
+
+        /**
+         * Used after calling verify... methods to reset expectations.
+         */
+        void resetInvocations() {
+            clearInvocations(mTimeServiceHelper);
+        }
+
+        void checkNoUnverifiedSetOperations() {
+            NewNitzStateMachineTest.checkNoUnverifiedSetOperations(mTimeServiceHelper);
+        }
+    }
+
+    /** A class used to construct a Device. */
+    class DeviceBuilder {
+
+        private long mInitialSystemClock;
+        private long mInitialRealtimeMillis;
+        private boolean mTimeZoneDetectionEnabled;
+        private boolean mTimeZoneSettingInitialized;
+
+        Device initialize() {
+            Device device = new Device(mInitialSystemClock, mInitialRealtimeMillis,
+                    mTimeZoneDetectionEnabled, mTimeZoneSettingInitialized);
+            device.initialize();
+            return device;
+        }
+
+        DeviceBuilder setTimeZoneDetectionEnabled(boolean enabled) {
+            mTimeZoneDetectionEnabled = enabled;
+            return this;
+        }
+
+        DeviceBuilder setTimeZoneSettingInitialized(boolean initialized) {
+            mTimeZoneSettingInitialized = initialized;
+            return this;
+        }
+
+        DeviceBuilder setClocksFromScenario(Scenario scenario) {
+            mInitialRealtimeMillis = scenario.getInitialRealTimeMillis();
+            mInitialSystemClock = scenario.getInitialSystemClockMillis();
+            return this;
+        }
+    }
+
+    /**
+     * A scenario used during tests. Describes a fictional reality.
+     */
+    static class Scenario {
+
+        private final long mInitialDeviceSystemClockMillis;
+        private final long mInitialDeviceRealtimeMillis;
+        private final long mActualTimeMillis;
+        private final TimeZone mZone;
+        private final String mNetworkCountryIsoCode;
+
+        private TimestampedValue<NitzData> mNitzSignal;
+
+        Scenario(long initialDeviceSystemClock, long elapsedRealtime, long timeMillis,
+                String zoneId, String countryIsoCode) {
+            mInitialDeviceSystemClockMillis = initialDeviceSystemClock;
+            mActualTimeMillis = timeMillis;
+            mInitialDeviceRealtimeMillis = elapsedRealtime;
+            mZone = TimeZone.getTimeZone(zoneId);
+            mNetworkCountryIsoCode = countryIsoCode;
+        }
+
+        TimestampedValue<NitzData> getNitzSignal() {
+            if (mNitzSignal == null) {
+                int[] offsets = new int[2];
+                mZone.getOffset(mActualTimeMillis, false /* local */, offsets);
+                int zoneOffsetMillis = offsets[0] + offsets[1];
+                NitzData nitzData = NitzData.createForTests(
+                        zoneOffsetMillis, offsets[1], mActualTimeMillis,
+                        null /* emulatorHostTimeZone */);
+                mNitzSignal = new TimestampedValue<>(mInitialDeviceRealtimeMillis, nitzData);
+            }
+            return mNitzSignal;
+        }
+
+        long getInitialRealTimeMillis() {
+            return mInitialDeviceRealtimeMillis;
+        }
+
+        long getInitialSystemClockMillis() {
+            return mInitialDeviceSystemClockMillis;
+        }
+
+        String getNetworkCountryIsoCode() {
+            return mNetworkCountryIsoCode;
+        }
+
+        String getTimeZoneId() {
+            return mZone.getID();
+        }
+
+        long getActualTimeMillis() {
+            return mActualTimeMillis;
+        }
+
+        static class Builder {
+
+            private long mInitialDeviceSystemClockMillis;
+            private long mInitialDeviceRealtimeMillis;
+            private long mActualTimeMillis;
+            private String mZoneId;
+            private String mCountryIsoCode;
+
+            Builder setInitialDeviceSystemClockUtc(int year, int monthInYear, int day,
+                    int hourOfDay, int minute, int second) {
+                mInitialDeviceSystemClockMillis = createUtcTime(year, monthInYear, day, hourOfDay,
+                        minute, second);
+                return this;
+            }
+
+            Builder setInitialDeviceRealtimeMillis(long realtimeMillis) {
+                mInitialDeviceRealtimeMillis = realtimeMillis;
+                return this;
+            }
+
+            Builder setActualTimeUtc(int year, int monthInYear, int day, int hourOfDay,
+                    int minute, int second) {
+                mActualTimeMillis = createUtcTime(year, monthInYear, day, hourOfDay, minute,
+                        second);
+                return this;
+            }
+
+            Builder setTimeZone(String zoneId) {
+                mZoneId = zoneId;
+                return this;
+            }
+
+            Builder setCountryIso(String isoCode) {
+                mCountryIsoCode = isoCode;
+                return this;
+            }
+
+            Scenario build() {
+                return new Scenario(mInitialDeviceSystemClockMillis, mInitialDeviceRealtimeMillis,
+                        mActualTimeMillis, mZoneId, mCountryIsoCode);
+            }
+        }
+    }
+
+    /**
+     * Confirms all mTimeServiceHelper side effects were verified.
+     */
+    private static void checkNoUnverifiedSetOperations(NewTimeServiceHelper mTimeServiceHelper) {
+        // We don't care about current auto time / time zone state retrievals / listening so we can
+        // use "at least 0" times to indicate they don't matter.
+        verify(mTimeServiceHelper, atLeast(0)).setListener(any());
+        verify(mTimeServiceHelper, atLeast(0)).isTimeZoneDetectionEnabled();
+        verify(mTimeServiceHelper, atLeast(0)).isTimeZoneSettingInitialized();
+        verify(mTimeServiceHelper, atLeast(0)).elapsedRealtime();
+        verify(mTimeServiceHelper, atLeast(0)).currentTimeMillis();
+        verifyNoMoreInteractions(mTimeServiceHelper);
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/OldNitzStateMachineTest.java b/tests/telephonytests/src/com/android/internal/telephony/OldNitzStateMachineTest.java
index 608e71b..2fc864a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/OldNitzStateMachineTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/OldNitzStateMachineTest.java
@@ -32,10 +32,10 @@
 import android.icu.util.Calendar;
 import android.icu.util.GregorianCalendar;
 import android.icu.util.TimeZone;
+import android.util.TimestampedValue;
 
 import com.android.internal.telephony.TimeZoneLookupHelper.CountryResult;
 import com.android.internal.telephony.TimeZoneLookupHelper.OffsetResult;
-import com.android.internal.telephony.util.TimeStampedValue;
 
 import org.junit.After;
 import org.junit.Before;
@@ -80,7 +80,7 @@
     private OldNitzStateMachine.DeviceState mDeviceState;
 
     @Mock
-    private TimeServiceHelper mTimeServiceHelper;
+    private OldTimeServiceHelper mTimeServiceHelper;
 
     private TimeZoneLookupHelper mRealTimeZoneLookupHelper;
 
@@ -124,7 +124,7 @@
         OffsetResult expectedLookupResult =
                 new OffsetResult("America/Los_Angeles", true /* isOnlyMatch */);
         OffsetResult actualLookupResult = mRealTimeZoneLookupHelper.lookupByNitzCountry(
-                UNIQUE_US_ZONE_SCENARIO.getNitzSignal().mValue,
+                UNIQUE_US_ZONE_SCENARIO.getNitzSignal().getValue(),
                 UNIQUE_US_ZONE_SCENARIO.getNetworkCountryIsoCode());
         assertEquals(expectedLookupResult, actualLookupResult);
     }
@@ -147,7 +147,7 @@
         OffsetResult expectedLookupResult =
                 new OffsetResult("Europe/London", true /* isOnlyMatch */);
         OffsetResult actualLookupResult = mRealTimeZoneLookupHelper.lookupByNitzCountry(
-                UNITED_KINGDOM_SCENARIO.getNitzSignal().mValue,
+                UNITED_KINGDOM_SCENARIO.getNitzSignal().getValue(),
                 UNITED_KINGDOM_SCENARIO.getNetworkCountryIsoCode());
         assertEquals(expectedLookupResult, actualLookupResult);
     }
@@ -176,7 +176,7 @@
 
         // Check NitzStateMachine state.
         assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
-        assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData());
+        assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
         assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
     }
 
@@ -204,7 +204,7 @@
 
         // Check NitzStateMachine state.
         assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
-        assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData());
+        assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
         assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
     }
 
@@ -231,7 +231,7 @@
 
         // Check NitzStateMachine state.
         assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
-        assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData());
+        assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
         assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
     }
 
@@ -259,7 +259,7 @@
 
         // Check NitzStateMachine state.
         assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
-        assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData());
+        assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
         assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
     }
 
@@ -283,7 +283,7 @@
 
         // Check NitzStateMachine state.
         assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
-        assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData());
+        assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
         assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
     }
 
@@ -307,7 +307,7 @@
 
         // Check NitzStateMachine state.
         assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
-        assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData());
+        assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
         assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
     }
 
@@ -331,7 +331,7 @@
 
         // Check NitzStateMachine state.
         assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
-        assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData());
+        assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
         assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
     }
 
@@ -355,7 +355,7 @@
 
         // Check NitzStateMachine state.
         assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
-        assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData());
+        assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
         assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
     }
 
@@ -377,7 +377,7 @@
 
         // Check NitzStateMachine state.
         assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
-        assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData());
+        assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
         assertNull(mNitzStateMachine.getSavedTimeZoneId());
 
         // Simulate the country code becoming known.
@@ -387,7 +387,7 @@
 
         // Check NitzStateMachine state.
         assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
-        assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData());
+        assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
         assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
     }
 
@@ -409,7 +409,7 @@
 
         // Check NitzStateMachine state.
         assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
-        assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData());
+        assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
         assertNull(mNitzStateMachine.getSavedTimeZoneId());
 
         // Simulate the country code becoming known.
@@ -423,7 +423,7 @@
 
         // Check NitzStateMachine state.
         assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
-        assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData());
+        assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
         assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
     }
 
@@ -438,7 +438,7 @@
                 .initialize();
         Script script = new Script(device);
 
-        TimeStampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+        TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
 
         // Simulate receiving an NITZ signal.
         script.nitzReceived(goodNitzSignal)
@@ -447,7 +447,7 @@
 
         // Check NitzStateMachine state.
         assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
-        assertEquals(goodNitzSignal.mValue, mNitzStateMachine.getCachedNitzData());
+        assertEquals(goodNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
         assertNull(mNitzStateMachine.getSavedTimeZoneId());
 
         // Simulate the country code becoming known.
@@ -458,7 +458,7 @@
 
         // Check NitzStateMachine state.
         assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
-        assertEquals(goodNitzSignal.mValue, mNitzStateMachine.getCachedNitzData());
+        assertEquals(goodNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
         assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
     }
 
@@ -473,7 +473,7 @@
                 .initialize();
         Script script = new Script(device);
 
-        TimeStampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+        TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
 
         // Simulate the country code becoming known.
         script.countryReceived(scenario.getNetworkCountryIsoCode())
@@ -494,7 +494,7 @@
 
         // Check NitzStateMachine state.
         assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
-        assertEquals(goodNitzSignal.mValue, mNitzStateMachine.getCachedNitzData());
+        assertEquals(goodNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
         assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
     }
 
@@ -509,14 +509,15 @@
                 .initialize();
         Script script = new Script(device);
 
-        TimeStampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+        TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
 
         // Create a corrupted NITZ signal, where the offset information has been lost.
         NitzData bogusNitzData = NitzData.createForTests(
                 0 /* UTC! */, null /* dstOffsetMillis */,
-                goodNitzSignal.mValue.getCurrentTimeInMillis(), null /* emulatorHostTimeZone */);
-        TimeStampedValue<NitzData> badNitzSignal = new TimeStampedValue<>(
-                bogusNitzData, goodNitzSignal.mElapsedRealtime);
+                goodNitzSignal.getValue().getCurrentTimeInMillis(),
+                null /* emulatorHostTimeZone */);
+        TimestampedValue<NitzData> badNitzSignal = new TimestampedValue<>(
+                goodNitzSignal.getReferenceTimeMillis(), bogusNitzData);
 
         // Simulate receiving an NITZ signal.
         script.nitzReceived(badNitzSignal)
@@ -526,7 +527,7 @@
 
         // Check NitzStateMachine state.
         assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
-        assertEquals(badNitzSignal.mValue, mNitzStateMachine.getCachedNitzData());
+        assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
         assertNull(mNitzStateMachine.getSavedTimeZoneId());
 
         // Simulate the country code becoming known.
@@ -537,7 +538,7 @@
 
         // Check NitzStateMachine state.
         assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
-        assertEquals(badNitzSignal.mValue, mNitzStateMachine.getCachedNitzData());
+        assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
         assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
     }
 
@@ -552,14 +553,15 @@
                 .initialize();
         Script script = new Script(device);
 
-        TimeStampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+        TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
 
         // Create a corrupted NITZ signal, where the offset information has been lost.
         NitzData bogusNitzData = NitzData.createForTests(
                 0 /* UTC! */, null /* dstOffsetMillis */,
-                goodNitzSignal.mValue.getCurrentTimeInMillis(), null /* emulatorHostTimeZone */);
-        TimeStampedValue<NitzData> badNitzSignal = new TimeStampedValue<>(
-                bogusNitzData, goodNitzSignal.mElapsedRealtime);
+                goodNitzSignal.getValue().getCurrentTimeInMillis(),
+                null /* emulatorHostTimeZone */);
+        TimestampedValue<NitzData> badNitzSignal = new TimestampedValue<>(
+                goodNitzSignal.getReferenceTimeMillis(), bogusNitzData);
 
         // Simulate the country code becoming known.
         script.countryReceived(scenario.getNetworkCountryIsoCode())
@@ -578,7 +580,7 @@
 
         // Check NitzStateMachine state.
         assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
-        assertEquals(badNitzSignal.mValue, mNitzStateMachine.getCachedNitzData());
+        assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
         assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
     }
 
@@ -593,14 +595,15 @@
                 .initialize();
         Script script = new Script(device);
 
-        TimeStampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+        TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
 
         // Create a corrupted NITZ signal, where the offset information has been lost.
         NitzData bogusNitzData = NitzData.createForTests(
                 0 /* UTC! */, null /* dstOffsetMillis */,
-                goodNitzSignal.mValue.getCurrentTimeInMillis(), null /* emulatorHostTimeZone */);
-        TimeStampedValue<NitzData> badNitzSignal = new TimeStampedValue<>(
-                bogusNitzData, goodNitzSignal.mElapsedRealtime);
+                goodNitzSignal.getValue().getCurrentTimeInMillis(),
+                null /* emulatorHostTimeZone */);
+        TimestampedValue<NitzData> badNitzSignal = new TimestampedValue<>(
+                goodNitzSignal.getReferenceTimeMillis(), bogusNitzData);
 
         // Simulate receiving an NITZ signal.
         script.nitzReceived(badNitzSignal)
@@ -610,7 +613,7 @@
 
         // Check NitzStateMachine state.
         assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
-        assertEquals(badNitzSignal.mValue, mNitzStateMachine.getCachedNitzData());
+        assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
         assertNull(mNitzStateMachine.getSavedTimeZoneId());
 
         // Simulate the country code becoming known.
@@ -621,7 +624,7 @@
 
         // Check NitzStateMachine state.
         assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
-        assertEquals(badNitzSignal.mValue, mNitzStateMachine.getCachedNitzData());
+        assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
         assertNull(mNitzStateMachine.getSavedTimeZoneId());
     }
 
@@ -636,14 +639,15 @@
                 .initialize();
         Script script = new Script(device);
 
-        TimeStampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+        TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
 
         // Create a corrupted NITZ signal, where the offset information has been lost.
         NitzData bogusNitzData = NitzData.createForTests(
                 0 /* UTC! */, null /* dstOffsetMillis */,
-                goodNitzSignal.mValue.getCurrentTimeInMillis(), null /* emulatorHostTimeZone */);
-        TimeStampedValue<NitzData> badNitzSignal = new TimeStampedValue<>(
-                bogusNitzData, goodNitzSignal.mElapsedRealtime);
+                goodNitzSignal.getValue().getCurrentTimeInMillis(),
+                null /* emulatorHostTimeZone */);
+        TimestampedValue<NitzData> badNitzSignal = new TimestampedValue<>(
+                goodNitzSignal.getReferenceTimeMillis(), bogusNitzData);
 
         // Simulate the country code becoming known.
         script.countryReceived(scenario.getNetworkCountryIsoCode())
@@ -663,7 +667,7 @@
 
         // Check NitzStateMachine state.
         assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
-        assertEquals(badNitzSignal.mValue, mNitzStateMachine.getCachedNitzData());
+        assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
         assertNull(mNitzStateMachine.getSavedTimeZoneId());
     }
 
@@ -678,10 +682,10 @@
                 .initialize();
         Script script = new Script(device);
 
-        TimeStampedValue<NitzData> originalNitzSignal = scenario.getNitzSignal();
+        TimestampedValue<NitzData> originalNitzSignal = scenario.getNitzSignal();
 
         // Create an NITZ signal with an explicit time zone (as can happen on emulators)
-        NitzData originalNitzData = originalNitzSignal.mValue;
+        NitzData originalNitzData = originalNitzSignal.getValue();
         // A time zone that is obviously not in the US, but it should not be questioned.
         String emulatorTimeZoneId = "Europe/London";
         NitzData emulatorNitzData = NitzData.createForTests(
@@ -689,8 +693,8 @@
                 originalNitzData.getDstAdjustmentMillis(),
                 originalNitzData.getCurrentTimeInMillis(),
                 java.util.TimeZone.getTimeZone(emulatorTimeZoneId) /* emulatorHostTimeZone */);
-        TimeStampedValue<NitzData> emulatorNitzSignal = new TimeStampedValue<>(
-                emulatorNitzData, originalNitzSignal.mElapsedRealtime);
+        TimestampedValue<NitzData> emulatorNitzSignal = new TimestampedValue<>(
+                originalNitzSignal.getReferenceTimeMillis(), emulatorNitzData);
 
         // Simulate receiving the emulator NITZ signal.
         script.nitzReceived(emulatorNitzSignal)
@@ -698,7 +702,7 @@
 
         // Check NitzStateMachine state.
         assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
-        assertEquals(emulatorNitzSignal.mValue, mNitzStateMachine.getCachedNitzData());
+        assertEquals(emulatorNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
         assertEquals(emulatorTimeZoneId, mNitzStateMachine.getSavedTimeZoneId());
     }
 
@@ -729,7 +733,7 @@
 
         // Check NitzStateMachine state.
         assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
-        assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData());
+        assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
         assertEquals(expectedZoneId, mNitzStateMachine.getSavedTimeZoneId());
     }
 
@@ -752,7 +756,7 @@
 
         // Check NitzStateMachine state.
         assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
-        assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData());
+        assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
         assertNull(mNitzStateMachine.getSavedTimeZoneId());
 
         // The time zone should be set (but the country is not valid so it's unlikely to be
@@ -761,7 +765,7 @@
 
         // Check NitzStateMachine state.
         assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
-        assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData());
+        assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
         assertEquals(expectedZoneId, mNitzStateMachine.getSavedTimeZoneId());
     }
 
@@ -772,7 +776,7 @@
      */
     private String checkNitzOnlyLookupIsAmbiguousAndReturnZoneId(Scenario scenario) {
         OffsetResult result =
-                mRealTimeZoneLookupHelper.lookupByNitz(scenario.getNitzSignal().mValue);
+                mRealTimeZoneLookupHelper.lookupByNitz(scenario.getNitzSignal().getValue());
         String expectedZoneId = result.zoneId;
         // All our scenarios should return multiple matches. The only cases where this wouldn't be
         // true are places that use offsets like XX:15, XX:30 and XX:45.
@@ -809,7 +813,7 @@
             return this;
         }
 
-        Script nitzReceived(TimeStampedValue<NitzData> nitzSignal) {
+        Script nitzReceived(TimestampedValue<NitzData> nitzSignal) {
             mDevice.nitzSignalReceived(nitzSignal);
             return this;
         }
@@ -914,7 +918,7 @@
             when(mTimeServiceHelper.currentTimeMillis()).thenReturn(currentTimeMillis + millis);
         }
 
-        void nitzSignalReceived(TimeStampedValue<NitzData> nitzSignal) {
+        void nitzSignalReceived(TimestampedValue<NitzData> nitzSignal) {
             mNitzStateMachine.handleNitzReceived(nitzSignal);
         }
 
@@ -1001,7 +1005,7 @@
         private final TimeZone mZone;
         private final String mNetworkCountryIsoCode;
 
-        private TimeStampedValue<NitzData> mNitzSignal;
+        private TimestampedValue<NitzData> mNitzSignal;
 
         Scenario(long initialDeviceSystemClock, long elapsedRealtime, long timeMillis,
                 String zoneId, String countryIsoCode) {
@@ -1012,7 +1016,7 @@
             mNetworkCountryIsoCode = countryIsoCode;
         }
 
-        TimeStampedValue<NitzData> getNitzSignal() {
+        TimestampedValue<NitzData> getNitzSignal() {
             if (mNitzSignal == null) {
                 int[] offsets = new int[2];
                 mZone.getOffset(mActualTimeMillis, false /* local */, offsets);
@@ -1020,7 +1024,7 @@
                 NitzData nitzData = NitzData.createForTests(
                         zoneOffsetMillis, offsets[1], mActualTimeMillis,
                         null /* emulatorHostTimeZone */);
-                mNitzSignal = new TimeStampedValue<>(nitzData, mInitialDeviceRealtimeMillis);
+                mNitzSignal = new TimestampedValue<>(mInitialDeviceRealtimeMillis, nitzData);
             }
             return mNitzSignal;
         }
@@ -1092,7 +1096,7 @@
     /**
      * Confirms all mTimeServiceHelper side effects were verified.
      */
-    private static void checkNoUnverifiedSetOperations(TimeServiceHelper mTimeServiceHelper) {
+    private static void checkNoUnverifiedSetOperations(OldTimeServiceHelper mTimeServiceHelper) {
         // We don't care about current auto time / time zone state retrievals / listening so we can
         // use "at least 0" times to indicate they don't matter.
         verify(mTimeServiceHelper, atLeast(0)).setListener(any());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
index dd356aa..10dd6f2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
@@ -115,22 +115,25 @@
 import android.telephony.CellIdentityCdma;
 import android.telephony.CellIdentityGsm;
 import android.telephony.CellIdentityLte;
+import android.telephony.CellIdentityTdscdma;
 import android.telephony.CellIdentityWcdma;
 import android.telephony.CellInfo;
 import android.telephony.CellInfoCdma;
 import android.telephony.CellInfoGsm;
 import android.telephony.CellInfoLte;
+import android.telephony.CellInfoTdscdma;
 import android.telephony.CellInfoWcdma;
 import android.telephony.CellSignalStrengthCdma;
 import android.telephony.CellSignalStrengthGsm;
 import android.telephony.CellSignalStrengthLte;
+import android.telephony.CellSignalStrengthTdscdma;
 import android.telephony.CellSignalStrengthWcdma;
 import android.telephony.SmsManager;
 import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
 import android.telephony.data.DataProfile;
 
 import com.android.internal.telephony.RIL.RilHandler;
-import com.android.internal.telephony.dataconnection.ApnSetting;
 import com.android.internal.telephony.dataconnection.DcTracker;
 
 import org.junit.After;
@@ -194,6 +197,8 @@
     private static final int RSSNR = 2147483647;
     private static final int RSRP = 96;
     private static final int RSRQ = 10;
+    private static final int RSCP = 94;
+    private static final int ECNO = 5;
     private static final int SIGNAL_NOISE_RATIO = 6;
     private static final int SIGNAL_STRENGTH = 24;
     private static final int SYSTEM_ID = 65533;
@@ -205,6 +210,7 @@
     private static final int TYPE_GSM = 1;
     private static final int TYPE_LTE = 3;
     private static final int TYPE_WCDMA = 4;
+    private static final int TYPE_TD_SCDMA = 5;
 
     private static final int PROFILE_ID = 0;
     private static final String APN = "apn";
@@ -692,11 +698,12 @@
     @FlakyTest
     @Test
     public void testSetInitialAttachApn() throws Exception {
-        ApnSetting apnSetting = new ApnSetting(
-                -1, "22210", "Vodafone IT", "web.omnitel.it", "", "",
-                "", "", "", "", "", 0, new String[]{"DUN"}, "IP", "IP", true, 0, 0,
-                0, false, 0, 0, 0, 0, "", "");
-        DataProfile dataProfile = DcTracker.createDataProfile(apnSetting, apnSetting.profileId);
+        ApnSetting apnSetting = ApnSetting.makeApnSetting(
+                -1, "22210", "Vodafone IT", "web.omnitel.it", null, -1,
+                null, null, -1, "", "", 0, ApnSetting.TYPE_DUN, ApnSetting.PROTOCOL_IP,
+                ApnSetting.PROTOCOL_IP, true, 0, 0, false, 0, 0, 0, 0, -1, "");
+        DataProfile dataProfile = DcTracker.createDataProfile(
+                apnSetting, apnSetting.getProfileId());
         boolean isRoaming = false;
 
         mRILUnderTest.setInitialAttachApn(dataProfile, isRoaming, obtainMessage());
@@ -1183,7 +1190,8 @@
         expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
         CellIdentityWcdma ci = new CellIdentityWcdma(
                 LAC, CID, PSC, UARFCN, MCC_STR, MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
-        CellSignalStrengthWcdma cs = new CellSignalStrengthWcdma(SIGNAL_STRENGTH, BIT_ERROR_RATE);
+        CellSignalStrengthWcdma cs = new CellSignalStrengthWcdma(
+                SIGNAL_STRENGTH, BIT_ERROR_RATE, Integer.MAX_VALUE, Integer.MAX_VALUE);
         expected.setCellIdentity(ci);
         expected.setCellSignalStrength(cs);
         expected.setCellConnectionStatus(CellInfo.CONNECTION_UNKNOWN);
@@ -1191,6 +1199,47 @@
     }
 
     @Test
+    public void testConvertHalCellInfoListForTdscdma() throws Exception {
+        android.hardware.radio.V1_2.CellInfoTdscdma cellinfo =
+                new android.hardware.radio.V1_2.CellInfoTdscdma();
+        cellinfo.cellIdentityTdscdma.base.lac = LAC;
+        cellinfo.cellIdentityTdscdma.base.cid = CID;
+        cellinfo.cellIdentityTdscdma.base.cpid = PSC;
+        cellinfo.cellIdentityTdscdma.uarfcn = UARFCN;
+        cellinfo.cellIdentityTdscdma.base.mcc = MCC_STR;
+        cellinfo.cellIdentityTdscdma.base.mnc = MNC_STR;
+        cellinfo.signalStrengthTdscdma.signalStrength = SIGNAL_STRENGTH;
+        cellinfo.signalStrengthTdscdma.bitErrorRate = BIT_ERROR_RATE;
+        cellinfo.signalStrengthTdscdma.rscp = RSCP;
+        android.hardware.radio.V1_2.CellInfo record = new android.hardware.radio.V1_2.CellInfo();
+        record.cellInfoType = TYPE_TD_SCDMA;
+        record.registered = false;
+        record.timeStampType = RIL_TIMESTAMP_TYPE_OEM_RIL;
+        record.timeStamp = TIMESTAMP;
+        record.tdscdma.add(cellinfo);
+        ArrayList<android.hardware.radio.V1_2.CellInfo> records =
+                new ArrayList<android.hardware.radio.V1_2.CellInfo>();
+        records.add(record);
+
+        ArrayList<CellInfo> ret = RIL.convertHalCellInfoList_1_2(records);
+
+        assertEquals(1, ret.size());
+        CellInfoTdscdma cellInfoTdscdma = (CellInfoTdscdma) ret.get(0);
+        CellInfoTdscdma expected = new CellInfoTdscdma();
+        expected.setRegistered(false);
+        expected.setTimeStamp(TIMESTAMP);
+        expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
+        expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
+        CellIdentityTdscdma ci = new CellIdentityTdscdma(
+                MCC_STR, MNC_STR, LAC, CID, PSC, UARFCN, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
+        CellSignalStrengthTdscdma cs = new CellSignalStrengthTdscdma(
+                SIGNAL_STRENGTH, BIT_ERROR_RATE, RSCP);
+        expected.setCellIdentity(ci);
+        expected.setCellSignalStrength(cs);
+        assertEquals(expected, cellInfoTdscdma);
+    }
+
+    @Test
     public void testConvertHalCellInfoListForCdma() throws Exception {
         android.hardware.radio.V1_0.CellInfoCdma cellinfo =
                 new android.hardware.radio.V1_0.CellInfoCdma();
@@ -1374,7 +1423,8 @@
         expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
         CellIdentityWcdma ci = new CellIdentityWcdma(
                 LAC, CID, PSC, UARFCN, MCC_STR, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
-        CellSignalStrengthWcdma cs = new CellSignalStrengthWcdma(SIGNAL_STRENGTH, BIT_ERROR_RATE);
+        CellSignalStrengthWcdma cs =
+                new CellSignalStrengthWcdma(SIGNAL_STRENGTH, BIT_ERROR_RATE, RSCP, ECNO);
         expected.setCellIdentity(ci);
         expected.setCellSignalStrength(cs);
         expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
@@ -1394,7 +1444,8 @@
         expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
         CellIdentityWcdma ci = new CellIdentityWcdma(
                 LAC, CID, PSC, UARFCN, MCC_STR, MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
-        CellSignalStrengthWcdma cs = new CellSignalStrengthWcdma(SIGNAL_STRENGTH, BIT_ERROR_RATE);
+        CellSignalStrengthWcdma cs = new CellSignalStrengthWcdma(
+                SIGNAL_STRENGTH, BIT_ERROR_RATE, RSCP, ECNO);
         expected.setCellIdentity(ci);
         expected.setCellSignalStrength(cs);
         expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
@@ -1416,7 +1467,8 @@
         expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
         CellIdentityWcdma ci = new CellIdentityWcdma(
                 LAC, CID, PSC, UARFCN, null, null, ALPHA_LONG, ALPHA_SHORT);
-        CellSignalStrengthWcdma cs = new CellSignalStrengthWcdma(SIGNAL_STRENGTH, BIT_ERROR_RATE);
+        CellSignalStrengthWcdma cs = new CellSignalStrengthWcdma(
+                SIGNAL_STRENGTH, BIT_ERROR_RATE, RSCP, ECNO);
         expected.setCellIdentity(ci);
         expected.setCellSignalStrength(cs);
         expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
@@ -1539,8 +1591,8 @@
         cellinfo.cellIdentityWcdma.operatorNames.alphaShort = alphaShort;
         cellinfo.signalStrengthWcdma.base.signalStrength = SIGNAL_STRENGTH;
         cellinfo.signalStrengthWcdma.base.bitErrorRate = BIT_ERROR_RATE;
-        cellinfo.signalStrengthWcdma.rscp = 10;
-        cellinfo.signalStrengthWcdma.ecno = 5;
+        cellinfo.signalStrengthWcdma.rscp = RSCP;
+        cellinfo.signalStrengthWcdma.ecno = ECNO;
         android.hardware.radio.V1_2.CellInfo record = new android.hardware.radio.V1_2.CellInfo();
         record.cellInfoType = TYPE_WCDMA;
         record.registered = false;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java
index 0fa88bf..55fde49 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java
@@ -282,18 +282,18 @@
     @SmallTest
     public void testNetworkRegistrationState() {
         NetworkRegistrationState wwanVoiceRegState = new NetworkRegistrationState(
-                AccessNetworkConstants.TransportType.WWAN, NetworkRegistrationState.DOMAIN_CS,
+                NetworkRegistrationState.DOMAIN_CS, AccessNetworkConstants.TransportType.WWAN,
                 0, 0, 0, false,
                 null, null, true, 0, 0, 0);
 
 
         NetworkRegistrationState wwanDataRegState = new NetworkRegistrationState(
-                AccessNetworkConstants.TransportType.WWAN, NetworkRegistrationState.DOMAIN_PS,
+                NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TransportType.WWAN,
                 0, 0, 0, false,
                 null, null, 0);
 
         NetworkRegistrationState wlanRegState = new NetworkRegistrationState(
-                AccessNetworkConstants.TransportType.WLAN, NetworkRegistrationState.DOMAIN_PS,
+                NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TransportType.WLAN,
                 0, 0, 0, false,
                 null, null);
 
@@ -303,20 +303,20 @@
         ss.addNetworkRegistrationState(wwanDataRegState);
         ss.addNetworkRegistrationState(wlanRegState);
 
-        assertEquals(ss.getNetworkRegistrationStates(AccessNetworkConstants.TransportType.WWAN,
-                NetworkRegistrationState.DOMAIN_CS), wwanVoiceRegState);
-        assertEquals(ss.getNetworkRegistrationStates(AccessNetworkConstants.TransportType.WWAN,
-                NetworkRegistrationState.DOMAIN_PS), wwanDataRegState);
-        assertEquals(ss.getNetworkRegistrationStates(AccessNetworkConstants.TransportType.WLAN,
-                NetworkRegistrationState.DOMAIN_PS), wlanRegState);
+        assertEquals(ss.getNetworkRegistrationStates(NetworkRegistrationState.DOMAIN_CS,
+                AccessNetworkConstants.TransportType.WWAN), wwanVoiceRegState);
+        assertEquals(ss.getNetworkRegistrationStates(NetworkRegistrationState.DOMAIN_PS,
+                AccessNetworkConstants.TransportType.WWAN), wwanDataRegState);
+        assertEquals(ss.getNetworkRegistrationStates(NetworkRegistrationState.DOMAIN_PS,
+                AccessNetworkConstants.TransportType.WLAN), wlanRegState);
 
         wwanDataRegState = new NetworkRegistrationState(
-                AccessNetworkConstants.TransportType.WWAN, NetworkRegistrationState.DOMAIN_PS,
+                NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TransportType.WWAN,
                 0, 0, 0, true,
                 null, null, 0);
         ss.addNetworkRegistrationState(wwanDataRegState);
-        assertEquals(ss.getNetworkRegistrationStates(AccessNetworkConstants.TransportType.WWAN,
-                NetworkRegistrationState.DOMAIN_PS), wwanDataRegState);
+        assertEquals(ss.getNetworkRegistrationStates(NetworkRegistrationState.DOMAIN_PS,
+                AccessNetworkConstants.TransportType.WWAN), wwanDataRegState);
     }
 
     @SmallTest
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index 3108d2b..603f800 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -60,24 +60,27 @@
 import android.telephony.AccessNetworkConstants.AccessNetworkType;
 import android.telephony.CarrierConfigManager;
 import android.telephony.CellIdentityGsm;
+import android.telephony.CellIdentityLte;
 import android.telephony.CellInfo;
 import android.telephony.CellInfoGsm;
 import android.telephony.NetworkRegistrationState;
 import android.telephony.NetworkService;
+import android.telephony.PhysicalChannelConfig;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.telephony.gsm.GsmCellLocation;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Pair;
+import android.util.TimestampedValue;
 
 import com.android.internal.R;
 import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
 import com.android.internal.telephony.dataconnection.DcTracker;
 import com.android.internal.telephony.test.SimulatedCommands;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus;
-import com.android.internal.telephony.util.TimeStampedValue;
 
 import org.junit.After;
 import org.junit.Before;
@@ -88,6 +91,7 @@
 import org.mockito.Mockito;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 
@@ -137,7 +141,7 @@
         mCellularNetworkService = new CellularNetworkService();
         ServiceInfo serviceInfo =  new ServiceInfo();
         serviceInfo.packageName = "com.android.phone";
-        serviceInfo.permission = "android.permission.BIND_NETWORK_SERVICE";
+        serviceInfo.permission = "android.permission.BIND_TELEPHONY_NETWORK_SERVICE";
         IntentFilter filter = new IntentFilter();
         mContextFixture.addService(
                 NetworkService.NETWORK_SERVICE_INTERFACE,
@@ -1490,15 +1494,15 @@
             mSimulatedCommands.triggerNITZupdate(nitzStr);
             waitForMs(200);
 
-            ArgumentCaptor<TimeStampedValue<NitzData>> argumentsCaptor =
-                    ArgumentCaptor.forClass(TimeStampedValue.class);
+            ArgumentCaptor<TimestampedValue<NitzData>> argumentsCaptor =
+                    ArgumentCaptor.forClass(TimestampedValue.class);
             verify(mNitzStateMachine, times(1))
                     .handleNitzReceived(argumentsCaptor.capture());
 
             // Confirm the argument was what we expected.
-            TimeStampedValue<NitzData> actualNitzSignal = argumentsCaptor.getValue();
-            assertEquals(expectedNitzData, actualNitzSignal.mValue);
-            assertTrue(actualNitzSignal.mElapsedRealtime <= SystemClock.elapsedRealtime());
+            TimestampedValue<NitzData> actualNitzSignal = argumentsCaptor.getValue();
+            assertEquals(expectedNitzData, actualNitzSignal.getValue());
+            assertTrue(actualNitzSignal.getReferenceTimeMillis() <= SystemClock.elapsedRealtime());
         }
     }
 
@@ -1627,4 +1631,97 @@
         waitForMs(200);
         assertEquals(ServiceState.RIL_RADIO_TECHNOLOGY_GPRS, sst.mSS.getRilVoiceRadioTechnology());
     }
+
+    private void sendPhyChanConfigChange(int[] bandwidths) {
+        ArrayList<PhysicalChannelConfig> pc = new ArrayList<>();
+        int ssType = PhysicalChannelConfig.CONNECTION_PRIMARY_SERVING;
+        for (int bw : bandwidths) {
+            pc.add(new PhysicalChannelConfig(ssType, bw));
+
+            // All cells after the first are secondary serving cells.
+            ssType = PhysicalChannelConfig.CONNECTION_SECONDARY_SERVING;
+        }
+        sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_PHYSICAL_CHANNEL_CONFIG,
+                new AsyncResult(null, pc, null)));
+        waitForMs(100);
+    }
+
+    private void sendRegStateUpdateForLteCellId(CellIdentityLte cellId) {
+        NetworkRegistrationState dataResult = new NetworkRegistrationState(
+                2, 1, 1, TelephonyManager.NETWORK_TYPE_LTE, 0, false, null, cellId, 1);
+        NetworkRegistrationState voiceResult = new NetworkRegistrationState(
+                1, 1, 1, TelephonyManager.NETWORK_TYPE_LTE, 0, false, null, cellId,
+                false, 0, 0, 0);
+        sst.mPollingContext[0] = 2;
+        // update data reg state to be in service
+        sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_GPRS,
+                new AsyncResult(sst.mPollingContext, dataResult, null)));
+        waitForMs(200);
+        sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_REGISTRATION,
+                new AsyncResult(sst.mPollingContext, voiceResult, null)));
+        waitForMs(200);
+    }
+
+    @Test
+    public void testPhyChanBandwidthUpdatedOnDataRegState() throws Exception {
+        // Cell ID change should trigger hasLocationChanged.
+        CellIdentityLte cellIdentity5 =
+                new CellIdentityLte(1, 1, 5, 1, 5000, "001", "01", "test", "tst");
+
+        sendPhyChanConfigChange(new int[] {10000});
+        sendRegStateUpdateForLteCellId(cellIdentity5);
+        assertTrue(Arrays.equals(new int[] {5000}, sst.mSS.getCellBandwidths()));
+    }
+
+    @Test
+    public void testPhyChanBandwidthNotUpdatedWhenInvalidInCellIdentity() throws Exception {
+        // Cell ID change should trigger hasLocationChanged.
+        CellIdentityLte cellIdentityInv =
+                new CellIdentityLte(1, 1, 5, 1, 12345, "001", "01", "test", "tst");
+
+        sendPhyChanConfigChange(new int[] {10000});
+        sendRegStateUpdateForLteCellId(cellIdentityInv);
+        assertTrue(Arrays.equals(new int[] {10000}, sst.mSS.getCellBandwidths()));
+    }
+
+    @Test
+    public void testPhyChanBandwidthPrefersCarrierAggregationReport() throws Exception {
+        // Cell ID change should trigger hasLocationChanged.
+        CellIdentityLte cellIdentity10 =
+                new CellIdentityLte(1, 1, 5, 1, 10000, "001", "01", "test", "tst");
+
+        sendPhyChanConfigChange(new int[] {10000, 5000});
+        sendRegStateUpdateForLteCellId(cellIdentity10);
+        assertTrue(Arrays.equals(new int[] {10000, 5000}, sst.mSS.getCellBandwidths()));
+    }
+
+    @Test
+    public void testPhyChanBandwidthRatchetedOnPhyChanBandwidth() throws Exception {
+        // LTE Cell with bandwidth = 10000
+        CellIdentityLte cellIdentity10 =
+                new CellIdentityLte(1, 1, 1, 1, 10000, "1", "1", "test", "tst");
+
+        sendRegStateUpdateForLteCellId(cellIdentity10);
+        assertTrue(Arrays.equals(new int[] {10000}, sst.mSS.getCellBandwidths()));
+        sendPhyChanConfigChange(new int[] {10000, 5000});
+        assertTrue(Arrays.equals(new int[] {10000, 5000}, sst.mSS.getCellBandwidths()));
+    }
+
+    @Test
+    public void testPhyChanBandwidthResetsOnOos() throws Exception {
+        testPhyChanBandwidthRatchetedOnPhyChanBandwidth();
+        NetworkRegistrationState dataResult = new NetworkRegistrationState(
+                2, 1, 0, TelephonyManager.NETWORK_TYPE_UNKNOWN, 0, false, null, null, 1);
+        NetworkRegistrationState voiceResult = new NetworkRegistrationState(
+                1, 1, 0, TelephonyManager.NETWORK_TYPE_UNKNOWN, 0, false, null, null,
+                false, 0, 0, 0);
+        sst.mPollingContext[0] = 2;
+        sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_GPRS,
+                new AsyncResult(sst.mPollingContext, dataResult, null)));
+        waitForMs(200);
+        sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_REGISTRATION,
+                new AsyncResult(sst.mPollingContext, voiceResult, null)));
+        waitForMs(200);
+        assertTrue(Arrays.equals(new int[0], sst.mSS.getCellBandwidths()));
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index 56c6e91..89d8143 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -332,7 +332,8 @@
                         nullable(CommandsInterface.class));
         doReturn(mUiccProfile).when(mTelephonyComponentFactory)
                 .makeUiccProfile(nullable(Context.class), nullable(CommandsInterface.class),
-                        nullable(IccCardStatus.class), anyInt(), nullable(UiccCard.class));
+                        nullable(IccCardStatus.class), anyInt(), nullable(UiccCard.class),
+                        nullable(Object.class));
         doReturn(mCT).when(mTelephonyComponentFactory)
                 .makeGsmCdmaCallTracker(nullable(GsmCdmaPhone.class));
         doReturn(mIccPhoneBookIntManager).when(mTelephonyComponentFactory)
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnContextTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnContextTest.java
index 8c49c21..6722691 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnContextTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnContextTest.java
@@ -16,9 +16,19 @@
 
 package com.android.internal.telephony.dataconnection;
 
-import android.net.NetworkCapabilities;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
 import android.net.NetworkConfig;
 import android.net.NetworkRequest;
+import android.telephony.data.ApnSetting;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.LocalLog;
 
@@ -32,16 +42,6 @@
 import org.junit.Test;
 import org.mockito.Mock;
 
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertTrue;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
 public class ApnContextTest extends TelephonyTest {
 
     @Mock
@@ -143,14 +143,14 @@
         NetworkRequest nr = new NetworkRequest.Builder().build();
         mApnContext.requestNetwork(nr, log);
 
-        verify(mDcTracker, times(1)).setEnabled(eq(DctConstants.APN_DEFAULT_ID), eq(true));
+        verify(mDcTracker, times(1)).setEnabled(eq(ApnSetting.TYPE_DEFAULT), eq(true));
         mApnContext.requestNetwork(nr, log);
-        verify(mDcTracker, times(1)).setEnabled(eq(DctConstants.APN_DEFAULT_ID), eq(true));
+        verify(mDcTracker, times(1)).setEnabled(eq(ApnSetting.TYPE_DEFAULT), eq(true));
 
         mApnContext.releaseNetwork(nr, log);
-        verify(mDcTracker, times(1)).setEnabled(eq(DctConstants.APN_DEFAULT_ID), eq(false));
+        verify(mDcTracker, times(1)).setEnabled(eq(ApnSetting.TYPE_DEFAULT), eq(false));
         mApnContext.releaseNetwork(nr, log);
-        verify(mDcTracker, times(1)).setEnabled(eq(DctConstants.APN_DEFAULT_ID), eq(false));
+        verify(mDcTracker, times(1)).setEnabled(eq(ApnSetting.TYPE_DEFAULT), eq(false));
     }
 
     @Test
@@ -176,32 +176,31 @@
     public void testProvisionApn() throws Exception {
         mContextFixture.putResource(R.string.mobile_provisioning_apn, "fake_apn");
 
-        ApnSetting myApn = new ApnSetting(
+        ApnSetting myApn = ApnSetting.makeApnSetting(
                 2163,                   // id
                 "44010",                // numeric
                 "sp-mode",              // name
                 "fake_apn",             // apn
-                "",                     // proxy
-                "",                     // port
-                "",                     // mmsc
-                "",                     // mmsproxy
-                "",                     // mmsport
+                null,                     // proxy
+                -1,                     // port
+                null,                     // mmsc
+                null,                     // mmsproxy
+                -1,                     // mmsport
                 "",                     // user
                 "",                     // password
                 -1,                     // authtype
-                new String[]{"default", "supl"},     // types
-                "IP",                   // protocol
-                "IP",                   // roaming_protocol
+                ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL,     // types
+                ApnSetting.PROTOCOL_IP,                   // protocol
+                ApnSetting.PROTOCOL_IP,                   // roaming_protocol
                 true,                   // carrier_enabled
-                0,                      // bearer
-                0,                      // bearer_bitmask
+                0,                      // networktype_bismask
                 0,                      // profile_id
                 false,                  // modem_cognitive
                 0,                      // max_conns
                 0,                      // wait_time
                 0,                      // max_conns_time
                 0,                      // mtu
-                "",                     // mvno_type
+                -1,                     // mvno_type
                 "");                    // mnvo_match_data
 
         mApnContext.setApnSetting(myApn);
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 4c49e5b..b9738e5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
@@ -16,13 +16,6 @@
 
 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;
@@ -30,9 +23,11 @@
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Mockito.doReturn;
 
+import android.net.Uri;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.ServiceState;
+import android.telephony.data.ApnSetting;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.internal.telephony.PhoneConstants;
@@ -44,6 +39,7 @@
 
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
+import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -62,41 +58,40 @@
         super.tearDown();
     }
 
-    static ApnSetting createApnSetting(String[] apnTypes) {
-        return createApnSettingInternal(apnTypes, true);
+    static ApnSetting createApnSetting(int apnTypesBitmask) {
+        return createApnSettingInternal(apnTypesBitmask, true);
     }
 
-    private static ApnSetting createDisabledApnSetting(String[] apnTypes) {
-        return createApnSettingInternal(apnTypes, false);
+    private static ApnSetting createDisabledApnSetting(int apnTypesBitmask) {
+        return createApnSettingInternal(apnTypesBitmask, false);
     }
 
-    private static ApnSetting createApnSettingInternal(String[] apnTypes, boolean carrierEnabled) {
-        return new ApnSetting(
+    private static ApnSetting createApnSettingInternal(int apnTypeBitmask, boolean carrierEnabled) {
+        return ApnSetting.makeApnSetting(
                 2163,                   // id
                 "44010",                // numeric
                 "sp-mode",              // name
                 "spmode.ne.jp",         // apn
-                "",                     // proxy
-                "",                     // port
-                "",                     // mmsc
-                "",                     // mmsproxy
-                "",                     // mmsport
+                null,                     // proxy
+                -1,                     // port
+                null,                     // mmsc
+                null,                     // mmsproxy
+                -1,                     // mmsport
                 "",                     // user
                 "",                     // password
                 -1,                     // authtype
-                apnTypes,               // types
-                "IP",                   // protocol
-                "IP",                   // roaming_protocol
+                apnTypeBitmask,               // types
+                ApnSetting.PROTOCOL_IP,                   // protocol
+                ApnSetting.PROTOCOL_IP,                   // roaming_protocol
                 carrierEnabled,         // carrier_enabled
-                0,                      // bearer
-                0,                      // bearer_bitmask
+                0,                      // networktype_bitmask
                 0,                      // profile_id
                 false,                  // modem_cognitive
                 0,                      // max_conns
                 0,                      // wait_time
                 0,                      // max_conns_time
                 0,                      // mtu
-                "",                     // mvno_type
+                -1,                     // mvno_type
                 "");                    // mnvo_match_data
     }
 
@@ -108,91 +103,84 @@
     }
 
     private static void assertApnSettingEqual(ApnSetting a1, ApnSetting a2) {
-        assertEquals(a1.carrier, a2.carrier);
-        assertEquals(a1.apn, a2.apn);
-        assertEquals(a1.proxy, a2.proxy);
-        assertEquals(a1.port, a2.port);
-        assertEquals(a1.mmsc, a2.mmsc);
-        assertEquals(a1.mmsProxy, a2.mmsProxy);
-        assertEquals(a1.mmsPort, a2.mmsPort);
-        assertEquals(a1.user, a2.user);
-        assertEquals(a1.password, a2.password);
-        assertEquals(a1.authType, a2.authType);
-        assertEquals(a1.id, a2.id);
-        assertEquals(a1.numeric, a2.numeric);
-        assertEquals(a1.protocol, a2.protocol);
-        assertEquals(a1.roamingProtocol, a2.roamingProtocol);
-        assertEquals(a1.types.length, a2.types.length);
-        int i;
-        for (i = 0; i < a1.types.length; i++) {
-            assertEquals(a1.types[i], a2.types[i]);
-        }
-        assertEquals(a1.carrierEnabled, a2.carrierEnabled);
-        assertEquals(a1.bearerBitmask, a2.bearerBitmask);
-        assertEquals(a1.profileId, a2.profileId);
-        assertEquals(a1.modemCognitive, a2.modemCognitive);
-        assertEquals(a1.maxConns, a2.maxConns);
-        assertEquals(a1.waitTime, a2.waitTime);
-        assertEquals(a1.maxConnsTime, a2.maxConnsTime);
-        assertEquals(a1.mtu, a2.mtu);
-        assertEquals(a1.mvnoType, a2.mvnoType);
-        assertEquals(a1.mvnoMatchData, a2.mvnoMatchData);
-        assertEquals(a1.networkTypeBitmask, a2.networkTypeBitmask);
-        assertEquals(a1.apnSetId, a2.apnSetId);
+        assertEquals(a1.getEntryName(), a2.getEntryName());
+        assertEquals(a1.getApnName(), a2.getApnName());
+        assertEquals(a1.getProxyAddressAsString(), a2.getProxyAddressAsString());
+        assertEquals(a1.getProxyPort(), a2.getProxyPort());
+        assertEquals(a1.getMmsc(), a2.getMmsc());
+        assertEquals(a1.getMmsProxyAddressAsString(), a2.getMmsProxyAddressAsString());
+        assertEquals(a1.getMmsProxyPort(), a2.getMmsProxyPort());
+        assertEquals(a1.getUser(), a2.getUser());
+        assertEquals(a1.getPassword(), a2.getPassword());
+        assertEquals(a1.getAuthType(), a2.getAuthType());
+        assertEquals(a1.getId(), a2.getId());
+        assertEquals(a1.getOperatorNumeric(), a2.getOperatorNumeric());
+        assertEquals(a1.getProtocol(), a2.getProtocol());
+        assertEquals(a1.getRoamingProtocol(), a2.getRoamingProtocol());
+        assertEquals(a1.getApnTypeBitmask(), a2.getApnTypeBitmask());
+        assertEquals(a1.isEnabled(), a2.isEnabled());
+        assertEquals(a1.getProfileId(), a2.getProfileId());
+        assertEquals(a1.getModemCognitive(), a2.getModemCognitive());
+        assertEquals(a1.getMaxConns(), a2.getMaxConns());
+        assertEquals(a1.getWaitTime(), a2.getWaitTime());
+        assertEquals(a1.getMaxConnsTime(), a2.getMaxConnsTime());
+        assertEquals(a1.getMtu(), a2.getMtu());
+        assertEquals(a1.getMvnoType(), a2.getMvnoType());
+        assertEquals(a1.getMvnoMatchData(), a2.getMvnoMatchData());
+        assertEquals(a1.getNetworkTypeBitmask(), a2.getNetworkTypeBitmask());
+        assertEquals(a1.getApnSetId(), a2.getApnSetId());
     }
 
     @Test
     @SmallTest
     public void testFromString() throws Exception {
-        String[] dunTypes = {"DUN"};
-        String[] mmsTypes = {"mms", "*"};
+        final int dunTypesBitmask = ApnSetting.TYPE_DUN;
+        final int mmsTypesBitmask = ApnSetting.TYPE_MMS | ApnSetting.TYPE_ALL;
 
         ApnSetting expectedApn;
         String testString;
 
         // A real-world v1 example string.
         testString = "Vodafone IT,web.omnitel.it,,,,,,,,,222,10,,DUN";
-        expectedApn = new ApnSetting(
-                -1, "22210", "Vodafone IT", "web.omnitel.it", "", "",
-                "", "", "", "", "", 0, dunTypes, "IP", "IP", true, 0, 0,
-                0, false, 0, 0, 0, 0, "", "");
+        expectedApn = ApnSetting.makeApnSetting(
+                -1, "22210", "Vodafone IT", "web.omnitel.it", "", -1, null, "", -1, "", "", 0,
+                dunTypesBitmask, ApnSetting.PROTOCOL_IP, ApnSetting.PROTOCOL_IP, true,
+                0, 0, false, 0, 0, 0, 0, -1, "");
         assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
 
         // A v2 string.
         testString = "[ApnSettingV2] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,14";
-        expectedApn = new ApnSetting(
-                -1, "12345", "Name", "apn", "", "",
-                "", "", "", "", "", 0, mmsTypes, "IPV6", "IP", true, 14, 0,
-                0, false, 0, 0, 0, 0, "", "");
+        int networkTypeBitmask = 1 << (13 - 1);
+        expectedApn = ApnSetting.makeApnSetting(
+                -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+                mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+                networkTypeBitmask, 0, false, 0, 0, 0, 0, -1, "");
         assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
 
         // A v2 string with spaces.
         testString = "[ApnSettingV2] Name,apn, ,,,,,,,,123,45,,mms|*,IPV6, IP,true,14";
-        expectedApn = new ApnSetting(
-                -1, "12345", "Name", "apn", "", "",
-                "", "", "", "", "", 0, mmsTypes, "IPV6", "IP", true, 14, 0,
-                0, false, 0, 0, 0, 0, "", "");
-        assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
-        int networkTypeBitmask = 1 << (13 - 1);
-        expectedApn = new ApnSetting(
-                -1, "12345", "Name", "apn", "", "", "", "", "", "", "", 0, mmsTypes, "IPV6",
-                "IP", true, networkTypeBitmask, 0, false, 0, 0, 0, 0, "", "");
+        expectedApn = ApnSetting.makeApnSetting(
+                -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+                mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+                networkTypeBitmask, 0, false, 0, 0, 0, 0, -1, "");
         assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
 
         // A v3 string.
         testString = "[ApnSettingV3] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,14,,,,,,,spn,testspn";
-        expectedApn = new ApnSetting(
-                -1, "12345", "Name", "apn", "", "", "", "", "", "", "", 0, mmsTypes, "IPV6",
-                "IP", true, 14, 0, 0, false, 0, 0, 0, 0, "spn", "testspn");
+        expectedApn = ApnSetting.makeApnSetting(
+                -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+                mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+                networkTypeBitmask, 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "testspn");
         assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
 
         // A v4 string with network type bitmask.
         testString =
                 "[ApnSettingV4] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,0,,,,,,,spn,testspn,6";
         networkTypeBitmask = 1 << (6 - 1);
-        expectedApn = new ApnSetting(
-                -1, "12345", "Name", "apn", "", "", "", "", "", "", "", 0, mmsTypes, "IPV6",
-                "IP", true, networkTypeBitmask, 0, false, 0, 0, 0, 0, "spn", "testspn");
+        expectedApn = ApnSetting.makeApnSetting(
+                -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+                mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+                networkTypeBitmask, 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "testspn");
         assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
 
         testString =
@@ -200,51 +188,48 @@
                         + "4|5|6|7|8|12|13|14|19";
         // The value was calculated by adding "4|5|6|7|8|12|13|14|19".
         networkTypeBitmask = 276728;
-        expectedApn = new ApnSetting(
-                -1, "12345", "Name", "apn", "", "", "", "", "", "", "", 0, mmsTypes, "IPV6",
-                "IP", true, networkTypeBitmask, 0, false, 0, 0, 0, 0, "spn", "testspn");
+        expectedApn = ApnSetting.makeApnSetting(
+                -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+                mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+                networkTypeBitmask, 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "testspn");
         assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
 
         // A v4 string with network type bitmask and compatible bearer bitmask.
         testString =
                 "[ApnSettingV4] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,8,,,,,,,spn,testspn, 6";
         networkTypeBitmask = 1 << (6 - 1);
-        int bearerBitmask = 1 << (8 - 1);
-        expectedApn = new ApnSetting(
-                -1, "12345", "Name", "apn", "", "", "", "", "", "", "", 0, mmsTypes, "IPV6",
-                "IP", true, 0, bearerBitmask, 0, false, 0, 0, 0, 0, "spn",
-                "testspn");
-        assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
-        expectedApn = new ApnSetting(
-                -1, "12345", "Name", "apn", "", "", "", "", "", "", "", 0, mmsTypes, "IPV6",
-                "IP", true, networkTypeBitmask, 0, false, 0, 0, 0, 0, "spn",
-                "testspn");
+        expectedApn = ApnSetting.makeApnSetting(
+                -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+                mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+                networkTypeBitmask, 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "testspn");
         assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
 
         // A v4 string with network type bitmask and incompatible bearer bitmask.
         testString =
                 "[ApnSettingV4] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,9,,,,,,,spn,testspn, 6";
-        bearerBitmask = 1 << (8 - 1);
-        expectedApn = new ApnSetting(
-                -1, "12345", "Name", "apn", "", "", "", "", "", "", "", 0, mmsTypes, "IPV6",
-                "IP", true, 0, bearerBitmask, 0, false, 0, 0, 0, 0, "spn",
-                "testspn");
+        expectedApn = ApnSetting.makeApnSetting(
+                -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+                mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+                networkTypeBitmask, 0, false, 0,
+                0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "testspn");
         assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
 
         // A v5 string with apnSetId=0
         testString =
                 "[ApnSettingV5] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,0,,,,,,,spn,testspn,0,0";
-        expectedApn = new ApnSetting(
-                -1, "12345", "Name", "apn", "", "", "", "", "", "", "", 0, mmsTypes, "IPV6",
-                "IP", true, 0, 0, 0, false, 0, 0, 0, 0, "spn", "testspn");
+        expectedApn = ApnSetting.makeApnSetting(
+                -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+                mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+                0, 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "testspn");
         assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
 
         // A v5 string with apnSetId=3
         testString =
                 "[ApnSettingV5] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,0,,,,,,,spn,testspn,0,3";
-        expectedApn = new ApnSetting(
-                -1, "12345", "Name", "apn", "", "", "", "", "", "", "", 0, mmsTypes, "IPV6",
-                "IP", true, 0, 0, false, 0, 0, 0, 0, "spn", "testspn", 3);
+        expectedApn = ApnSetting.makeApnSetting(
+                -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+                mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+                0, 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "testspn", 3);
         assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
 
         // Return no apn if insufficient fields given.
@@ -258,6 +243,7 @@
     @Test
     @SmallTest
     public void testArrayFromString() throws Exception {
+        final int mmsTypesBitmask = ApnSetting.TYPE_MMS;
         // Test a multiple v3 string.
         String testString =
                 "[ApnSettingV3] Name,apn,,,,,,,,,123,45,,mms,IPV6,IP,true,14,,,,,,,spn,testspn";
@@ -268,44 +254,48 @@
         testString +=
                 " ;[ApnSettingV5] Name1,apn2,,,,,,,,,123,46,,mms,IPV6,IP,true,0,,,,,,,,,,3";
         List<ApnSetting> expectedApns = new ArrayList<ApnSetting>();
-        expectedApns.add(new ApnSetting(
-                -1, "12345", "Name", "apn", "", "", "", "", "", "", "", 0, new String[]{"mms"}, "IPV6",
-                "IP", true, 14, 0, 0, false, 0, 0, 0, 0, "spn", "testspn"));
-        expectedApns.add(new ApnSetting(
-                -1, "12346", "Name1", "apn1", "", "", "", "", "", "", "", 0, new String[]{"mms"}, "IPV6",
-                "IP", true, 12, 0, 0, false, 0, 0, 0, 0, "gid", "testGid"));
-        expectedApns.add(new ApnSetting(
-                -1, "12346", "Name1", "apn2", "", "", "", "", "", "", "", 0, new String[]{"mms"}, "IPV6",
-                "IP", true, 12, 0, 0, false, 0, 0, 0, 0, "", ""));
-        expectedApns.add(new ApnSetting(
-                -1, "12346", "Name1", "apn2", "", "", "", "", "", "", "", 0, new String[]{"mms"}, "IPV6",
-                "IP", true, 0, 0, false, 0, 0, 0, 0, "", "", 3));
+        expectedApns.add(ApnSetting.makeApnSetting(
+                -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+                mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+                1 << (13 - 1), 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "testspn"));
+        expectedApns.add(ApnSetting.makeApnSetting(
+                -1, "12346", "Name1", "apn1", "", -1, null, "", -1, "", "", 0,
+                mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+                1 << (12 - 1), 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_GID, "testGid"));
+        expectedApns.add(ApnSetting.makeApnSetting(
+                -1, "12346", "Name1", "apn2", "", -1, null, "", -1, "", "", 0,
+                mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+                1 << (12 - 1), 0, false, 0, 0, 0, 0, -1, ""));
+        expectedApns.add(ApnSetting.makeApnSetting(
+                -1, "12346", "Name1", "apn2", "", -1, null, "", -1, "", "", 0,
+                mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+                0, 0, false, 0, 0, 0, 0, -1, "", 3));
         assertApnSettingsEqual(expectedApns, ApnSetting.arrayFromString(testString));
     }
 
     @Test
     @SmallTest
     public void testToString() throws Exception {
-        String[] types = {"default", "*"};
-        // use default apn_set_id constructor
-        ApnSetting apn = new ApnSetting(
-                99, "12345", "Name", "apn", "proxy", "port",
-                "mmsc", "mmsproxy", "mmsport", "user", "password", 0,
-                types, "IPV6", "IP", true, 14, 0, 0, false, 0, 0, 0, 0, "", "");
-        String expected = "[ApnSettingV5] Name, 99, 12345, apn, proxy, "
-                + "mmsc, mmsproxy, mmsport, port, 0, default | *, "
-                + "IPV6, IP, true, 14, 8192, 0, false, 0, 0, 0, 0, , , false, 4096, 0";
+        // Use default apn_set_id constructor.
+        ApnSetting apn = ApnSetting.makeApnSetting(
+                99, "12345", "Name", "apn", null, 10,
+                null, null, -1, "user", "password", 0,
+                ApnSetting.TYPE_DEFAULT, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+                4096, 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "");
+        String expected = "[ApnSettingV5] Name, 99, 12345, apn, null, "
+                + "null, null, null, 10, 0, default, "
+                + "IPV6, IP, true, 0, false, 0, 0, 0, 0, spn, , false, 4096, 0";
         assertEquals(expected, apn.toString());
 
-        int networkTypeBitmask = 1 << (14 - 1);
-        int bearerBitmask =
-                ServiceState.convertNetworkTypeBitmaskToBearerBitmask(networkTypeBitmask);
-        apn = new ApnSetting(99, "12345", "Name", "apn", "proxy", "port",
-                "mmsc", "mmsproxy", "mmsport", "user", "password", 0,
-                types, "IPV6", "IP", true, networkTypeBitmask, 0, false, 0, 0, 0, 0, "", "", 3);
-        expected = "[ApnSettingV5] Name, 99, 12345, apn, proxy, "
-                + "mmsc, mmsproxy, mmsport, port, 0, default | *, IPV6, IP, true, 0, "
-                + bearerBitmask + ", 0, false, 0, 0, 0, 0, , , false, 8192, 3";
+        final int networkTypeBitmask = 1 << (14 - 1);
+        apn = ApnSetting.makeApnSetting(
+                99, "12345", "Name", "apn", null, 10,
+                null, null, -1, "user", "password", 0,
+                ApnSetting.TYPE_DEFAULT, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+                networkTypeBitmask, 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "", 3);
+        expected = "[ApnSettingV5] Name, 99, 12345, apn, null, "
+                + "null, null, null, 10, 0, default, "
+                + "IPV6, IP, true, 0, false, 0, 0, 0, 0, spn, , false, 8192, 3";
         assertEquals(expected, apn.toString());
     }
 
@@ -317,50 +307,43 @@
 
         doReturn(false).when(mServiceState).getDataRoaming();
         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(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_DEFAULT), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_MMS}).isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_MMS, PhoneConstants.APN_TYPE_SUPL}).
-                isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_MMS), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_DUN}).
-                isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_MMS), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_DUN), mPhone));
 
-        assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_SUPL}).
-                isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_ALL), mPhone));
 
-        assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_CBS}).
-                isMetered(mPhone));
+        assertFalse(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_FOTA), mPhone));
 
-        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));
+        assertFalse(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_IA | ApnSetting.TYPE_CBS), mPhone));
+
+        assertTrue(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+        assertTrue(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
+        assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_SUPL, mPhone));
+        assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_CBS, mPhone));
+        assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_DUN, mPhone));
+        assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
+        assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_IA, mPhone));
+        assertFalse(ApnSettingUtils.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, mPhone));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
+        assertTrue(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+        assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
     }
 
     @Test
@@ -371,42 +354,34 @@
         doReturn(true).when(mServiceState).getDataRoaming();
         doReturn(1).when(mPhone).getSubId();
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_DEFAULT}).isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_DEFAULT), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS}).
-                isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_MMS}).isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_MMS), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_MMS, PhoneConstants.APN_TYPE_SUPL}).
-                isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_MMS), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_DUN}).
-                isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_DUN), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_ALL), mPhone));
 
-        assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_SUPL}).
-                isMetered(mPhone));
+        assertFalse(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_FOTA), mPhone));
 
-        assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_CBS}).
-                isMetered(mPhone));
+        assertFalse(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_IA | ApnSetting.TYPE_CBS), 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, mPhone));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
-        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
+        assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+        assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
+        assertTrue(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
     }
 
     @Test
@@ -419,42 +394,34 @@
                 .getRilDataRadioTechnology();
         doReturn(1).when(mPhone).getSubId();
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_DEFAULT}).isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_DEFAULT), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS})
-                .isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_MMS}).isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_MMS), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_MMS, PhoneConstants.APN_TYPE_SUPL})
-                .isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_MMS), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_DUN})
-                .isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_DUN), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_ALL), mPhone));
 
-        assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_SUPL})
-                .isMetered(mPhone));
+        assertFalse(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_FOTA), mPhone));
 
-        assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_CBS})
-                .isMetered(mPhone));
+        assertFalse(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_IA | ApnSetting.TYPE_CBS), 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));
+        assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+        assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
+        assertTrue(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
     }
 
     @Test
@@ -465,33 +432,26 @@
 
         doReturn(false).when(mServiceState).getDataRoaming();
         doReturn(1).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(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_CBS), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_CBS}).isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_SUPL), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS}).
-                isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_CBS), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_IA}).
-                isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_FOTA | ApnSetting.TYPE_CBS), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_IA), mPhone));
 
-        assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_IMS}).
-                isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_ALL), mPhone));
 
-        assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
+        assertFalse(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_IMS), mPhone));
+
+        assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_IMS), mPhone));
     }
 
     @Test
@@ -501,42 +461,35 @@
                 new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_CBS});
         doReturn(true).when(mServiceState).getDataRoaming();
         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(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_CBS), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_CBS}).isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_SUPL), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS}).
-                isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_CBS), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_IA}).
-                isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_FOTA | ApnSetting.TYPE_CBS), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_IA), mPhone));
 
-        assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_IMS}).
-                isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_ALL), mPhone));
 
-        assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
+        assertFalse(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_IMS), 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));
+        assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_IMS), mPhone));
+
+        assertTrue(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_SUPL, mPhone));
+        assertTrue(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_CBS, mPhone));
+        assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+        assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
+        assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_DUN, mPhone));
+        assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
+        assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_IA, mPhone));
+        assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_HIPRI, mPhone));
     }
 
     @Test
@@ -548,42 +501,35 @@
         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(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_CBS), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_CBS}).isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_SUPL), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS})
-                .isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_CBS), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_IA})
-                .isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_FOTA | ApnSetting.TYPE_CBS), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_IA), mPhone));
 
-        assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_IMS})
-                .isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_ALL), mPhone));
 
-        assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
+        assertFalse(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_IMS), 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));
+        assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_IMS), mPhone));
+
+        assertTrue(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_SUPL, mPhone));
+        assertTrue(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_CBS, mPhone));
+        assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+        assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
+        assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_DUN, mPhone));
+        assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
+        assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_IA, mPhone));
+        assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_HIPRI, mPhone));
     }
 
     @Test
@@ -595,19 +541,16 @@
         doReturn(false).when(mServiceState).getDataRoaming();
         doReturn(3).when(mPhone).getSubId();
 
-        assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
+        assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_IMS), mPhone));
 
-        assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_IMS, PhoneConstants.APN_TYPE_MMS})
-                .isMetered(mPhone));
+        assertFalse(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_IMS | ApnSetting.TYPE_MMS), mPhone));
 
-        assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_FOTA})
-                .isMetered(mPhone));
+        assertFalse(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_FOTA), mPhone));
 
-        assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+        assertFalse(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_ALL), mPhone));
     }
 
     @Test
@@ -618,20 +561,16 @@
         doReturn(true).when(mServiceState).getDataRoaming();
         doReturn(3).when(mPhone).getSubId();
 
-        assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
+        assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_IMS), mPhone));
 
-        assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_IMS, PhoneConstants.APN_TYPE_MMS}).
-                isMetered(mPhone));
+        assertFalse(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_IMS | ApnSetting.TYPE_MMS), mPhone));
 
-        assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_FOTA}).
-                isMetered(mPhone));
+        assertFalse(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_FOTA), mPhone));
 
-        assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_ALL}).
-                isMetered(mPhone));
+        assertFalse(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_ALL), mPhone));
     }
 
     @Test
@@ -644,19 +583,16 @@
                 .getRilDataRadioTechnology();
         doReturn(3).when(mPhone).getSubId();
 
-        assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
+        assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_IMS), mPhone));
 
-        assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_IMS, PhoneConstants.APN_TYPE_MMS})
-                .isMetered(mPhone));
+        assertFalse(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_IMS | ApnSetting.TYPE_MMS), mPhone));
 
-        assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_FOTA})
-                .isMetered(mPhone));
+        assertFalse(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_FOTA), mPhone));
 
-        assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+        assertFalse(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_ALL), mPhone));
     }
 
     @Test
@@ -668,22 +604,17 @@
         doReturn(false).when(mServiceState).getDataRoaming();
         doReturn(4).when(mPhone).getSubId();
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_ALL}).
-                isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_ALL), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS}).
-                isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS}).
-                isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_FOTA | ApnSetting.TYPE_CBS), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_DUN}).
-                isMetered(mPhone));
-
+        assertTrue(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_IA | ApnSetting.TYPE_DUN), mPhone));
     }
 
     @Test
@@ -695,20 +626,17 @@
         doReturn(true).when(mServiceState).getDataRoaming();
         doReturn(4).when(mPhone).getSubId();
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_ALL), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS})
-                .isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS})
-                .isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_FOTA | ApnSetting.TYPE_CBS), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_DUN})
-                .isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_IA | ApnSetting.TYPE_DUN), mPhone));
     }
 
     @Test
@@ -722,20 +650,17 @@
                 .getRilDataRadioTechnology();
         doReturn(4).when(mPhone).getSubId();
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_ALL), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS}).
-                isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS}).
-                isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_FOTA | ApnSetting.TYPE_CBS), mPhone));
 
-        assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_DUN}).
-                isMetered(mPhone));
+        assertTrue(ApnSettingUtils.isMetered(
+                createApnSetting(ApnSetting.TYPE_IA | ApnSetting.TYPE_DUN), mPhone));
     }
 
     @Test
@@ -743,59 +668,52 @@
     public void testCanHandleType() throws Exception {
         String types[] = {"mms"};
 
-        // empty string replaced with ALL ('*') when loaded to db
-        assertFalse(createApnSetting(new String[]{}).
-                canHandleType(APN_TYPE_MMS));
+        assertTrue(createApnSetting(ApnSetting.TYPE_ALL)
+                .canHandleType(ApnSetting.TYPE_MMS));
 
-        assertTrue(createApnSetting(new String[]{APN_TYPE_ALL}).
-                canHandleType(APN_TYPE_MMS));
+        assertFalse(createApnSetting(ApnSetting.TYPE_DEFAULT)
+                .canHandleType(ApnSetting.TYPE_MMS));
 
-        assertFalse(createApnSetting(new String[]{APN_TYPE_DEFAULT}).
-                canHandleType(APN_TYPE_MMS));
-
-        assertTrue(createApnSetting(new String[]{"DEfAULT"}).
-                canHandleType("defAult"));
+        assertTrue(createApnSetting(ApnSetting.TYPE_DEFAULT)
+                .canHandleType(ApnSetting.TYPE_DEFAULT));
 
         // Hipri is asymmetric
-        assertTrue(createApnSetting(new String[]{APN_TYPE_DEFAULT}).
-                canHandleType(APN_TYPE_HIPRI));
-        assertFalse(createApnSetting(new String[]{APN_TYPE_HIPRI}).
-                canHandleType(APN_TYPE_DEFAULT));
+        assertTrue(createApnSetting(ApnSetting.TYPE_DEFAULT)
+                .canHandleType(ApnSetting.TYPE_HIPRI));
+        assertFalse(createApnSetting(ApnSetting.TYPE_HIPRI)
+                .canHandleType(ApnSetting.TYPE_DEFAULT));
 
 
-        assertTrue(createApnSetting(new String[]{APN_TYPE_DEFAULT, APN_TYPE_MMS}).
-                canHandleType(APN_TYPE_DEFAULT));
+        assertTrue(createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS)
+                .canHandleType(ApnSetting.TYPE_DEFAULT));
 
-        assertTrue(createApnSetting(new String[]{APN_TYPE_DEFAULT, APN_TYPE_MMS}).
-                canHandleType(APN_TYPE_MMS));
+        assertTrue(createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS)
+                .canHandleType(ApnSetting.TYPE_MMS));
 
-        assertFalse(createApnSetting(new String[]{APN_TYPE_DEFAULT, APN_TYPE_MMS}).
-                canHandleType(APN_TYPE_SUPL));
+        assertFalse(createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS)
+                .canHandleType(ApnSetting.TYPE_SUPL));
 
         // special IA case - doesn't match wildcards
-        assertFalse(createApnSetting(new String[]{APN_TYPE_DEFAULT, APN_TYPE_MMS}).
-                canHandleType(APN_TYPE_IA));
-        assertFalse(createApnSetting(new String[]{APN_TYPE_ALL}).
-                canHandleType(APN_TYPE_IA));
-        assertFalse(createApnSetting(new String[]{APN_TYPE_ALL}).
-                canHandleType("iA"));
-        assertTrue(createApnSetting(new String[]{APN_TYPE_DEFAULT, APN_TYPE_MMS, APN_TYPE_IA}).
-                canHandleType(APN_TYPE_IA));
+        assertFalse(createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS)
+                .canHandleType(ApnSetting.TYPE_IA));
+        assertTrue(createApnSetting(
+                ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS | ApnSetting.TYPE_IA)
+                .canHandleType(ApnSetting.TYPE_IA));
 
         // check carrier disabled
-        assertFalse(createDisabledApnSetting(new String[]{APN_TYPE_ALL}).
-                canHandleType(APN_TYPE_MMS));
-        assertFalse(createDisabledApnSetting(new String[]{"DEfAULT"}).
-                canHandleType("defAult"));
-        assertFalse(createDisabledApnSetting(new String[]{APN_TYPE_DEFAULT}).
-                canHandleType(APN_TYPE_HIPRI));
-        assertFalse(createDisabledApnSetting(new String[]{APN_TYPE_DEFAULT, APN_TYPE_MMS}).
-                canHandleType(APN_TYPE_DEFAULT));
-        assertFalse(createDisabledApnSetting(new String[]{APN_TYPE_DEFAULT, APN_TYPE_MMS}).
-                canHandleType(APN_TYPE_MMS));
-        assertFalse(createDisabledApnSetting(new String[]
-                {APN_TYPE_DEFAULT, APN_TYPE_MMS, APN_TYPE_IA}).
-                canHandleType(APN_TYPE_IA));
+        assertFalse(createDisabledApnSetting(ApnSetting.TYPE_ALL)
+                .canHandleType(ApnSetting.TYPE_MMS));
+        assertFalse(createDisabledApnSetting(ApnSetting.TYPE_DEFAULT)
+                .canHandleType(ApnSetting.TYPE_DEFAULT));
+        assertFalse(createDisabledApnSetting(ApnSetting.TYPE_DEFAULT)
+                .canHandleType(ApnSetting.TYPE_HIPRI));
+        assertFalse(createDisabledApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS)
+                .canHandleType(ApnSetting.TYPE_DEFAULT));
+        assertFalse(createDisabledApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS)
+                .canHandleType(ApnSetting.TYPE_MMS));
+        assertFalse(createDisabledApnSetting(
+                ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS | ApnSetting.TYPE_IA)
+                .canHandleType(ApnSetting.TYPE_IA));
     }
 
     @Test
@@ -804,8 +722,10 @@
         final int dummyInt = 1;
         final String dummyString = "dummy";
         final String[] dummyStringArr = new String[] {"dummy"};
+        final InetAddress dummyProxyAddress = InetAddress.getByAddress(new byte[]{0, 0, 0, 0});
+        final Uri dummyUri = Uri.parse("www.google.com");
         // base apn
-        ApnSetting baseApn = createApnSetting(new String[] {"mms", "default"});
+        ApnSetting baseApn = createApnSetting(ApnSetting.TYPE_MMS | ApnSetting.TYPE_DEFAULT);
         Field[] fields = ApnSetting.class.getDeclaredFields();
         for (Field f : fields) {
             int modifiers = f.getModifiers();
@@ -815,17 +735,23 @@
             f.setAccessible(true);
             ApnSetting testApn = null;
             if (int.class.equals(f.getType())) {
-                testApn = new ApnSetting(baseApn);
+                testApn = ApnSetting.makeApnSetting(baseApn);
                 f.setInt(testApn, dummyInt + f.getInt(testApn));
             } else if (boolean.class.equals(f.getType())) {
-                testApn = new ApnSetting(baseApn);
+                testApn = ApnSetting.makeApnSetting(baseApn);
                 f.setBoolean(testApn, !f.getBoolean(testApn));
             } else if (String.class.equals(f.getType())) {
-                testApn = new ApnSetting(baseApn);
+                testApn = ApnSetting.makeApnSetting(baseApn);
                 f.set(testApn, dummyString);
             } else if (String[].class.equals(f.getType())) {
-                testApn = new ApnSetting(baseApn);
+                testApn = ApnSetting.makeApnSetting(baseApn);
                 f.set(testApn, dummyStringArr);
+            } else if (InetAddress.class.equals(f.getType())) {
+                testApn = ApnSetting.makeApnSetting(baseApn);
+                f.set(testApn, dummyProxyAddress);
+            } else if (Uri.class.equals(f.getType())) {
+                testApn = ApnSetting.makeApnSetting(baseApn);
+                f.set(testApn, dummyUri);
             } else {
                 fail("Unsupported field:" + f.getName());
             }
@@ -838,60 +764,58 @@
     @Test
     @SmallTest
     public void testEqualsRoamingProtocol() throws Exception {
-        ApnSetting apn1 = new ApnSetting(
+        ApnSetting apn1 = ApnSetting.makeApnSetting(
                 1234,
                 "310260",
                 "",
                 "ims",
-                "",
-                "",
-                "",
-                "",
-                "",
+                null,
+                -1,
+                null,
+                null,
+                -1,
                 "",
                 "",
                 -1,
-                 new String[]{"ims"},
-                "IPV6",
-                "",
+                ApnSetting.TYPE_IMS,
+                ApnSetting.PROTOCOL_IPV6,
+                -1,
                 true,
-                0,
-                131071,
+                ServiceState.convertBearerBitmaskToNetworkTypeBitmask(131071),
                 0,
                 false,
                 0,
                 0,
                 0,
                 1440,
-                "",
+                -1,
                 "");
 
-        ApnSetting apn2 = new ApnSetting(
+        ApnSetting apn2 = ApnSetting.makeApnSetting(
                 1235,
                 "310260",
                 "",
                 "ims",
-                "",
-                "",
-                "",
-                "",
-                "",
+                null,
+                -1,
+                null,
+                null,
+                -1,
                 "",
                 "",
                 -1,
-                new String[]{"ims"},
-                "IPV6",
-                "IPV6",
+                ApnSetting.TYPE_IMS,
+                ApnSetting.PROTOCOL_IPV6,
+                ApnSetting.PROTOCOL_IPV6,
                 true,
-                0,
-                131072,
+                ServiceState.convertBearerBitmaskToNetworkTypeBitmask(131072),
                 0,
                 false,
                 0,
                 0,
                 0,
                 1440,
-                "",
+                -1,
                 "");
 
         assertTrue(apn1.equals(apn2, false));
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 f0b2584..ce5eaeb 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
@@ -55,6 +55,7 @@
 import android.telephony.AccessNetworkConstants.TransportType;
 import android.telephony.CarrierConfigManager;
 import android.telephony.ServiceState;
+import android.telephony.data.ApnSetting;
 import android.telephony.data.DataCallResponse;
 import android.telephony.data.DataProfile;
 import android.telephony.data.DataService;
@@ -98,60 +99,58 @@
     private DataConnectionTestHandler mDataConnectionTestHandler;
     private DcController mDcc;
 
-    private ApnSetting mApn1 = new ApnSetting(
+    private ApnSetting mApn1 = ApnSetting.makeApnSetting(
             2163,                   // id
             "44010",                // numeric
             "sp-mode",              // name
             "spmode.ne.jp",         // apn
-            "",                     // proxy
-            "",                     // port
-            "",                     // mmsc
-            "",                     // mmsproxy
-            "",                     // mmsport
+            null,                     // proxy
+            -1,                     // port
+            null,                     // mmsc
+            null,                     // mmsproxy
+            -1,                     // mmsport
             "",                     // user
             "",                     // password
             -1,                     // authtype
-            new String[]{"default", "supl"},     // types
-            "IP",                   // protocol
-            "IP",                   // roaming_protocol
+            ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL,     // types
+            ApnSetting.PROTOCOL_IP,                   // protocol
+            ApnSetting.PROTOCOL_IP,                   // roaming_protocol
             true,                   // carrier_enabled
-            0,                      // bearer
-            0,                      // bearer_bitmask
+            0,                      // networktype_bitmask
             0,                      // profile_id
             false,                  // modem_cognitive
             0,                      // max_conns
             0,                      // wait_time
             0,                      // max_conns_time
             0,                      // mtu
-            "",                     // mvno_type
+            -1,                     // mvno_type
             "");                    // mnvo_match_data
 
-    private ApnSetting mApn2 = new ApnSetting(
+    private ApnSetting mApn2 = ApnSetting.makeApnSetting(
             2164,                   // id
             "44010",                // numeric
             "sp-mode",              // name
             "spmode.ne.jp",         // apn
-            "",                     // proxy
-            "",                     // port
-            "",                     // mmsc
-            "",                     // mmsproxy
-            "",                     // mmsport
+            null,                     // proxy
+            -1,                     // port
+            null,                     // mmsc
+            null,                     // mmsproxy
+            -1,                     // mmsport
             "",                     // user
             "",                     // password
             -1,                     // authtype
-            new String[]{"default", "dun"},     // types
-            "IP",                   // protocol
-            "IP",                   // roaming_protocol
+            ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_DUN,     // types
+            ApnSetting.PROTOCOL_IP,                   // protocol
+            ApnSetting.PROTOCOL_IP,                   // roaming_protocol
             true,                   // carrier_enabled
-            0,                      // bearer
-            0,                      // bearer_bitmask
+            0,                      // networktype_bitmask
             0,                      // profile_id
             false,                  // modem_cognitive
             0,                      // max_conns
             0,                      // wait_time
             0,                      // max_conns_time
             0,                      // mtu
-            "",                     // mvno_type
+            -1,                     // mvno_type
             "");                    // mnvo_match_data
 
     private class DataConnectionTestHandler extends HandlerThread {
@@ -176,7 +175,7 @@
         CellularDataService cellularDataService = new CellularDataService();
         ServiceInfo serviceInfo = new ServiceInfo();
         serviceInfo.packageName = "com.android.phone";
-        serviceInfo.permission = "android.permission.BIND_DATA_SERVICE";
+        serviceInfo.permission = "android.permission.BIND_TELEPHONY_DATA_SERVICE";
         IntentFilter filter = new IntentFilter();
         mContextFixture.addService(
                 DataService.DATA_SERVICE_INTERFACE,
@@ -501,6 +500,7 @@
         assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
     }
 
+    @Test
     @SmallTest
     public void testIsIpAddress() throws Exception {
         // IPv4
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataProfileTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataProfileTest.java
index 5dbe995..97fcc75 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataProfileTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataProfileTest.java
@@ -17,6 +17,7 @@
 package com.android.internal.telephony.dataconnection;
 
 import android.telephony.ServiceState;
+import android.telephony.data.ApnSetting;
 import android.telephony.data.DataProfile;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -26,132 +27,130 @@
 
 public class DataProfileTest extends TestCase {
 
-    private ApnSetting mApn1 = new ApnSetting(
+    private ApnSetting mApn1 = ApnSetting.makeApnSetting(
             2163,                   // id
             "44010",                // numeric
             "sp-mode",              // name
             "fake_apn",             // apn
-            "",                     // proxy
-            "",                     // port
-            "",                     // mmsc
-            "",                     // mmsproxy
-            "",                     // mmsport
+            null,                     // proxy
+            -1,                     // port
+            null,                     // mmsc
+            null,                     // mmsproxy
+            -1,                     // mmsport
             "user",                 // user
             "passwd",               // password
             -1,                     // authtype
-            new String[]{"default", "supl"},     // types
-            "IPV6",                 // protocol
-            "IP",                   // roaming_protocol
+            ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL,     // types
+            ApnSetting.PROTOCOL_IPV6,                 // protocol
+            ApnSetting.PROTOCOL_IP,                   // roaming_protocol
             true,                   // carrier_enabled
-            0,                      // bearer
-            0,                      // bearer_bitmask
+            0,                      // networktype_bitmask
             1234,                   // profile_id
             false,                  // modem_cognitive
             321,                    // max_conns
             456,                    // wait_time
             789,                    // max_conns_time
             0,                      // mtu
-            "",                     // mvno_type
+            -1,                     // mvno_type
             "");                    // mnvo_match_data
 
-    private ApnSetting mApn2 = new ApnSetting(
+    private ApnSetting mApn2 = ApnSetting.makeApnSetting(
             2163,                   // id
             "44010",                // numeric
             "sp-mode",              // name
             "fake_apn",             // apn
-            "",                     // proxy
-            "",                     // port
-            "",                     // mmsc
-            "",                     // mmsproxy
-            "",                     // mmsport
+            null,                     // proxy
+            -1,                     // port
+            null,                     // mmsc
+            null,                     // mmsproxy
+            -1,                     // mmsport
             "user",                 // user
             "passwd",               // password
             -1,                     // authtype
-            new String[]{"default", "supl"},     // types
-            "IP",                   // protocol
-            "IP",                   // roaming_protocol
+            ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL,     // types
+            ApnSetting.PROTOCOL_IP,                 // protocol
+            ApnSetting.PROTOCOL_IP,                   // roaming_protocol
             true,                   // carrier_enabled
-            0,                      // bearer
-            0,                      // bearer_bitmask
+            0,                      // networktype_bitmask
             1234,                   // profile_id
             false,                  // modem_cognitive
             111,                    // max_conns
             456,                    // wait_time
             789,                    // max_conns_time
             0,                      // mtu
-            "",                     // mvno_type
+            -1,                     // mvno_type
             "");                    // mnvo_match_data
 
-    private ApnSetting mApn3 = new ApnSetting(
+    private ApnSetting mApn3 = ApnSetting.makeApnSetting(
             2163,                   // id
             "44010",                // numeric
             "sp-mode",              // name
             "fake_apn",             // apn
-            "",                     // proxy
-            "",                     // port
-            "",                     // mmsc
-            "",                     // mmsproxy
-            "",                     // mmsport
+            null,                     // proxy
+            -1,                     // port
+            null,                     // mmsc
+            null,                     // mmsproxy
+            -1,                     // mmsport
             "user",                 // user
             "passwd",               // password
             -1,                     // authtype
-            new String[]{"default", "supl"},     // types
-            "IP",                   // protocol
-            "IP",                   // roaming_protocol
+            ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL,     // types
+            ApnSetting.PROTOCOL_IP,                 // protocol
+            ApnSetting.PROTOCOL_IP,                   // roaming_protocol
             true,                   // carrier_enabled
-            276600,                    // network_type_bitmask
+            276600,                      // networktype_bitmask
             1234,                   // profile_id
             false,                  // modem_cognitive
             111,                    // max_conns
             456,                    // wait_time
             789,                    // max_conns_time
             0,                      // mtu
-            "",                     // mvno_type
-            ""                   // mnvo_match_data
-            );
+            -1,                     // mvno_type
+            "");                    // mnvo_match_data
 
     @SmallTest
     public void testCreateFromApnSetting() throws Exception {
-        DataProfile dp = DcTracker.createDataProfile(mApn1, mApn1.profileId);
-        assertEquals(mApn1.profileId, dp.getProfileId());
-        assertEquals(mApn1.apn, dp.getApn());
-        assertEquals(mApn1.protocol, dp.getProtocol());
+        DataProfile dp = DcTracker.createDataProfile(mApn1, mApn1.getProfileId());
+        assertEquals(mApn1.getProfileId(), dp.getProfileId());
+        assertEquals(mApn1.getApnName(), dp.getApn());
+        assertEquals(ApnSetting.getProtocolStringFromInt(mApn1.getProtocol()), dp.getProtocol());
         assertEquals(RILConstants.SETUP_DATA_AUTH_PAP_CHAP, dp.getAuthType());
-        assertEquals(mApn1.user, dp.getUserName());
-        assertEquals(mApn1.password, dp.getPassword());
+        assertEquals(mApn1.getUser(), dp.getUserName());
+        assertEquals(mApn1.getPassword(), dp.getPassword());
         assertEquals(0, dp.getType());  // TYPE_COMMON
-        assertEquals(mApn1.maxConnsTime, dp.getMaxConnsTime());
-        assertEquals(mApn1.maxConns, dp.getMaxConns());
-        assertEquals(mApn1.waitTime, dp.getWaitTime());
-        assertEquals(mApn1.carrierEnabled, dp.isEnabled());
+        assertEquals(mApn1.getMaxConnsTime(), dp.getMaxConnsTime());
+        assertEquals(mApn1.getMaxConns(), dp.getMaxConns());
+        assertEquals(mApn1.getWaitTime(), dp.getWaitTime());
+        assertEquals(mApn1.isEnabled(), dp.isEnabled());
     }
 
     @SmallTest
     public void testCreateFromApnSettingWithNetworkTypeBitmask() throws Exception {
-        DataProfile dp = DcTracker.createDataProfile(mApn3, mApn3.profileId);
-        assertEquals(mApn3.profileId, dp.getProfileId());
-        assertEquals(mApn3.apn, dp.getApn());
-        assertEquals(mApn3.protocol, dp.getProtocol());
+        DataProfile dp = DcTracker.createDataProfile(mApn3, mApn3.getProfileId());
+        assertEquals(mApn3.getProfileId(), dp.getProfileId());
+        assertEquals(mApn3.getApnName(), dp.getApn());
+        assertEquals(ApnSetting.getProtocolStringFromInt(mApn3.getProtocol()), dp.getProtocol());
         assertEquals(RILConstants.SETUP_DATA_AUTH_PAP_CHAP, dp.getAuthType());
-        assertEquals(mApn3.user, dp.getUserName());
-        assertEquals(mApn3.password, dp.getPassword());
+        assertEquals(mApn3.getUser(), dp.getUserName());
+        assertEquals(mApn3.getPassword(), dp.getPassword());
         assertEquals(2, dp.getType());  // TYPE_3GPP2
-        assertEquals(mApn3.maxConnsTime, dp.getMaxConnsTime());
-        assertEquals(mApn3.maxConns, dp.getMaxConns());
-        assertEquals(mApn3.waitTime, dp.getWaitTime());
-        assertEquals(mApn3.carrierEnabled, dp.isEnabled());
+        assertEquals(mApn3.getMaxConnsTime(), dp.getMaxConnsTime());
+        assertEquals(mApn3.getMaxConns(), dp.getMaxConns());
+        assertEquals(mApn3.getWaitTime(), dp.getWaitTime());
+        assertEquals(mApn3.isEnabled(), dp.isEnabled());
         int expectedBearerBitmap =
-                ServiceState.convertNetworkTypeBitmaskToBearerBitmask(mApn3.networkTypeBitmask);
+                ServiceState.convertNetworkTypeBitmaskToBearerBitmask(
+                    mApn3.getNetworkTypeBitmask());
         assertEquals(expectedBearerBitmap, dp.getBearerBitmap());
     }
 
     @SmallTest
     public void testEquals() throws Exception {
-        DataProfile dp1 = DcTracker.createDataProfile(mApn1, mApn1.profileId);
-        DataProfile dp2 = DcTracker.createDataProfile(mApn1, mApn1.profileId);
+        DataProfile dp1 = DcTracker.createDataProfile(mApn1, mApn1.getProfileId());
+        DataProfile dp2 = DcTracker.createDataProfile(mApn1, mApn1.getProfileId());
         assertEquals(dp1, dp2);
 
-        dp2 = DcTracker.createDataProfile(mApn2, mApn2.profileId);
+        dp2 = DcTracker.createDataProfile(mApn2, mApn2.getProfileId());
         assertFalse(dp1.equals(dp2));
     }
 }
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 17f026f..451571e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
@@ -64,12 +64,14 @@
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
 import android.telephony.data.DataProfile;
 import android.telephony.data.DataService;
 import android.test.mock.MockContentProvider;
 import android.test.mock.MockContentResolver;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.text.TextUtils;
 import android.util.LocalLog;
 
 import com.android.internal.R;
@@ -126,7 +128,6 @@
     private static final Uri PREFERAPN_URI = Uri.parse(
             Telephony.Carriers.CONTENT_URI + "/preferapn");
 
-
     @Mock
     ISub mIsub;
     @Mock
@@ -158,7 +159,7 @@
         CellularDataService cellularDataService = new CellularDataService();
         ServiceInfo serviceInfo = new ServiceInfo();
         serviceInfo.packageName = "com.android.phone";
-        serviceInfo.permission = "android.permission.BIND_DATA_SERVICE";
+        serviceInfo.permission = "android.permission.BIND_TELEPHONY_DATA_SERVICE";
         IntentFilter filter = new IntentFilter();
         mContextFixture.addService(
                 DataService.DATA_SERVICE_INTERFACE,
@@ -604,7 +605,7 @@
 
         logd("Sending EVENT_ENABLE_NEW_APN");
         // APN id 0 is APN_TYPE_DEFAULT
-        mDct.setEnabled(DctConstants.APN_DEFAULT_ID, true);
+        mDct.setEnabled(ApnSetting.TYPE_DEFAULT, true);
         waitForMs(200);
 
         dataConnectionReasons = new DataConnectionReasons();
@@ -678,7 +679,7 @@
 
         logd("Sending EVENT_ENABLE_NEW_APN");
         // APN id 0 is APN_TYPE_DEFAULT
-        mDct.setEnabled(DctConstants.APN_DEFAULT_ID, true);
+        mDct.setEnabled(ApnSetting.TYPE_DEFAULT, true);
         waitForMs(200);
 
 
@@ -737,8 +738,8 @@
         boolean dataEnabled = mDct.isUserDataEnabled();
         mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
-        mDct.setEnabled(DctConstants.APN_IMS_ID, true);
-        mDct.setEnabled(DctConstants.APN_DEFAULT_ID, true);
+        mDct.setEnabled(ApnSetting.TYPE_IMS, true);
+        mDct.setEnabled(ApnSetting.TYPE_DEFAULT, true);
 
         logd("Sending EVENT_RECORDS_LOADED");
         mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
@@ -792,8 +793,8 @@
         mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
 
-        mDct.setEnabled(DctConstants.APN_IMS_ID, true);
-        mDct.setEnabled(DctConstants.APN_DEFAULT_ID, true);
+        mDct.setEnabled(ApnSetting.TYPE_IMS, true);
+        mDct.setEnabled(ApnSetting.TYPE_DEFAULT, true);
 
         logd("Sending EVENT_RECORDS_LOADED");
         mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
@@ -850,8 +851,8 @@
         //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(DctConstants.APN_IMS_ID, true);
-        mDct.setEnabled(DctConstants.APN_DEFAULT_ID, true);
+        mDct.setEnabled(ApnSetting.TYPE_IMS, true);
+        mDct.setEnabled(ApnSetting.TYPE_DEFAULT, true);
 
         logd("Sending DATA_ENABLED_CMD");
         mDct.setUserDataEnabled(true);
@@ -965,8 +966,8 @@
         boolean dataEnabled = mDct.isUserDataEnabled();
         mDct.setUserDataEnabled(true);
 
-        mDct.setEnabled(DctConstants.APN_IMS_ID, true);
-        mDct.setEnabled(DctConstants.APN_DEFAULT_ID, true);
+        mDct.setEnabled(ApnSetting.TYPE_IMS, true);
+        mDct.setEnabled(ApnSetting.TYPE_DEFAULT, true);
 
         logd("Sending EVENT_RECORDS_LOADED");
         mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
@@ -1006,7 +1007,8 @@
     private void initApns(String targetApn, String[] canHandleTypes) {
         doReturn(targetApn).when(mApnContext).getApnType();
         doReturn(true).when(mApnContext).isConnectable();
-        ApnSetting apnSetting = createApnSetting(canHandleTypes);
+        ApnSetting apnSetting = createApnSetting(ApnSetting.getApnTypesBitmaskFromString(
+                TextUtils.join(",", canHandleTypes)));
         doReturn(apnSetting).when(mApnContext).getNextApnSetting();
         doReturn(apnSetting).when(mApnContext).getApnSetting();
         doReturn(mDcac).when(mApnContext).getDcAc();
@@ -1261,7 +1263,7 @@
                 .getRilDataRadioTechnology();
         mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT});
-        mDct.setEnabled(DctConstants.APN_DEFAULT_ID, true);
+        mDct.setEnabled(ApnSetting.TYPE_DEFAULT, true);
         mDct.setUserDataEnabled(true);
         initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
 
@@ -1338,7 +1340,7 @@
                 Settings.Global.TETHER_DUN_APN, null);
         // should return APN from db
         dunApn = mDct.fetchDunApns().get(0);
-        assertEquals(FAKE_APN5, dunApn.apn);
+        assertEquals(FAKE_APN5, dunApn.getApnName());
     }
 
     // Test for fetchDunApns() with apn set id
@@ -1381,7 +1383,7 @@
                 .getRilDataRadioTechnology();
         mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT});
-        mDct.setEnabled(DctConstants.APN_DEFAULT_ID, true);
+        mDct.setEnabled(ApnSetting.TYPE_DEFAULT, true);
         mDct.setUserDataEnabled(true);
         initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/RetryManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/RetryManagerTest.java
index 6b00289..2f452f6 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/RetryManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/RetryManagerTest.java
@@ -16,8 +16,12 @@
 
 package com.android.internal.telephony.dataconnection;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
+import android.telephony.data.ApnSetting;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.internal.telephony.RetryManager;
@@ -31,97 +35,91 @@
 
 import java.util.ArrayList;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
 /**
  * APN retry manager tests
  */
 public class RetryManagerTest extends TelephonyTest {
 
     // This is the real APN data for the Japanese carrier NTT Docomo.
-    private ApnSetting mApn1 = new ApnSetting(
+    private ApnSetting mApn1 = ApnSetting.makeApnSetting(
             2163,                   // id
             "44010",                // numeric
             "sp-mode",              // name
             "spmode.ne.jp",         // apn
-            "",                     // proxy
-            "",                     // port
-            "",                     // mmsc
-            "",                     // mmsproxy
-            "",                     // mmsport
+            null,                     // proxy
+            -1,                     // port
+            null,                     // mmsc
+            null,                     // mmsproxy
+            -1,                     // mmsport
             "",                     // user
             "",                     // password
             -1,                     // authtype
-            new String[]{"default", "supl"},     // types
-            "IP",                   // protocol
-            "IP",                   // roaming_protocol
+            ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL, // types
+            ApnSetting.PROTOCOL_IP, // protocol
+            ApnSetting.PROTOCOL_IP, // roaming_protocol
             true,                   // carrier_enabled
-            0,                      // bearer
-            0,                      // bearer_bitmask
+            0,                      // networktype_bitmask
             0,                      // profile_id
             false,                  // modem_cognitive
             0,                      // max_conns
             0,                      // wait_time
             0,                      // max_conns_time
             0,                      // mtu
-            "",                     // mvno_type
+            -1,                     // mvno_type
             "");                    // mnvo_match_data
 
-    private ApnSetting mApn2 = new ApnSetting(
+    private ApnSetting mApn2 = ApnSetting.makeApnSetting(
             2164,                   // id
             "44010",                // numeric
             "mopera U",             // name
             "mopera.net",           // apn
-            "",                     // proxy
-            "",                     // port
-            "",                     // mmsc
-            "",                     // mmsproxy
-            "",                     // mmsport
+            null,                     // proxy
+            -1,                     // port
+            null,                     // mmsc
+            null,                     // mmsproxy
+            -1,                     // mmsport
             "",                     // user
             "",                     // password
             -1,                     // authtype
-            new String[]{"default", "supl"},     // types
-            "IP",                   // protocol
-            "IP",                   // roaming_protocol
+            ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL,     // types
+            ApnSetting.PROTOCOL_IP,                   // protocol
+            ApnSetting.PROTOCOL_IP,                   // roaming_protocol
             true,                   // carrier_enabled
-            0,                      // bearer
-            0,                      // bearer_bitmask
+            0,                      // networktype_bitmask
             0,                      // profile_id
             false,                  // modem_cognitive
             0,                      // max_conns
             0,                      // wait_time
             0,                      // max_conns_time
             0,                      // mtu
-            "",                     // mvno_type
+            -1,                     // mvno_type
             "");                    // mnvo_match_data
 
-    private ApnSetting mApn3 = new ApnSetting(
+    private ApnSetting mApn3 = ApnSetting.makeApnSetting(
             2165,                   // id
             "44010",                // numeric
             "b-mobile for Nexus",   // name
             "bmobile.ne.jp",        // apn
-            "",                     // proxy
-            "",                     // port
-            "",                     // mmsc
-            "",                     // mmsproxy
-            "",                     // mmsport
+            null,                     // proxy
+            -1,                     // port
+            null,                     // mmsc
+            null,                     // mmsproxy
+            -1,                     // mmsport
             "",                     // user
             "",                     // password
             3,                      // authtype
-            new String[]{"default", "supl"},     // types
-            "IP",                   // protocol
-            "IP",                   // roaming_protocol
+            ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL,     // types
+            ApnSetting.PROTOCOL_IP,                   // protocol
+            ApnSetting.PROTOCOL_IP,                   // roaming_protocol
             true,                   // carrier_enabled
-            0,                      // bearer
-            0,                      // bearer_bitmask
+            0,                      // networktype_bitmask
             0,                      // profile_id
             false,                  // modem_cognitive
             0,                      // max_conns
             0,                      // wait_time
             0,                      // max_conns_time
             0,                      // mtu
-            "",                     // mvno_type
+            -1,                     // mvno_type
             "");                    // mnvo_match_data
 
     private PersistableBundle mBundle;
@@ -173,7 +171,7 @@
                 new String[]{"default:"});
 
         ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        waitingApns.add(new ApnSetting(mApn1));
+        waitingApns.add(ApnSetting.makeApnSetting(mApn1));
 
         RetryManager rm = new RetryManager(mPhone, "default");
         rm.setWaitingApns(waitingApns);
@@ -195,7 +193,7 @@
                 new String[]{"supl:2000,3000"});
 
         ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        waitingApns.add(new ApnSetting(mApn1));
+        waitingApns.add(ApnSetting.makeApnSetting(mApn1));
 
         RetryManager rm = new RetryManager(mPhone, "supl");
         rm.setWaitingApns(waitingApns);
@@ -249,8 +247,8 @@
                 new String[]{"others:2000"});
 
         ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        waitingApns.add(new ApnSetting(mApn1));
-        waitingApns.add(new ApnSetting(mApn2));
+        waitingApns.add(ApnSetting.makeApnSetting(mApn1));
+        waitingApns.add(ApnSetting.makeApnSetting(mApn2));
 
         RetryManager rm = new RetryManager(mPhone, "default");
         rm.setWaitingApns(waitingApns);
@@ -287,8 +285,8 @@
                 new String[]{"dun:2000,5000"});
 
         ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        waitingApns.add(new ApnSetting(mApn1));
-        waitingApns.add(new ApnSetting(mApn2));
+        waitingApns.add(ApnSetting.makeApnSetting(mApn1));
+        waitingApns.add(ApnSetting.makeApnSetting(mApn2));
 
         RetryManager rm = new RetryManager(mPhone, "dun");
         rm.setWaitingApns(waitingApns);
@@ -335,8 +333,8 @@
                 new String[]{"mms:      3000,6000"});
 
         ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        waitingApns.add(new ApnSetting(mApn1));
-        waitingApns.add(new ApnSetting(mApn2));
+        waitingApns.add(ApnSetting.makeApnSetting(mApn1));
+        waitingApns.add(ApnSetting.makeApnSetting(mApn2));
 
         RetryManager rm = new RetryManager(mPhone, "mms");
         rm.setWaitingApns(waitingApns);
@@ -383,7 +381,7 @@
                 new String[]{"fota:1000,4000,7000"});
 
         ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        ApnSetting apn = new ApnSetting(mApn1);
+        ApnSetting apn = ApnSetting.makeApnSetting(mApn1);
         waitingApns.add(apn);
 
         RetryManager rm = new RetryManager(mPhone, "fota");
@@ -416,8 +414,8 @@
                 new String[]{"xyz  :   1000,4000,7000"});
 
         ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        ApnSetting myApn1 = new ApnSetting(mApn1);
-        ApnSetting myApn2 = new ApnSetting(mApn2);
+        ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
+        ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
         waitingApns.add(myApn1);
         waitingApns.add(myApn2);
 
@@ -473,9 +471,9 @@
                 new String[]{"default:2000:2000,3000:3000", "ims:1000,4000"});
 
         ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        ApnSetting myApn1 = new ApnSetting(mApn1);
-        ApnSetting myApn2 = new ApnSetting(mApn2);
-        ApnSetting myApn3 = new ApnSetting(mApn3);
+        ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
+        ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
+        ApnSetting myApn3 = ApnSetting.makeApnSetting(mApn3);
         waitingApns.add(myApn1);
         waitingApns.add(myApn2);
         waitingApns.add(myApn3);
@@ -532,8 +530,8 @@
                 new String[]{"default:1000,4000,7000,9000", "mms:1234,4123"});
 
         ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        ApnSetting myApn1 = new ApnSetting(mApn1);
-        ApnSetting myApn2 = new ApnSetting(mApn2);
+        ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
+        ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
         waitingApns.add(myApn1);
         waitingApns.add(myApn2);
 
@@ -587,7 +585,7 @@
                 new String[]{"default:default_randomization=1000,3000:2000,6000:3000,10000"});
 
         ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        waitingApns.add(new ApnSetting(mApn1));
+        waitingApns.add(ApnSetting.makeApnSetting(mApn1));
 
         RetryManager rm = new RetryManager(mPhone, "default");
         rm.setWaitingApns(waitingApns);
@@ -624,8 +622,8 @@
                 new String[]{"default:max_retries=infinite,1000,2000"});
 
         ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        waitingApns.add(new ApnSetting(mApn1));
-        waitingApns.add(new ApnSetting(mApn2));
+        waitingApns.add(ApnSetting.makeApnSetting(mApn1));
+        waitingApns.add(ApnSetting.makeApnSetting(mApn2));
 
         RetryManager rm = new RetryManager(mPhone, "default");
         rm.setWaitingApns(waitingApns);
@@ -682,8 +680,8 @@
                 new String[]{"hipri:  max_retries=4,1000,2000"});
 
         ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        waitingApns.add(new ApnSetting(mApn1));
-        waitingApns.add(new ApnSetting(mApn2));
+        waitingApns.add(ApnSetting.makeApnSetting(mApn1));
+        waitingApns.add(ApnSetting.makeApnSetting(mApn2));
 
         RetryManager rm = new RetryManager(mPhone, "hipri");
         rm.setWaitingApns(waitingApns);
@@ -752,8 +750,8 @@
         mBundle.putLong(CarrierConfigManager.KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG, 2000);
 
         ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        waitingApns.add(new ApnSetting(mApn1));
-        waitingApns.add(new ApnSetting(mApn2));
+        waitingApns.add(ApnSetting.makeApnSetting(mApn1));
+        waitingApns.add(ApnSetting.makeApnSetting(mApn2));
 
         RetryManager rm = new RetryManager(mPhone, "default");
         rm.setWaitingApns(waitingApns);
@@ -800,8 +798,8 @@
                 new String[]{"dun:1000,4000,7000,9000"});
 
         ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        ApnSetting myApn1 = new ApnSetting(mApn1);
-        ApnSetting myApn2 = new ApnSetting(mApn2);
+        ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
+        ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
         waitingApns.add(myApn1);
         waitingApns.add(myApn2);
 
@@ -845,7 +843,7 @@
 
         // reset the retry manager
 
-        ApnSetting myApn3 = new ApnSetting(mApn3);
+        ApnSetting myApn3 = ApnSetting.makeApnSetting(mApn3);
         waitingApns.clear();
         waitingApns.add(myApn3);
 
@@ -881,8 +879,8 @@
                 new String[]{"others:1000,4000,7000,9000"});
 
         ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        ApnSetting myApn1 = new ApnSetting(mApn1);
-        ApnSetting myApn2 = new ApnSetting(mApn2);
+        ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
+        ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
         waitingApns.add(myApn1);
         waitingApns.add(myApn2);
 
@@ -937,8 +935,8 @@
                 new String[]{"default:1000,4000,7000,9000"});
 
         ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        ApnSetting myApn1 = new ApnSetting(mApn1);
-        ApnSetting myApn2 = new ApnSetting(mApn2);
+        ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
+        ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
         waitingApns.add(myApn1);
         waitingApns.add(myApn2);
 
@@ -980,8 +978,8 @@
                 new String[]{"mms:2000,3000", "default:1000,4000,7000,9000"});
 
         ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        ApnSetting myApn1 = new ApnSetting(mApn1);
-        ApnSetting myApn2 = new ApnSetting(mApn2);
+        ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
+        ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
         waitingApns.add(myApn1);
         waitingApns.add(myApn2);
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsManagerTest.java
index f217c7d..fd19f80 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsManagerTest.java
@@ -22,6 +22,7 @@
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -35,6 +36,7 @@
 
 import com.android.ims.ImsConfig;
 import com.android.ims.ImsManager;
+import com.android.ims.MmTelFeatureConnection;
 import com.android.internal.telephony.TelephonyTest;
 
 import org.junit.After;
@@ -47,13 +49,16 @@
 public class ImsManagerTest extends TelephonyTest {
     private static final String UNSET_PROVISIONED_STRING = "unset";
     private static final boolean ENHANCED_4G_MODE_DEFAULT_VAL = true;
-    private static final boolean ENHANCED_4G_ENABLE_DEFAULT_VAL = true;
     private static final boolean ENHANCED_4G_MODE_EDITABLE = true;
     private static final boolean WFC_IMS_ENABLE_DEFAULT_VAL = false;
     private static final boolean WFC_IMS_ROAMING_ENABLE_DEFAULT_VAL = true;
     private static final boolean VT_IMS_ENABLE_DEFAULT_VAL = true;
-    private static final int WFC_IMS_MODE_DEFAULT_VAL = 2;
-    private static final int WFC_IMS_ROAMING_MODE_DEFAULT_VAL = 3;
+    private static final boolean WFC_IMS_EDITABLE_VAL = true;
+    private static final boolean WFC_IMS_NOT_EDITABLE_VAL = false;
+    private static final int WFC_IMS_MODE_DEFAULT_VAL =
+            ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED;
+    private static final int WFC_IMS_ROAMING_MODE_DEFAULT_VAL =
+            ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED;
 
     PersistableBundle mBundle;
 
@@ -64,7 +69,7 @@
     Hashtable<Integer, Integer> mProvisionedIntVals = new Hashtable<>();
     Hashtable<Integer, String> mProvisionedStringVals = new Hashtable<>();
     ImsConfigImplBase.ImsConfigStub mImsConfigStub;
-    ImsConfig mImsConfig;
+    @Mock MmTelFeatureConnection mMmTelFeatureConnection;
 
     private final int[] mSubId = {0};
     private int mPhoneId;
@@ -80,6 +85,8 @@
         doReturn(mSubscriptionController).when(mBinder).queryLocalInterface(anyString());
         mServiceManagerMockedServices.put("isub", mBinder);
 
+        doReturn(true).when(mMmTelFeatureConnection).isBinderAlive();
+
         mImsManagerInstances.remove(mPhoneId);
 
         setDefaultValues();
@@ -92,7 +99,9 @@
 
     private void setDefaultValues() {
         mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL,
-                ENHANCED_4G_ENABLE_DEFAULT_VAL);
+                ENHANCED_4G_MODE_EDITABLE);
+        mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL,
+                WFC_IMS_EDITABLE_VAL);
         mBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL,
                 WFC_IMS_ENABLE_DEFAULT_VAL);
         mBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL,
@@ -119,7 +128,7 @@
                 eq(SubscriptionManager.WFC_IMS_ENABLED),
                 anyString());
 
-        assertEquals(ENHANCED_4G_ENABLE_DEFAULT_VAL,
+        assertEquals(ENHANCED_4G_MODE_DEFAULT_VAL,
                 imsManager.isEnhanced4gLteModeSettingEnabledByUser());
         verify(mSubscriptionController, times(1)).getSubscriptionProperty(
                 anyInt(),
@@ -239,7 +248,126 @@
 
     }
 
-    private ImsManager initializeProvisionedValues() {
+    /**
+     * Tests that when a WFC mode is set for home/roaming, that setting is sent to the ImsService
+     * correctly.
+     *
+     * Preconditions:
+     *  - CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL = true
+     */
+    @Test @SmallTest
+    public void testSetWfcSetting_true_shouldSetWfcModeWrtRoamingState() throws Exception {
+        // First, Set WFC home/roaming mode that is not the Carrier Config default.
+        doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED))
+                .when(mSubscriptionController).getSubscriptionProperty(
+                        anyInt(),
+                        eq(SubscriptionManager.WFC_IMS_MODE),
+                        anyString());
+        doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED))
+                .when(mSubscriptionController).getSubscriptionProperty(
+                        anyInt(),
+                        eq(SubscriptionManager.WFC_IMS_ROAMING_MODE),
+                        anyString());
+        ImsManager imsManager = initializeProvisionedValues();
+
+        // Roaming
+        doReturn(true).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
+        // Turn on WFC
+        imsManager.setWfcSetting(true);
+        // Roaming mode (CELLULAR_PREFERRED) should be set. With 1000 ms timeout.
+        verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
+                eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
+                eq(ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED));
+
+        // Not roaming
+        doReturn(false).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
+        // Turn on WFC
+        imsManager.setWfcSetting(true);
+        // Home mode (WIFI_PREFERRED) should be set. With 1000 ms timeout.
+        verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
+                eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
+                eq(ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED));
+    }
+
+    /**
+     * Tests that the settings for WFC mode are ignored if the Carrier sets the settings to not
+     * editable.
+     *
+     * Preconditions:
+     *  - CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL = false
+     */
+    @Test @SmallTest
+    public void testSetWfcSetting_wfcNotEditable() throws Exception {
+        mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL,
+                WFC_IMS_NOT_EDITABLE_VAL);
+        // Set some values that are different than the defaults for WFC mode.
+        doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY))
+                .when(mSubscriptionController).getSubscriptionProperty(
+                anyInt(),
+                eq(SubscriptionManager.WFC_IMS_MODE),
+                anyString());
+        doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY))
+                .when(mSubscriptionController).getSubscriptionProperty(
+                anyInt(),
+                eq(SubscriptionManager.WFC_IMS_ROAMING_MODE),
+                anyString());
+        ImsManager imsManager = initializeProvisionedValues();
+
+        // Roaming
+        doReturn(true).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
+        // Turn on WFC
+        imsManager.setWfcSetting(true);
+        // User defined setting for Roaming mode (WIFI_ONLY) should be set independent of whether or
+        // not WFC mode is editable. With 1000 ms timeout.
+        verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
+                eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
+                eq(ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY));
+
+        // Not roaming
+        doReturn(false).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
+        // Turn on WFC
+        imsManager.setWfcSetting(true);
+        // Default Home mode (CELLULAR_PREFERRED) should be set. With 1000 ms timeout.
+        verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
+                eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
+                eq(WFC_IMS_MODE_DEFAULT_VAL));
+    }
+
+    /**
+     * Tests that the CarrierConfig defaults will be used if no setting is set in the Subscription
+     * Manager.
+     *
+     * Preconditions:
+     *  - CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL = true
+     *  - CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT = Carrier preferred
+     *  - CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT = WiFi preferred
+     */
+    @Test @SmallTest
+    public void testSetWfcSetting_noUserSettingSet() throws Exception {
+        ImsManager imsManager = initializeProvisionedValues();
+
+        // Roaming
+        doReturn(true).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
+        // Turn on WFC
+        imsManager.setWfcSetting(true);
+
+        // Default Roaming mode (WIFI_PREFERRED) for carrier should be set. With 1000 ms timeout.
+        verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
+                eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
+                eq(WFC_IMS_ROAMING_MODE_DEFAULT_VAL));
+
+        // Not roaming
+        doReturn(false).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
+        // Turn on WFC
+        imsManager.setWfcSetting(true);
+
+        // Default Home mode (CELLULAR_PREFERRED) for carrier should be set. With 1000 ms timeout.
+        verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
+                eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
+                eq(WFC_IMS_MODE_DEFAULT_VAL));
+    }
+
+    private ImsManager initializeProvisionedValues() throws Exception {
         when(mImsConfigImplBaseMock.getConfigInt(anyInt()))
                 .thenAnswer(invocation ->  {
                     return getProvisionedInt((Integer) (invocation.getArguments()[0]));
@@ -255,15 +383,13 @@
 
         // Configure ImsConfigStub
         mImsConfigStub = new ImsConfigImplBase.ImsConfigStub(mImsConfigImplBaseMock);
-        doReturn(mImsConfigStub).when(mImsConfigImplBaseMock).getIImsConfig();
-
-        // Configure ImsConfig
-        mImsConfig = new ImsConfig(mImsConfigStub, mContext);
+        doReturn(mImsConfigStub).when(mMmTelFeatureConnection).getConfigInterface();
 
         // Configure ImsManager
         ImsManager imsManager = ImsManager.getInstance(mContext, mPhoneId);
         try {
-            replaceInstance(ImsManager.class, "mConfig", imsManager, mImsConfig);
+            replaceInstance(ImsManager.class, "mMmTelFeatureConnection", imsManager,
+                    mMmTelFeatureConnection);
         } catch (Exception ex) {
             fail("failed with " + ex);
         }
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 a09c4eb..c02f68b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/DcTrackerMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/DcTrackerMock.java
@@ -53,14 +53,6 @@
         throw new RuntimeException("Not Implemented");
     }
     @Override
-    public boolean isApnSupported(String name) {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
-    public int getApnPriority(String name) {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
     public LinkProperties getLinkProperties(String apnType) {
         throw new RuntimeException("Not Implemented");
     }
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 dc21b70..27b8531 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java
@@ -63,7 +63,8 @@
         @Override
         public void onLooperPrepared() {
             mUicccard = new UiccCard(mContextFixture.getTestDouble(),
-                                     mSimulatedCommands, mIccCardStatus, 0 /* phoneId */);
+                                     mSimulatedCommands, mIccCardStatus, 0 /* phoneId */,
+                                     new Object());
             /* create a custom handler for the Handler Thread */
             mHandler = new Handler(mTestHandlerThread.getLooper()) {
                 @Override
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java
index 1e12729..01220d5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java
@@ -82,7 +82,7 @@
         public void onLooperPrepared() {
             mUiccProfile = new UiccProfile(mContextFixture.getTestDouble(),
                                            mSimulatedCommands, mIccCardStatus, 0 /* phoneId */,
-                                           mUiccCard);
+                                           mUiccCard, new Object());
             /* create a custom handler for the Handler Thread */
             mHandler = new Handler(mTestHandlerThread.getLooper()) {
                 @Override
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java
index e3ac6a8..457f021 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java
@@ -251,7 +251,8 @@
         mIccCardStatus.mCardState = IccCardStatus.CardState.CARDSTATE_PRESENT;
         mUiccSlot.update(mSimulatedCommands, mIccCardStatus, phoneId);
         verify(mTelephonyComponentFactory).makeUiccProfile(
-                anyObject(), eq(mSimulatedCommands), eq(mIccCardStatus), anyInt(), anyObject());
+                anyObject(), eq(mSimulatedCommands), eq(mIccCardStatus), anyInt(), anyObject(),
+                anyObject());
         assertEquals(IccCardStatus.CardState.CARDSTATE_PRESENT, mUiccSlot.getCardState());
         assertNotNull(mUiccSlot.getUiccCard());
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
index a90e947..f933596 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
@@ -101,7 +101,7 @@
 
         // The first broadcast should be sent after initialization.
         UiccCard card = new UiccCard(mContext, mSimulatedCommands,
-                makeCardStatus(CardState.CARDSTATE_PRESENT), 0 /* phoneId */);
+                makeCardStatus(CardState.CARDSTATE_PRESENT), 0 /* phoneId */, new Object());
         when(UiccController.getInstance().getUiccCardForPhone(0)).thenReturn(card);
         uiccLauncher.handleMessage(msg);
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java
index 831d0b8..dc621a5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java
@@ -103,7 +103,7 @@
         public void onLooperPrepared() {
             mEuiccCard =
                     new EuiccCard(mContextFixture.getTestDouble(), mMockCi, mMockIccCardStatus,
-                            0 /* phoneId */) {
+                            0 /* phoneId */, new Object()) {
                         @Override
                         protected byte[] getDeviceId() {
                             return IccUtils.bcdToBytes("987654321012345");
@@ -173,7 +173,7 @@
             final CountDownLatch latch = new CountDownLatch(1);
             mHandler.post(() -> {
                 mEuiccCard = new EuiccCard(mContextFixture.getTestDouble(), mMockCi,
-                        mMockIccCardStatus, 0 /* phoneId */);
+                        mMockIccCardStatus, 0 /* phoneId */, new Object());
                 latch.countDown();
             });
             assertTrue(latch.await(WAIT_TIMEOUT_MLLIS, TimeUnit.MILLISECONDS));