Merge "Extract telephony-protos target."
diff --git a/src/java/com/android/internal/telephony/AnswerToReset.java b/src/java/com/android/internal/telephony/AnswerToReset.java
new file mode 100644
index 0000000..4440fd2
--- /dev/null
+++ b/src/java/com/android/internal/telephony/AnswerToReset.java
@@ -0,0 +1,415 @@
+/*
+ * 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 android.annotation.Nullable;
+import android.telephony.Rlog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.uicc.IccUtils;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * This class parses an Answer To Reset (ATR) message.
+ * The ATR message structure is defined in standard ISO/IEC 7816-3. The eUICC related ATR message
+ * is defined in standard ETSI TS 102 221 V14.0.0.
+ */
+public class AnswerToReset {
+    private static final String TAG = "AnswerToReset";
+    private static final boolean VDBG = false; // STOPSHIP if true
+
+    public static final byte EUICC_SUPPORTED = (byte) 0x82;
+    public static final byte DIRECT_CONVENTION = (byte) 0x3B;
+    public static final byte INVERSE_CONVENTION = (byte) 0x3F;
+    public static final int INTERFACE_BYTES_MASK = 0xF0;
+    public static final int T_MASK = 0x0F;
+    public static final int T_VALUE_FOR_GLOBAL_INTERFACE = 15;
+    public static final int TA_MASK = 0x10;
+    public static final int TB_MASK = 0x20;
+    public static final int TC_MASK = 0x40;
+    public static final int TD_MASK = 0x80;
+
+    private boolean mIsDirectConvention;
+    private boolean mOnlyTEqualsZero = true;
+    private boolean mIsEuiccSupported;
+    private byte mFormatByte;
+    private ArrayList<InterfaceByte> mInterfaceBytes = new ArrayList<>();
+    private byte[] mHistoricalBytes;
+    private Byte mCheckByte;
+
+    /**
+     * Returns an AnswerToReset by parsing the input atr string, return null if the parsing fails.
+     */
+    public static AnswerToReset parseAtr(String atr) {
+        AnswerToReset answerToReset = new AnswerToReset();
+        if (answerToReset.parseAtrString(atr)) {
+            return answerToReset;
+        }
+        return null;
+    }
+
+    private AnswerToReset() {}
+
+    private static String byteToStringHex(Byte b) {
+        return b == null ? null : IccUtils.byteToHex(b);
+    }
+
+    private void checkIsEuiccSupported() {
+        // eUICC is supported only if the value of the first tB after T=15 is 82.
+        for (int i = 0; i < mInterfaceBytes.size() - 1; i++) {
+            if (mInterfaceBytes.get(i).getTD() != null
+                    && (mInterfaceBytes.get(i).getTD() & T_MASK) == T_VALUE_FOR_GLOBAL_INTERFACE
+                    && mInterfaceBytes.get(i + 1).getTB() != null
+                    && mInterfaceBytes.get(i + 1).getTB() == EUICC_SUPPORTED) {
+                mIsEuiccSupported = true;
+                return;
+            }
+        }
+    }
+
+    private int parseConventionByte(byte[] atrBytes, int index) {
+        if (index >= atrBytes.length) {
+            loge("Failed to read the convention byte.");
+            return -1;
+        }
+        byte value = atrBytes[index];
+        if (value == DIRECT_CONVENTION) {
+            mIsDirectConvention = true;
+        } else if (value == INVERSE_CONVENTION) {
+            mIsDirectConvention = false;
+        } else {
+            loge("Unrecognized convention byte " + IccUtils.byteToHex(value));
+            return -1;
+        }
+        return index + 1;
+    }
+
+    private int parseFormatByte(byte[] atrBytes, int index) {
+        if (index >= atrBytes.length) {
+            loge("Failed to read the format byte.");
+            return -1;
+        }
+        mFormatByte = atrBytes[index];
+        mHistoricalBytes = new byte[mFormatByte & T_MASK];
+        if (VDBG) log("mHistoricalBytesLength: " + mHistoricalBytes.length);
+        return index + 1;
+    }
+
+    private int parseInterfaceBytes(byte[] atrBytes, int index) {
+        // The first lastTD is actually not any TD but instead the format byte.
+        byte lastTD = mFormatByte;
+        while (true) {
+            if (VDBG) log("lastTD: " + IccUtils.byteToHex(lastTD));
+            // Parse the interface bytes.
+            if ((lastTD & INTERFACE_BYTES_MASK) == 0) {
+                break;
+            }
+
+            InterfaceByte interfaceByte = new InterfaceByte();
+            if (VDBG) log("lastTD & TA_MASK: " + IccUtils.byteToHex((byte) (lastTD & TA_MASK)));
+            if ((lastTD & TA_MASK) != 0) {
+                if (index >= atrBytes.length) {
+                    loge("Failed to read the byte for TA.");
+                    return -1;
+                }
+                interfaceByte.setTA(atrBytes[index]);
+                index++;
+            }
+            if (VDBG) log("lastTD & TB_MASK: " + IccUtils.byteToHex((byte) (lastTD & TB_MASK)));
+            if ((lastTD & TB_MASK) != 0) {
+                if (index >= atrBytes.length) {
+                    loge("Failed to read the byte for TB.");
+                    return -1;
+                }
+                interfaceByte.setTB(atrBytes[index]);
+                index++;
+            }
+            if (VDBG) log("lastTD & TC_MASK: " + IccUtils.byteToHex((byte) (lastTD & TC_MASK)));
+            if ((lastTD & TC_MASK) != 0) {
+                if (index >= atrBytes.length) {
+                    loge("Failed to read the byte for TC.");
+                    return -1;
+                }
+                interfaceByte.setTC(atrBytes[index]);
+                index++;
+            }
+            if (VDBG) log("lastTD & TD_MASK: " + IccUtils.byteToHex((byte) (lastTD & TD_MASK)));
+            if ((lastTD & TD_MASK) != 0) {
+                if (index >= atrBytes.length) {
+                    loge("Failed to read the byte for TD.");
+                    return -1;
+                }
+                interfaceByte.setTD(atrBytes[index]);
+                index++;
+            }
+            mInterfaceBytes.add(interfaceByte);
+            Byte newTD = interfaceByte.getTD();
+            if (VDBG) log("index=" + index + ", " + toString());
+            if (newTD == null) {
+                break;
+            }
+            lastTD = newTD;
+            // Parse the T values from all the TD, here we only check whether T is equal to any
+            // other values other than 0, since the check byte can be absent only when T is
+            // equal to 0.
+            if ((lastTD & T_MASK) != 0) {
+                mOnlyTEqualsZero = false;
+            }
+        }
+        return index;
+    }
+
+    private int parseHistoricalBytes(byte[] atrBytes, int index) {
+        if (mHistoricalBytes.length + index > atrBytes.length) {
+            loge("Failed to read the historical bytes.");
+            return -1;
+        }
+        if (mHistoricalBytes.length > 0) {
+            System.arraycopy(atrBytes, index, mHistoricalBytes, 0, mHistoricalBytes.length);
+        }
+        return index + mHistoricalBytes.length;
+    }
+
+    private int parseCheckBytes(byte[] atrBytes, int index) {
+        if (index < atrBytes.length) {
+            mCheckByte = atrBytes[index];
+            index++;
+        } else {
+            if (!mOnlyTEqualsZero) {
+                loge("Check byte must be present because T equals to values other than 0.");
+                return -1;
+            } else {
+                log("Check byte can be absent because T=0.");
+            }
+        }
+        return index;
+    }
+
+    private boolean parseAtrString(String atr) {
+        if (atr.length() % 2 != 0) {
+            loge("The length of input ATR string " + atr.length() + " is not even.");
+            return false;
+        }
+
+        if (atr.length() < 4) {
+            loge("Valid ATR string must at least contains TS and T0.");
+            return false;
+        }
+
+        byte[] atrBytes = IccUtils.hexStringToBytes(atr);
+        if (atrBytes == null) {
+            return false;
+        }
+
+        int index = parseConventionByte(atrBytes, 0);
+        if (index == -1) {
+            return false;
+        }
+
+        index = parseFormatByte(atrBytes, index);
+        if (index == -1) {
+            return false;
+        }
+
+        index = parseInterfaceBytes(atrBytes, index);
+        if (index == -1) {
+            return false;
+        }
+
+        index = parseHistoricalBytes(atrBytes, index);
+        if (index == -1) {
+            return false;
+        }
+
+        index = parseCheckBytes(atrBytes, index);
+        if (index == -1) {
+            return false;
+        }
+
+        if (index != atrBytes.length) {
+            loge("Unexpected bytes after the check byte.");
+            return false;
+        }
+        log("Successfully parsed the ATR string " + atr + " into " + toString());
+        checkIsEuiccSupported();
+        return true;
+    }
+
+    /**
+     * This class holds the interface bytes.
+     */
+    public static class InterfaceByte {
+        private Byte mTA;
+        private Byte mTB;
+        private Byte mTC;
+        private Byte mTD;
+
+        @Nullable
+        public Byte getTA() {
+            return mTA;
+        }
+
+        @Nullable
+        public Byte getTB() {
+            return mTB;
+        }
+
+        @Nullable
+        public Byte getTC() {
+            return mTC;
+        }
+
+        @Nullable
+        public Byte getTD() {
+            return mTD;
+        }
+
+        public void setTA(Byte tA) {
+            mTA = tA;
+        }
+
+        public void setTB(Byte tB) {
+            mTB = tB;
+        }
+
+        public void setTC(Byte tC) {
+            mTC = tC;
+        }
+
+        public void setTD(Byte tD) {
+            mTD = tD;
+        }
+
+        private InterfaceByte() {
+            mTA = null;
+            mTB = null;
+            mTC = null;
+            mTD = null;
+        }
+
+        @VisibleForTesting
+        public InterfaceByte(Byte tA, Byte tB, Byte tC, Byte tD) {
+            this.mTA = tA;
+            this.mTB = tB;
+            this.mTC = tC;
+            this.mTD = tD;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            InterfaceByte ib = (InterfaceByte) o;
+            return (Objects.equals(mTA, ib.getTA())
+                    && Objects.equals(mTB, ib.getTB())
+                    && Objects.equals(mTC, ib.getTC())
+                    && Objects.equals(mTD, ib.getTD()));
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mTA, mTB, mTC, mTD);
+        }
+
+        @Override
+        public String toString() {
+            StringBuffer sb = new StringBuffer();
+            sb.append("{");
+            sb.append("TA=").append(byteToStringHex(mTA)).append(",");
+            sb.append("TB=").append(byteToStringHex(mTB)).append(",");
+            sb.append("TC=").append(byteToStringHex(mTC)).append(",");
+            sb.append("TD=").append(byteToStringHex(mTD));
+            sb.append("}");
+            return sb.toString();
+        }
+    };
+
+    private static void log(String msg) {
+        Rlog.d(TAG, msg);
+    }
+
+    private static void loge(String msg) {
+        Rlog.e(TAG, msg);
+    }
+
+    public byte getConventionByte() {
+        return mIsDirectConvention ? DIRECT_CONVENTION : INVERSE_CONVENTION;
+    }
+
+    public byte getFormatByte() {
+        return mFormatByte;
+    }
+
+    public List<InterfaceByte> getInterfaceBytes() {
+        return mInterfaceBytes;
+    }
+
+    @Nullable
+    public byte[] getHistoricalBytes() {
+        return mHistoricalBytes;
+    }
+
+    @Nullable
+    public Byte getCheckByte() {
+        return mCheckByte;
+    }
+
+    public boolean isEuiccSupported() {
+        return mIsEuiccSupported;
+    }
+
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+
+        sb.append("AnswerToReset:{");
+        sb.append("mConventionByte=")
+                .append(IccUtils.byteToHex(getConventionByte())).append(",");
+        sb.append("mFormatByte=").append(byteToStringHex(mFormatByte)).append(",");
+        sb.append("mInterfaceBytes={");
+        for (InterfaceByte ib : mInterfaceBytes) {
+            sb.append(ib.toString());
+        }
+        sb.append("},");
+        sb.append("mHistoricalBytes={");
+        for (byte b : mHistoricalBytes) {
+            sb.append(IccUtils.byteToHex(b)).append(",");
+        }
+        sb.append("},");
+        sb.append("mCheckByte=").append(byteToStringHex(mCheckByte));
+        sb.append("}");
+        return sb.toString();
+    }
+
+    /**
+     * Dump
+     */
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("AnswerToReset:");
+        pw.println(toString());
+        pw.flush();
+    }
+}
diff --git a/src/java/com/android/internal/telephony/CellularNetworkService.java b/src/java/com/android/internal/telephony/CellularNetworkService.java
index f8efd30..55d5f25 100644
--- a/src/java/com/android/internal/telephony/CellularNetworkService.java
+++ b/src/java/com/android/internal/telephony/CellularNetworkService.java
@@ -167,8 +167,7 @@
             }
         }
 
-        private int[] getAvailableServices(int regState, int domain,
-                boolean emergencyOnly) {
+        private int[] getAvailableServices(int regState, int domain, boolean emergencyOnly) {
             int[] availableServices = null;
 
             // In emergency only states, only SERVICE_TYPE_EMERGENCY is available.
@@ -177,7 +176,7 @@
             if (emergencyOnly) {
                 availableServices = new int[] {NetworkRegistrationState.SERVICE_TYPE_EMERGENCY};
             } else if (regState == NetworkRegistrationState.REG_STATE_ROAMING
-                    || regState != NetworkRegistrationState.REG_STATE_HOME) {
+                    || regState == NetworkRegistrationState.REG_STATE_HOME) {
                 if (domain == NetworkRegistrationState.DOMAIN_PS) {
                     availableServices = new int[] {NetworkRegistrationState.SERVICE_TYPE_DATA};
                 } else if (domain == NetworkRegistrationState.DOMAIN_CS) {
@@ -243,6 +242,10 @@
 
         private CellIdentity convertHalCellIdentityToCellIdentity(
                 android.hardware.radio.V1_0.CellIdentity cellIdentity) {
+            if (cellIdentity == null) {
+                return null;
+            }
+
             CellIdentity result = null;
             switch(cellIdentity.cellInfoType) {
                 case CellInfoType.GSM: {
@@ -305,6 +308,7 @@
             return result;
         }
 
+        @Override
         public void getNetworkRegistrationState(int domain, NetworkServiceCallback callback) {
             if (DBG) log("getNetworkRegistrationState for domain " + domain);
             Message message = null;
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index 0c51dca..3e6391a 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -1957,11 +1957,6 @@
     }
 
     @Override
-    public void getDataCallList(Message response) {
-        mCi.getDataCallList(response);
-    }
-
-    @Override
     public void updateServiceLocation() {
         mSST.enableSingleLocationUpdate();
     }
@@ -2294,8 +2289,14 @@
                         config_switch_phone_on_voice_reg_state_change)) {
                     mCi.getVoiceRadioTechnology(obtainMessage(EVENT_REQUEST_VOICE_RADIO_TECH_DONE));
                 }
-                // Force update IMS service
-                ImsManager.getInstance(mContext, mPhoneId).updateImsServiceConfig(true);
+                // Force update IMS service if it is available, if it isn't the config will be
+                // updated when ImsPhoneCallTracker opens a connection.
+                ImsManager imsManager = ImsManager.getInstance(mContext, mPhoneId);
+                if (imsManager.isServiceAvailable()) {
+                    imsManager.updateImsServiceConfig(true);
+                } else {
+                    logd("ImsManager is not available to update CarrierConfig.");
+                }
 
                 // Update broadcastEmergencyCallStateChanges
                 CarrierConfigManager configMgr = (CarrierConfigManager)
diff --git a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
index ac629f8..9059f2f 100644
--- a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
+++ b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
@@ -16,32 +16,25 @@
 
 package com.android.internal.telephony;
 
-import android.app.Activity;
 import android.os.RemoteException;
 import android.os.Message;
-import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
-import android.provider.Telephony.Sms;
-import android.content.Intent;
 import android.telephony.Rlog;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.aidl.IImsSmsListener;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.ImsSmsImplBase;
+import android.telephony.ims.stub.ImsSmsImplBase.SendStatusResult;
+import android.provider.Telephony.Sms.Intents;
+import android.util.Pair;
 
 import com.android.ims.ImsException;
 import com.android.ims.ImsManager;
-import com.android.ims.ImsServiceProxy;
-import com.android.ims.internal.IImsSmsListener;
+import com.android.ims.MmTelFeatureConnection;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
 import com.android.internal.telephony.util.SMSDispatcherUtil;
-import com.android.internal.telephony.gsm.SmsMessage;
-
-import android.telephony.ims.internal.feature.ImsFeature;
-import android.telephony.ims.internal.feature.MmTelFeature;
-import android.telephony.ims.internal.stub.SmsImplBase;
-import android.telephony.ims.internal.stub.SmsImplBase.SendStatusResult;
-import android.telephony.ims.internal.stub.SmsImplBase.StatusReportResult;
-import android.telephony.ims.stub.ImsRegistrationImplBase;
-import android.provider.Telephony.Sms.Intents;
-import android.util.Pair;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -51,6 +44,7 @@
 /**
  * Responsible for communications with {@link com.android.ims.ImsManager} to send/receive messages
  * over IMS.
+ * @hide
  */
 public class ImsSmsDispatcher extends SMSDispatcher {
     // Initial condition for ims connection retry.
@@ -107,7 +101,7 @@
                 }
 
                 @Override
-                public void onDeregistered(com.android.ims.ImsReasonInfo info) {
+                public void onDeregistered(ImsReasonInfo info) {
                     Rlog.d(TAG, "onImsDisconnected imsReasonInfo=" + info);
                     synchronized (mLock) {
                         mIsRegistered = false;
@@ -127,12 +121,12 @@
     };
 
     // Callback fires when ImsManager MMTel Feature changes state
-    private ImsServiceProxy.IFeatureUpdate mNotifyStatusChangedCallback =
-            new ImsServiceProxy.IFeatureUpdate() {
+    private MmTelFeatureConnection.IFeatureUpdate mNotifyStatusChangedCallback =
+            new MmTelFeatureConnection.IFeatureUpdate() {
                 @Override
                 public void notifyStateChanged() {
                     try {
-                        int status = getImsManager().getImsServiceStatus();
+                        int status = getImsManager().getImsServiceState();
                         Rlog.d(TAG, "Status Changed: " + status);
                         switch (status) {
                             case android.telephony.ims.feature.ImsFeature.STATE_READY: {
@@ -144,7 +138,7 @@
                             }
                             case android.telephony.ims.feature.ImsFeature.STATE_INITIALIZING:
                                 // fall through
-                            case android.telephony.ims.feature.ImsFeature.STATE_NOT_AVAILABLE:
+                            case ImsFeature.STATE_UNAVAILABLE:
                                 synchronized (mLock) {
                                     mIsImsServiceUp = false;
                                 }
@@ -174,18 +168,18 @@
                 throw new IllegalArgumentException("Invalid token.");
             }
             switch(reason) {
-                case SmsImplBase.SEND_STATUS_OK:
+                case ImsSmsImplBase.SEND_STATUS_OK:
                     tracker.onSent(mContext);
                     break;
-                case SmsImplBase.SEND_STATUS_ERROR:
+                case ImsSmsImplBase.SEND_STATUS_ERROR:
                     tracker.onFailed(mContext, reason, 0 /* errorCode */);
                     mTrackers.remove(token);
                     break;
-                case SmsImplBase.SEND_STATUS_ERROR_RETRY:
+                case ImsSmsImplBase.SEND_STATUS_ERROR_RETRY:
                     tracker.mRetryCount += 1;
                     sendSms(tracker);
                     break;
-                case SmsImplBase.SEND_STATUS_ERROR_FALLBACK:
+                case ImsSmsImplBase.SEND_STATUS_ERROR_FALLBACK:
                     fallbackToPstn(token, tracker);
                     break;
                 default:
@@ -208,8 +202,8 @@
                 getImsManager().acknowledgeSmsReport(
                         token,
                         messageRef,
-                        result.first ? SmsImplBase.STATUS_REPORT_STATUS_OK
-                                : SmsImplBase.STATUS_REPORT_STATUS_ERROR);
+                        result.first ? ImsSmsImplBase.STATUS_REPORT_STATUS_OK
+                                : ImsSmsImplBase.STATUS_REPORT_STATUS_ERROR);
             } catch (ImsException e) {
                 Rlog.e(TAG, "Failed to acknowledgeSmsReport(). Error: "
                         + e.getMessage());
@@ -229,8 +223,8 @@
                     getImsManager().acknowledgeSms(token,
                             0,
                             result == Intents.RESULT_SMS_HANDLED
-                                    ? SmsImplBase.STATUS_REPORT_STATUS_OK
-                                    : SmsImplBase.DELIVER_STATUS_ERROR);
+                                    ? ImsSmsImplBase.STATUS_REPORT_STATUS_OK
+                                    : ImsSmsImplBase.DELIVER_STATUS_ERROR);
                 } catch (ImsException e) {
                     Rlog.e(TAG, "Failed to acknowledgeSms(). Error: " + e.getMessage());
                 }
@@ -286,7 +280,7 @@
         if (getImsManager().isServiceAvailable()) {
             return;
         }
-        // remove callback so we do not receive updates from old ImsServiceProxy when switching
+        // remove callback so we do not receive updates from old MmTelFeatureConnection when switching
         // between ImsServices.
         getImsManager().removeNotifyStatusChangedCallback(mNotifyStatusChangedCallback);
         // Exponential backoff during retry, limited to 32 seconds.
diff --git a/src/java/com/android/internal/telephony/NetworkRegistrationManager.java b/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
new file mode 100644
index 0000000..30210a8
--- /dev/null
+++ b/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
@@ -0,0 +1,230 @@
+/*
+ * 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 android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.os.Registrant;
+import android.os.RegistrantList;
+import android.os.RemoteException;
+import android.telephony.AccessNetworkConstants.TransportType;
+import android.telephony.CarrierConfigManager;
+import android.telephony.INetworkService;
+import android.telephony.INetworkServiceCallback;
+import android.telephony.NetworkRegistrationState;
+import android.telephony.NetworkService;
+import android.telephony.Rlog;
+
+/**
+ * Class that serves as the layer between NetworkService and ServiceStateTracker. It helps binding,
+ * sending request and registering for state change to NetworkService.
+ */
+public class NetworkRegistrationManager {
+    private static final String TAG = NetworkRegistrationManager.class.getSimpleName();
+
+    private final int mTransportType;
+
+    private final Phone mPhone;
+
+    private final CarrierConfigManager mCarrierConfigManager;
+
+    // Registrants who listens registration state change callback from this class.
+    private final RegistrantList mRegStateChangeRegistrants = new RegistrantList();
+
+    private INetworkService.Stub mServiceBinder;
+
+    private RegManagerDeathRecipient mDeathRecipient;
+
+    public NetworkRegistrationManager(int transportType, Phone phone) {
+        mTransportType = transportType;
+        mPhone = phone;
+        mCarrierConfigManager = (CarrierConfigManager) phone.getContext().getSystemService(
+                Context.CARRIER_CONFIG_SERVICE);
+
+        bindService();
+    }
+
+    public boolean isServiceConnected() {
+        return (mServiceBinder != null) && (mServiceBinder.isBinderAlive());
+    }
+
+    public void unregisterForNetworkRegistrationStateChanged(Handler h) {
+        mRegStateChangeRegistrants.remove(h);
+    }
+
+    public void registerForNetworkRegistrationStateChanged(Handler h, int what, Object obj) {
+        logd("registerForNetworkRegistrationStateChanged");
+        Registrant r = new Registrant(h, what, obj);
+        mRegStateChangeRegistrants.addUnique(h, what, obj);
+    }
+
+    public void getNetworkRegistrationState(int domain, Message onCompleteMessage) {
+        if (onCompleteMessage == null) return;
+
+        logd("getNetworkRegistrationState domain " + domain);
+        if (!isServiceConnected()) {
+            onCompleteMessage.obj = new AsyncResult(onCompleteMessage.obj, null,
+                    new IllegalStateException("Service not connected."));
+            onCompleteMessage.sendToTarget();
+            return;
+        }
+
+        try {
+            mServiceBinder.getNetworkRegistrationState(mPhone.getPhoneId(), domain,
+                    new NetworkRegStateCallback(onCompleteMessage));
+        } catch (RemoteException e) {
+            Rlog.e(TAG, "getNetworkRegistrationState RemoteException " + e);
+            onCompleteMessage.obj = new AsyncResult(onCompleteMessage.obj, null, e);
+            onCompleteMessage.sendToTarget();
+        }
+    }
+
+    private class RegManagerDeathRecipient implements IBinder.DeathRecipient {
+
+        private final ComponentName mComponentName;
+
+        RegManagerDeathRecipient(ComponentName name) {
+            mComponentName = name;
+        }
+
+        @Override
+        public void binderDied() {
+            // TODO: try to restart the service.
+            logd("NetworkService(" + mComponentName +  " transport type "
+                    + mTransportType + ") died.");
+        }
+    }
+
+    private class NetworkServiceConnection implements ServiceConnection {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            mServiceBinder = (INetworkService.Stub) service;
+            mDeathRecipient = new RegManagerDeathRecipient(name);
+            try {
+                mServiceBinder.linkToDeath(mDeathRecipient, 0);
+                mServiceBinder.createNetworkServiceProvider(mPhone.getPhoneId());
+                mServiceBinder.registerForNetworkRegistrationStateChanged(mPhone.getPhoneId(),
+                        new NetworkRegStateCallback(null));
+            } catch (RemoteException exception) {
+                // Remote exception means that the binder already died.
+                mDeathRecipient.binderDied();
+                logd("RemoteException " + exception);
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            logd("onServiceDisconnected " + name);
+            if (mServiceBinder != null) {
+                mServiceBinder.unlinkToDeath(mDeathRecipient, 0);
+            }
+        }
+    }
+
+    private class NetworkRegStateCallback extends INetworkServiceCallback.Stub {
+        // Message only used upon onGetNetworkRegistrationStateComplete.
+        // If the callback is passed to listen to network state change,
+        // this message is null.
+        private final Message mOnCompleteMessage;
+
+        NetworkRegStateCallback(Message onCompleteMessage) {
+            mOnCompleteMessage = onCompleteMessage;
+        }
+
+        @Override
+        public void onGetNetworkRegistrationStateComplete(
+                int result, NetworkRegistrationState state) {
+            logd("onGetNetworkRegistrationStateComplete result "
+                    + result + " state " + state);
+            mOnCompleteMessage.arg1 = result;
+            mOnCompleteMessage.obj = new AsyncResult(mOnCompleteMessage.obj, state, null);
+            mOnCompleteMessage.sendToTarget();
+        }
+
+        @Override
+        public void onNetworkStateChanged() {
+            logd("onNetworkStateChanged");
+            mRegStateChangeRegistrants.notifyRegistrants();
+        }
+    }
+
+    private boolean bindService() {
+        Intent intent = new Intent(NetworkService.NETWORK_SERVICE_INTERFACE);
+        intent.setPackage(getPackageName());
+        try {
+            // We bind this as a foreground service because it is operating directly on the SIM,
+            // and we do not want it subjected to power-savings restrictions while doing so.
+            return mPhone.getContext().bindService(intent, new NetworkServiceConnection(),
+                    Context.BIND_AUTO_CREATE);
+        } catch (SecurityException e) {
+            loge("bindService failed " + e);
+            return false;
+        }
+    }
+
+    private String getPackageName() {
+        String packageName;
+        int resourceId;
+        String carrierConfig;
+
+        switch (mTransportType) {
+            case TransportType.WWAN:
+                resourceId = com.android.internal.R.string.config_wwan_network_service_package;
+                carrierConfig = CarrierConfigManager
+                        .KEY_CARRIER_NETWORK_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING;
+                break;
+            case TransportType.WLAN:
+                resourceId = com.android.internal.R.string.config_wlan_network_service_package;
+                carrierConfig = CarrierConfigManager
+                        .KEY_CARRIER_NETWORK_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING;
+                break;
+            default:
+                throw new IllegalStateException("Transport type not WWAN or WLAN. type="
+                        + mTransportType);
+        }
+
+        // Read package name from resource overlay
+        packageName = mPhone.getContext().getResources().getString(resourceId);
+
+        PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId());
+
+        if (b != null) {
+            // If carrier config overrides it, use the one from carrier config
+            packageName = b.getString(carrierConfig, packageName);
+        }
+
+        logd("Binding to packageName " + packageName + " for transport type"
+                + mTransportType);
+
+        return packageName;
+    }
+
+    private static int logd(String msg) {
+        return Rlog.d(TAG, msg);
+    }
+
+    private static int loge(String msg) {
+        return Rlog.e(TAG, msg);
+    }
+}
diff --git a/src/java/com/android/internal/telephony/NitzStateMachine.java b/src/java/com/android/internal/telephony/NitzStateMachine.java
index 20e9802..1a365ee 100644
--- a/src/java/com/android/internal/telephony/NitzStateMachine.java
+++ b/src/java/com/android/internal/telephony/NitzStateMachine.java
@@ -19,7 +19,6 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.os.PowerManager;
-import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.provider.Settings;
 import android.telephony.Rlog;
@@ -29,6 +28,8 @@
 import android.util.TimeUtils;
 
 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;
@@ -97,20 +98,6 @@
             return ignoreNitz != null && ignoreNitz.equals("yes");
         }
 
-        /**
-         * Returns the same value as {@link System#currentTimeMillis()}.
-         */
-        public long currentTimeMillis() {
-            return System.currentTimeMillis();
-        }
-
-        /**
-         * Returns the same value as {@link SystemClock#elapsedRealtime()}.
-         */
-        public long elapsedRealtime() {
-            return SystemClock.elapsedRealtime();
-        }
-
         public String getNetworkCountryIsoForPhone() {
             return mTelephonyManager.getNetworkCountryIsoForPhone(mPhone.getPhoneId());
         }
@@ -131,13 +118,13 @@
     // Time Zone detection state.
 
     /**
-     * Sometimes we get the NITZ time before we know what country we
-     * are in. Keep the time zone information from the NITZ string in
-     * mNitzData so we can fix the time zone once know the country.
+     * Sometimes we get the NITZ time before we know what country we are in. We keep the time zone
+     * information from the NITZ string in mLatestNitzSignal so we can fix the time zone once we
+     * know the country.
      */
     private boolean mNeedCountryCodeForNitz = false;
 
-    private NitzData mNitzData;
+    private TimeStampedValue<NitzData> mLatestNitzSignal;
     private boolean mGotCountryCode = false;
     private String mSavedTimeZoneId;
 
@@ -165,7 +152,7 @@
 
     public NitzStateMachine(GsmCdmaPhone phone) {
         this(phone,
-                TelephonyComponentFactory.getInstance().makeTimeServiceHelper(phone.getContext()),
+                new TimeServiceHelper(phone.getContext()),
                 new DeviceState(phone),
                 new TimeZoneLookupHelper());
     }
@@ -218,121 +205,147 @@
         }
 
         if (countryChanged || mNeedCountryCodeForNitz) {
-            // Capture the time zone property. This allows us to tell whether the device has a time
-            // zone set. TimeZone.getDefault() returns a default zone (GMT) even when time zone is
-            // not explicitly set making the system property a better indicator of whether an
-            // explicit time zone choice has been made.
+            // 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, "fixTimeZone"
+                Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet:"
                         + " isTimeZoneSettingInitialized=" + isTimeZoneSettingInitialized
-                        + " mNitzData=" + mNitzData
-                        + " iso-cc='" + isoCountryCode
-                        + "' countryUsesUtc="
-                        + mTimeZoneLookupHelper.countryUsesUtc(isoCountryCode));
+                        + " mLatestNitzSignal=" + mLatestNitzSignal
+                        + " isoCountryCode=" + isoCountryCode);
             }
             String zoneId;
             if (TextUtils.isEmpty(isoCountryCode) && mNeedCountryCodeForNitz) {
                 // Country code not found.  This is likely a test network.
                 // Get a TimeZone based only on the NITZ parameters (best guess).
 
-                // mNeedCountryCodeForNitz is only set to true when mNitzData is set so there's no
-                // need to check mNitzData == null.
-                zoneId = mTimeZoneLookupHelper.guessZoneIdByNitz(mNitzData);
+                // mNeedCountryCodeForNitz is only set to true when mLatestNitzSignal is set so
+                // there's no need to check mLatestNitzSignal == null.
+                OffsetResult lookupResult =
+                        mTimeZoneLookupHelper.lookupByNitz(mLatestNitzSignal.mValue);
                 if (DBG) {
-                    Rlog.d(LOG_TAG, "fixTimeZone(): guessNitzTimeZone returned " + zoneId);
+                    Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: guessZoneIdByNitz() returned"
+                            + " lookupResult=" + lookupResult);
                 }
-            } else if ((mNitzData == null || nitzOffsetMightBeBogus(mNitzData))
-                    && isTimeZoneSettingInitialized
-                    && !mTimeZoneLookupHelper.countryUsesUtc(isoCountryCode)) {
+                zoneId = lookupResult != null ? lookupResult.zoneId : null;
+            } else if (mLatestNitzSignal == null) {
+                zoneId = null;
+                if (DBG) {
+                    Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: No cached NITZ data available,"
+                            + " not setting zone");
+                }
+            } else { // mLatestNitzSignal != null
+                if (nitzOffsetMightBeBogus(mLatestNitzSignal.mValue)
+                        && isTimeZoneSettingInitialized
+                        && !countryUsesUtc(isoCountryCode, mLatestNitzSignal)) {
 
-                // This case means that (1) the device received no NITZ signal yet or received an
-                // NITZ signal that looks bogus due to having a zero offset from UTC, (2) the device
-                // has a time zone set explicitly, and (3) the iso tells us the country is NOT one
-                // that uses a zero offset. This is interpreted as being NITZ incorrectly reporting
-                // a local time and not a UTC time. The zone is left as the current device's zone
-                // setting, and the time may be adjusted by assuming the current zone setting is
-                // correct.
-                TimeZone zone = TimeZone.getDefault();
-                zoneId = zone.getID();
+                    // This case means that (1) the device received an NITZ signal that could be
+                    // bogus due to having a zero offset from UTC, (2) the device has had a time
+                    // zone set explicitly and (3) the iso tells us the country is NOT one that uses
+                    // a zero offset. This is interpreted as being NITZ incorrectly reporting a
+                    // local time and not a UTC time. The zone is left as the current device's zone
+                    // setting, and the system clock may be adjusted by taking the NITZ time and
+                    // assuming the current zone setting is correct.
 
-                // Note that mNeedCountryCodeForNitz => (implies) { mNitzData != null }. Therefore,
-                // if mNitzData == null, mNeedCountryCodeForNitz cannot be true. The code in this
-                // section therefore means that when mNitzData == null (and the country is one that
-                // doesn't use a zero UTC offset) the device will retain the existing time zone
-                // setting and not try to derive one from the isoCountryCode.
-                if (mNeedCountryCodeForNitz) {
-                    try {
-                        // Acquire the wakelock as we're reading the system clock here.
-                        mWakeLock.acquire();
-
-                        long ctm = mDeviceState.currentTimeMillis();
-                        long tzOffset = zone.getOffset(ctm);
-                        if (DBG) {
-                            Rlog.d(LOG_TAG, "fixTimeZone: tzOffset=" + tzOffset
-                                    + " ltod=" + TimeUtils.logTimeOfDay(ctm));
-                        }
-                        if (mTimeServiceHelper.isTimeDetectionEnabled()) {
-                            long adj = ctm - tzOffset;
-                            String msg = "fixTimeZone: adj ltod=" + TimeUtils.logTimeOfDay(adj);
-                            setAndBroadcastNetworkSetTime(msg, adj);
-                        } else {
-                            // Adjust the saved NITZ time to account for tzOffset.
-                            mSavedNitzTime = new TimeStampedValue<>(
-                                    mSavedNitzTime.mValue - tzOffset,
-                                    mSavedNitzTime.mElapsedRealtime);
-                            if (DBG) {
-                                Rlog.d(LOG_TAG, "fixTimeZone: adj mSavedTime=" + mSavedNitzTime);
-                            }
-                        }
-                    } finally {
-                        mWakeLock.release();
+                    TimeZone zone = TimeZone.getDefault();
+                    if (DBG) {
+                        Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: NITZ looks bogus, maybe using"
+                                + " current default zone to adjust the system clock,"
+                                + " mNeedCountryCodeForNitz=" + mNeedCountryCodeForNitz
+                                + " mLatestNitzSignal=" + mLatestNitzSignal
+                                + " zone=" + zone);
                     }
-                }
-                if (DBG) {
-                    Rlog.d(LOG_TAG, "fixTimeZone: using default TimeZone");
-                }
-            } else if (mNitzData == null) {
-                // The use of 1/1/1970 UTC is unusual but consistent with historical behavior when
-                // it wasn't possible to detect whether a previous NITZ signal had been saved.
-                zoneId = mTimeZoneLookupHelper.guessZoneIdByInstantOffsetDstCountry(
-                        0 /* when */, 0 /* offset */, false /* dst */, isoCountryCode);
-                if (DBG) {
-                    Rlog.d(LOG_TAG, "fixTimeZone: No cached NITZ data available, using only country"
-                            + " code. zoneId=" + zoneId);
-                }
-            } else {
-                zoneId = mTimeZoneLookupHelper.guessZoneIdByNitzCountry(mNitzData, isoCountryCode);
-                if (DBG) {
-                    Rlog.d(LOG_TAG, "fixTimeZone: using getTimeZone(off, dst, time, iso)");
+                    zoneId = zone.getID();
+
+                    if (mNeedCountryCodeForNitz) {
+                        NitzData nitzData = mLatestNitzSignal.mValue;
+                        try {
+                            // Acquire the wakelock as we're reading the elapsed realtime clock
+                            // here.
+                            mWakeLock.acquire();
+
+                            // Use the time that came with the NITZ offset that we think is bogus:
+                            // we just interpret it as local time.
+                            long ctm = nitzData.getCurrentTimeInMillis();
+                            long delayAdjustedCtm = ctm + (mTimeServiceHelper.elapsedRealtime()
+                                    - mLatestNitzSignal.mElapsedRealtime);
+                            long tzOffset = zone.getOffset(delayAdjustedCtm);
+                            if (DBG) {
+                                Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet:"
+                                        + " tzOffset=" + tzOffset
+                                        + " delayAdjustedCtm="
+                                        + TimeUtils.logTimeOfDay(delayAdjustedCtm));
+                            }
+                            if (mTimeServiceHelper.isTimeDetectionEnabled()) {
+                                long timeZoneAdjustedCtm = delayAdjustedCtm - tzOffset;
+                                String msg = "handleNetworkCountryCodeSet: setting time"
+                                        + " timeZoneAdjustedCtm="
+                                        + TimeUtils.logTimeOfDay(timeZoneAdjustedCtm);
+                                setAndBroadcastNetworkSetTime(msg, timeZoneAdjustedCtm);
+                            } else {
+                                // Adjust the saved NITZ time to account for tzOffset.
+                                mSavedNitzTime = new TimeStampedValue<>(
+                                        mSavedNitzTime.mValue - tzOffset,
+                                        mSavedNitzTime.mElapsedRealtime);
+                                if (DBG) {
+                                    Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet:"
+                                            + "adjusting time mSavedNitzTime=" + mSavedNitzTime);
+                                }
+                            }
+                        } finally {
+                            mWakeLock.release();
+                        }
+                    }
+                } else {
+                    NitzData nitzData = mLatestNitzSignal.mValue;
+                    OffsetResult lookupResult =
+                            mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, isoCountryCode);
+                    if (DBG) {
+                        Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: using"
+                                + " guessZoneIdByNitzCountry(nitzData, isoCountryCode),"
+                                + " nitzData=" + nitzData
+                                + " isoCountryCode=" + isoCountryCode
+                                + " lookupResult=" + lookupResult);
+                    }
+                    zoneId = lookupResult != null ? lookupResult.zoneId : null;
                 }
             }
-            final String tmpLog = "fixTimeZone:"
+            final String tmpLog = "handleNetworkCountryCodeSet:"
                     + " isTimeZoneSettingInitialized=" + isTimeZoneSettingInitialized
-                    + " mNitzData=" + mNitzData
-                    + " iso-cc=" + isoCountryCode
+                    + " mLatestNitzSignal=" + mLatestNitzSignal
+                    + " isoCountryCode=" + isoCountryCode
                     + " mNeedCountryCodeForNitz=" + mNeedCountryCodeForNitz
                     + " zoneId=" + zoneId;
             mTimeZoneLog.log(tmpLog);
 
             if (zoneId != null) {
-                Rlog.d(LOG_TAG, "fixTimeZone: zone != null zoneId=" + zoneId);
+                Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: zoneId != null, zoneId=" + zoneId);
                 if (mTimeServiceHelper.isTimeZoneDetectionEnabled()) {
                     setAndBroadcastNetworkSetTimeZone(zoneId);
                 } else {
-                    Rlog.d(LOG_TAG, "fixTimeZone: skip changing zone as getAutoTimeZone was false");
+                    Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: skip changing zone as"
+                            + " isTimeZoneDetectionEnabled() is false");
                 }
                 if (mNeedCountryCodeForNitz) {
-                    saveNitzTimeZone(zoneId);
+                    mSavedTimeZoneId = zoneId;
                 }
             } else {
-                Rlog.d(LOG_TAG, "fixTimeZone: zone == null, do nothing for zone");
+                Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: lookupResult == null, do nothing");
             }
             mNeedCountryCodeForNitz = false;
         }
     }
 
+    private boolean countryUsesUtc(
+            String isoCountryCode, TimeStampedValue<NitzData> nitzSignal) {
+        return mTimeZoneLookupHelper.countryUsesUtc(
+                isoCountryCode,
+                nitzSignal.mValue.getCurrentTimeInMillis());
+    }
+
     /**
      * Informs the {@link NitzStateMachine} that the network has become available.
      */
@@ -384,41 +397,34 @@
                 if (!mGotCountryCode) {
                     zoneId = null;
                 } else if (!TextUtils.isEmpty(iso)) {
-                    zoneId = mTimeZoneLookupHelper.guessZoneIdByNitzCountry(newNitzData, iso);
+                    OffsetResult lookupResult =
+                            mTimeZoneLookupHelper.lookupByNitzCountry(newNitzData, iso);
+                    zoneId = lookupResult != null ? lookupResult.zoneId : null;
                 } else {
                     // We don't have a valid iso country code.  This is
                     // most likely because we're on a test network that's
                     // using a bogus MCC (eg, "001"), so get a TimeZone
                     // based only on the NITZ parameters.
-                    zoneId = mTimeZoneLookupHelper.guessZoneIdByNitz(newNitzData);
+                    OffsetResult lookupResult = mTimeZoneLookupHelper.lookupByNitz(newNitzData);
                     if (DBG) {
-                        Rlog.d(LOG_TAG, "setTimeZoneFromNitz(): findByNitz returned " + zoneId);
+                        Rlog.d(LOG_TAG, "handleTimeZoneFromNitz: guessZoneIdByNitz returned"
+                                + " lookupResult=" + lookupResult);
                     }
+                    zoneId = lookupResult != null ? lookupResult.zoneId : null;
                 }
             }
 
-            int previousUtcOffset;
-            boolean previousIsDst;
-            if (mNitzData == null) {
-                // No previously saved NITZ data. Use the same defaults as Android would have done
-                // before it was possible to detect this case.
-                previousUtcOffset = 0;
-                previousIsDst = false;
-            } else {
-                previousUtcOffset = mNitzData.getLocalOffsetMillis();
-                previousIsDst = mNitzData.isDst();
-            }
             if ((zoneId == null)
-                    || (newNitzData.getLocalOffsetMillis() != previousUtcOffset)
-                    || (newNitzData.isDst() != previousIsDst)) {
-                // We got the time before the country or the zone has changed
+                    || mLatestNitzSignal == null
+                    || offsetInfoDiffers(newNitzData, mLatestNitzSignal.mValue)) {
+                // We got the time before the country, or the zone has changed
                 // so we don't know how to identify the DST rules yet.  Save
                 // the information and hope to fix it up later.
                 mNeedCountryCodeForNitz = true;
-                mNitzData = newNitzData;
+                mLatestNitzSignal = nitzSignal;
             }
 
-            String tmpLog = "NITZ: nitzTimeSignal=" + nitzSignal
+            String tmpLog = "handleTimeZoneFromNitz: nitzSignal=" + nitzSignal
                     + " zoneId=" + zoneId
                     + " iso=" + iso + " mGotCountryCode=" + mGotCountryCode
                     + " mNeedCountryCodeForNitz=" + mNeedCountryCodeForNitz
@@ -434,19 +440,26 @@
                     setAndBroadcastNetworkSetTimeZone(zoneId);
                 }
                 mNitzTimeZoneDetectionSuccessful = true;
-                saveNitzTimeZone(zoneId);
+                mSavedTimeZoneId = zoneId;
             }
         } catch (RuntimeException ex) {
-            Rlog.e(LOG_TAG, "NITZ: Processing NITZ data " + nitzSignal + " ex=" + ex);
+            Rlog.e(LOG_TAG, "handleTimeZoneFromNitz: Processing NITZ data"
+                    + " nitzSignal=" + nitzSignal
+                    + " ex=" + ex);
         }
     }
 
-    private void handleTimeFromNitz(TimeStampedValue<NitzData> nitzTimeSignal) {
+    private static boolean offsetInfoDiffers(NitzData one, NitzData two) {
+        return one.getLocalOffsetMillis() != two.getLocalOffsetMillis()
+                || one.isDst() != two.isDst();
+    }
+
+    private void handleTimeFromNitz(TimeStampedValue<NitzData> nitzSignal) {
         try {
             boolean ignoreNitz = mDeviceState.getIgnoreNitz();
             if (ignoreNitz) {
                 Rlog.d(LOG_TAG,
-                        "setTimeFromNitz: Not setting clock because gsm.ignore-nitz is set");
+                        "handleTimeFromNitz: Not setting clock because gsm.ignore-nitz is set");
                 return;
             }
 
@@ -456,25 +469,25 @@
                 mWakeLock.acquire();
 
                 // Validate the nitzTimeSignal to reject obviously bogus elapsedRealtime values.
-                long elapsedRealtime = mDeviceState.elapsedRealtime();
-                long millisSinceNitzReceived = elapsedRealtime - nitzTimeSignal.mElapsedRealtime;
+                long elapsedRealtime = mTimeServiceHelper.elapsedRealtime();
+                long millisSinceNitzReceived = elapsedRealtime - nitzSignal.mElapsedRealtime;
                 if (millisSinceNitzReceived < 0 || millisSinceNitzReceived > Integer.MAX_VALUE) {
                     if (DBG) {
-                        Rlog.d(LOG_TAG, "setTimeFromNitz: not setting time, unexpected"
+                        Rlog.d(LOG_TAG, "handleTimeFromNitz: not setting time, unexpected"
                                 + " elapsedRealtime=" + elapsedRealtime
-                                + " nitzTimeSignal=" + nitzTimeSignal);
+                                + " nitzSignal=" + nitzSignal);
                     }
                     return;
                 }
 
                 // Adjust the NITZ time by the delay since it was received to get the time now.
                 long adjustedCurrentTimeMillis =
-                        nitzTimeSignal.mValue.getCurrentTimeInMillis() + millisSinceNitzReceived;
-                long gained = adjustedCurrentTimeMillis - mDeviceState.currentTimeMillis();
+                        nitzSignal.mValue.getCurrentTimeInMillis() + millisSinceNitzReceived;
+                long gained = adjustedCurrentTimeMillis - mTimeServiceHelper.currentTimeMillis();
 
                 if (mTimeServiceHelper.isTimeDetectionEnabled()) {
-                    String logMsg = "(setTimeFromNitz)"
-                            + " nitzTimeSignal=" + nitzTimeSignal
+                    String logMsg = "handleTimeFromNitz:"
+                            + " nitzSignal=" + nitzSignal
                             + " adjustedCurrentTimeMillis=" + adjustedCurrentTimeMillis
                             + " millisSinceNitzReceived= " + millisSinceNitzReceived
                             + " gained=" + gained;
@@ -483,8 +496,8 @@
                         logMsg += ": First update received.";
                         setAndBroadcastNetworkSetTime(logMsg, adjustedCurrentTimeMillis);
                     } else {
-                        long elapsedRealtimeSinceLastSaved =
-                                mDeviceState.elapsedRealtime() - mSavedNitzTime.mElapsedRealtime;
+                        long elapsedRealtimeSinceLastSaved = mTimeServiceHelper.elapsedRealtime()
+                                - mSavedNitzTime.mElapsedRealtime;
                         int nitzUpdateSpacing = mDeviceState.getNitzUpdateSpacingMillis();
                         int nitzUpdateDiff = mDeviceState.getNitzUpdateDiffMillis();
                         if (elapsedRealtimeSinceLastSaved > nitzUpdateSpacing
@@ -512,38 +525,36 @@
                 // Save the last NITZ time signal used so we can return to it later
                 // if auto-time detection is toggled.
                 mSavedNitzTime = new TimeStampedValue<>(
-                        adjustedCurrentTimeMillis, nitzTimeSignal.mElapsedRealtime);
+                        adjustedCurrentTimeMillis, nitzSignal.mElapsedRealtime);
             } finally {
                 mWakeLock.release();
             }
         } catch (RuntimeException ex) {
-            Rlog.e(LOG_TAG, "setTimeFromNitz: Processing NITZ data " + nitzTimeSignal
+            Rlog.e(LOG_TAG, "handleTimeFromNitz: Processing NITZ data"
+                    + " nitzSignal=" + nitzSignal
                     + " ex=" + ex);
         }
     }
 
-    private void saveNitzTimeZone(String zoneId) {
-        mSavedTimeZoneId = zoneId;
-    }
-
     private void setAndBroadcastNetworkSetTimeZone(String zoneId) {
         if (DBG) {
-            Rlog.d(LOG_TAG, "setAndBroadcastNetworkSetTimeZone: setTimeZone=" + zoneId);
+            Rlog.d(LOG_TAG, "setAndBroadcastNetworkSetTimeZone: zoneId=" + zoneId);
         }
         mTimeServiceHelper.setDeviceTimeZone(zoneId);
         if (DBG) {
             Rlog.d(LOG_TAG,
-                    "setAndBroadcastNetworkSetTimeZone: call alarm.setTimeZone and broadcast"
+                    "setAndBroadcastNetworkSetTimeZone: called setDeviceTimeZone()"
                             + " zoneId=" + zoneId);
         }
     }
 
     private void setAndBroadcastNetworkSetTime(String msg, long time) {
         if (!mWakeLock.isHeld()) {
-            Rlog.w(LOG_TAG, "Wake lock not held while setting device time (msg=" + msg + ")");
+            Rlog.w(LOG_TAG, "setAndBroadcastNetworkSetTime: Wake lock not held while setting device"
+                    + " time (msg=" + msg + ")");
         }
 
-        msg = "setAndBroadcastNetworkSetTime: [setting time to time=" + time + "]:" + msg;
+        msg = "setAndBroadcastNetworkSetTime: [Setting time to time=" + time + "]:" + msg;
         if (DBG) {
             Rlog.d(LOG_TAG, msg);
         }
@@ -554,15 +565,17 @@
 
     private void handleAutoTimeEnabled() {
         if (DBG) {
-            Rlog.d(LOG_TAG, "Reverting to NITZ Time: mSavedTime=" + mSavedNitzTime);
+            Rlog.d(LOG_TAG, "handleAutoTimeEnabled: Reverting to NITZ Time:"
+                    + " mSavedNitzTime=" + mSavedNitzTime);
         }
         if (mSavedNitzTime != null) {
             try {
                 // Acquire the wakelock as we're reading the elapsed realtime clock here.
                 mWakeLock.acquire();
 
-                long elapsedRealtime = mDeviceState.elapsedRealtime();
-                String msg = "Reverting to NITZ time, elapsedRealtime=" + elapsedRealtime
+                long elapsedRealtime = mTimeServiceHelper.elapsedRealtime();
+                String msg = "mSavedNitzTime: Reverting to NITZ time"
+                        + " elapsedRealtime=" + elapsedRealtime
                         + " mSavedNitzTime=" + mSavedNitzTime;
                 long adjustedCurrentTimeMillis =
                         mSavedNitzTime.mValue + (elapsedRealtime - mSavedNitzTime.mElapsedRealtime);
@@ -574,7 +587,8 @@
     }
 
     private void handleAutoTimeZoneEnabled() {
-        String tmpLog = "Reverting to NITZ TimeZone: tz=" + mSavedTimeZoneId;
+        String tmpLog = "handleAutoTimeZoneEnabled: Reverting to NITZ TimeZone:"
+                + " mSavedTimeZoneId=" + mSavedTimeZoneId;
         if (DBG) {
             Rlog.d(LOG_TAG, tmpLog);
         }
@@ -598,9 +612,9 @@
 
         // Time Zone Detection State
         pw.println(" mNeedCountryCodeForNitz=" + mNeedCountryCodeForNitz);
-        pw.println(" mNitzData=" + mNitzData);
+        pw.println(" mLatestNitzSignal=" + mLatestNitzSignal);
         pw.println(" mGotCountryCode=" + mGotCountryCode);
-        pw.println(" mSavedTimeZone=" + mSavedTimeZoneId);
+        pw.println(" mSavedTimeZoneId=" + mSavedTimeZoneId);
         pw.println(" mNitzTimeZoneDetectionSuccessful=" + mNitzTimeZoneDetectionSuccessful);
 
         // Miscellaneous
@@ -630,22 +644,22 @@
      * @param iso Country code from network MCC
      */
     private void updateTimeZoneByNetworkCountryCode(String iso) {
-        String zoneId = mTimeZoneLookupHelper.guessZoneIdByCountry(
-                iso, mDeviceState.currentTimeMillis());
-        if (zoneId != null) {
+        CountryResult lookupResult = mTimeZoneLookupHelper.lookupByCountry(
+                iso, mTimeServiceHelper.currentTimeMillis());
+        if (lookupResult != null && lookupResult.allZonesHaveSameOffset) {
+            String logMsg = "updateTimeZoneByNetworkCountryCode: set time"
+                    + " lookupResult=" + lookupResult
+                    + " iso=" + iso;
             if (DBG) {
-                Rlog.d(LOG_TAG, "updateTimeZoneByNetworkCountryCode: set time zone=" + zoneId
-                        + " iso=" + iso);
+                Rlog.d(LOG_TAG, logMsg);
             }
-
-            mTimeZoneLog.log("updateTimeZoneByNetworkCountryCode: set time zone=" + zoneId
-                    + " iso=" + iso);
-            setAndBroadcastNetworkSetTimeZone(zoneId);
+            mTimeZoneLog.log(logMsg);
+            setAndBroadcastNetworkSetTimeZone(lookupResult.zoneId);
         } else {
             if (DBG) {
-                Rlog.d(LOG_TAG,
-                        "updateTimeZoneByNetworkCountryCode: no good zone for iso-cc='" + iso
-                                + "', do nothing");
+                Rlog.d(LOG_TAG, "updateTimeZoneByNetworkCountryCode: no good zone for"
+                        + " iso=" + iso
+                        + " lookupResult=" + lookupResult);
             }
         }
     }
@@ -661,7 +675,7 @@
      * Returns the last NITZ data that was cached.
      */
     public NitzData getCachedNitzData() {
-        return mNitzData;
+        return mLatestNitzSignal != null ? mLatestNitzSignal.mValue : null;
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 7e8621a..662f8e1 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -125,10 +125,6 @@
                 } else if (intent.getAction().equals(ImsManager.ACTION_IMS_SERVICE_DOWN)) {
                     mImsServiceReady = false;
                     updateImsPhone();
-                } else if (intent.getAction().equals(ImsConfig.ACTION_IMS_CONFIG_CHANGED)) {
-                    int item = intent.getIntExtra(ImsConfig.EXTRA_CHANGED_ITEM, -1);
-                    String value = intent.getStringExtra(ImsConfig.EXTRA_NEW_VALUE);
-                    ImsManager.onProvisionedValueChanged(context, item, value);
                 }
             }
         }
@@ -562,7 +558,6 @@
                 filter.addAction(ImsManager.ACTION_IMS_SERVICE_UP);
                 filter.addAction(ImsManager.ACTION_IMS_SERVICE_DOWN);
             }
-            filter.addAction(ImsConfig.ACTION_IMS_CONFIG_CHANGED);
             mContext.registerReceiver(mImsIntentReceiver, filter);
 
             // Monitor IMS service - but first poll to see if already up (could miss
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index c857740..5b6dffb 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -150,11 +150,17 @@
                    where as in single SIM mode only instance. isMultiSimEnabled() function checks
                    whether it is single SIM or multi SIM mode */
                 int numPhones = TelephonyManager.getDefault().getPhoneCount();
-                // Start ImsResolver and bind to ImsServices.
+                // Return whether or not the device should use dynamic binding or the static
+                // implementation (deprecated)
+                boolean isDynamicBinding = sContext.getResources().getBoolean(
+                        com.android.internal.R.bool.config_dynamic_bind_ims);
+                // Get the package name of the default IMS implementation.
                 String defaultImsPackage = sContext.getResources().getString(
                         com.android.internal.R.string.config_ims_package);
+                // Start ImsResolver and bind to ImsServices.
                 Rlog.i(LOG_TAG, "ImsResolver: defaultImsPackage: " + defaultImsPackage);
-                sImsResolver = new ImsResolver(sContext, defaultImsPackage, numPhones);
+                sImsResolver = new ImsResolver(sContext, defaultImsPackage, numPhones,
+                        isDynamicBinding);
                 sImsResolver.populateCacheAndStartBind();
 
                 int[] networkModes = new int[numPhones];
diff --git a/src/java/com/android/internal/telephony/PhoneInternalInterface.java b/src/java/com/android/internal/telephony/PhoneInternalInterface.java
index c64c090..af393f8 100644
--- a/src/java/com/android/internal/telephony/PhoneInternalInterface.java
+++ b/src/java/com/android/internal/telephony/PhoneInternalInterface.java
@@ -736,19 +736,6 @@
     boolean getMute();
 
     /**
-     * Get the current active Data Call list
-     *
-     * @param response <strong>On success</strong>, "response" bytes is
-     * made available as:
-     * (String[])(((AsyncResult)response.obj).result).
-     * <strong>On failure</strong>,
-     * (((AsyncResult)response.obj).result) == null and
-     * (((AsyncResult)response.obj).exception) being an instance of
-     * com.android.internal.telephony.gsm.CommandException
-     */
-    void getDataCallList(Message response);
-
-    /**
      * Update the ServiceState CellLocation for current network registration.
      */
     void updateServiceLocation();
diff --git a/src/java/com/android/internal/telephony/PhoneSwitcher.java b/src/java/com/android/internal/telephony/PhoneSwitcher.java
index cd28b2b..1c6e023 100644
--- a/src/java/com/android/internal/telephony/PhoneSwitcher.java
+++ b/src/java/com/android/internal/telephony/PhoneSwitcher.java
@@ -124,8 +124,7 @@
         mCommandsInterfaces = cis;
 
         try {
-            tr.addOnSubscriptionsChangedListener(context.getOpPackageName(),
-                    mSubscriptionsChangedListener);
+            tr.addOnSubscriptionsChangedListener("PhoneSwitcher", mSubscriptionsChangedListener);
         } catch (RemoteException e) {
         }
 
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index ce4743f..a190cde 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -48,15 +48,12 @@
 import android.hardware.radio.V1_0.RadioResponseType;
 import android.hardware.radio.V1_0.ResetNvType;
 import android.hardware.radio.V1_0.SelectUiccSub;
-import android.hardware.radio.V1_0.SetupDataCallResult;
 import android.hardware.radio.V1_0.SimApdu;
 import android.hardware.radio.V1_0.SmsWriteArgs;
 import android.hardware.radio.V1_0.UusInfo;
 import android.net.ConnectivityManager;
 import android.net.KeepalivePacketData;
-import android.net.LinkAddress;
 import android.net.LinkProperties;
-import android.net.NetworkUtils;
 import android.os.AsyncResult;
 import android.os.Build;
 import android.os.Handler;
@@ -87,7 +84,6 @@
 import android.telephony.SmsManager;
 import android.telephony.TelephonyHistogram;
 import android.telephony.TelephonyManager;
-import android.telephony.data.DataCallResponse;
 import android.telephony.data.DataProfile;
 import android.telephony.data.DataService;
 import android.text.TextUtils;
@@ -288,10 +284,6 @@
                             " mRadioProxyCookie = " + mRadioProxyCookie.get());
                     if ((long) msg.obj == mRadioProxyCookie.get()) {
                         resetProxyAndRequestList();
-
-                        // todo: rild should be back up since message was sent with a delay. this is
-                        // a hack.
-                        getRadioProxy(null);
                     }
                     break;
             }
@@ -326,11 +318,7 @@
         public void serviceDied(long cookie) {
             // Deal with service going away
             riljLog("serviceDied");
-            // todo: temp hack to send delayed message so that rild is back up by then
-            //mRilHandler.sendMessage(mRilHandler.obtainMessage(EVENT_RADIO_PROXY_DEAD, cookie));
-            mRilHandler.sendMessageDelayed(
-                    mRilHandler.obtainMessage(EVENT_RADIO_PROXY_DEAD, cookie),
-                    IRADIO_GET_SERVICE_DELAY_MILLIS);
+            mRilHandler.sendMessage(mRilHandler.obtainMessage(EVENT_RADIO_PROXY_DEAD, cookie));
         }
     }
 
@@ -346,9 +334,7 @@
         // Clear request list on close
         clearRequestList(RADIO_NOT_AVAILABLE, false);
 
-        // todo: need to get service right away so setResponseFunctions() can be called for
-        // unsolicited indications. getService() is not a blocking call, so it doesn't help to call
-        // it here. Current hack is to call getService() on death notification after a delay.
+        getRadioProxy(null);
     }
 
     /** Returns a {@link IRadio} instance or null if the service is not available. */
@@ -369,7 +355,8 @@
         }
 
         try {
-            mRadioProxy = IRadio.getService(HIDL_SERVICE_NAME[mPhoneId == null ? 0 : mPhoneId]);
+            mRadioProxy = IRadio.getService(HIDL_SERVICE_NAME[mPhoneId == null ? 0 : mPhoneId],
+                    true);
             if (mRadioProxy != null) {
                 mRadioProxy.linkToDeath(mRadioProxyDeathRecipient,
                         mRadioProxyCookie.incrementAndGet());
@@ -383,17 +370,13 @@
         }
 
         if (mRadioProxy == null) {
+            // getService() is a blocking call, so this should never happen
+            riljLoge("getRadioProxy: mRadioProxy == null");
             if (result != null) {
                 AsyncResult.forMessage(result, null,
                         CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
                 result.sendToTarget();
             }
-
-            // if service is not up, treat it like death notification to try to get service again
-            mRilHandler.sendMessageDelayed(
-                    mRilHandler.obtainMessage(EVENT_RADIO_PROXY_DEAD,
-                            mRadioProxyCookie.incrementAndGet()),
-                    IRADIO_GET_SERVICE_DELAY_MILLIS);
         }
 
         return mRadioProxy;
@@ -478,13 +461,6 @@
     private void handleRadioProxyExceptionForRR(RILRequest rr, String caller, Exception e) {
         riljLoge(caller + ": " + e);
         resetProxyAndRequestList();
-
-        // service most likely died, handle exception like death notification to try to get service
-        // again
-        mRilHandler.sendMessageDelayed(
-                mRilHandler.obtainMessage(EVENT_RADIO_PROXY_DEAD,
-                        mRadioProxyCookie.incrementAndGet()),
-                IRADIO_GET_SERVICE_DELAY_MILLIS);
     }
 
     private String convertNullToEmptyString(String string) {
@@ -1136,103 +1112,13 @@
         return -1;
     }
 
-    /**
-     * Convert SetupDataCallResult defined in types.hal into DataCallResponse
-     * @param dcResult setup data call result
-     * @return converted DataCallResponse object
-     */
-    static DataCallResponse convertDataCallResult(SetupDataCallResult dcResult) {
-
-        // Process address
-        String[] addresses = null;
-        if (!TextUtils.isEmpty(dcResult.addresses)) {
-            addresses = dcResult.addresses.split("\\s+");
-        }
-
-        List<LinkAddress> laList = new ArrayList<>();
-        if (addresses != null) {
-            for (String address : addresses) {
-                address = address.trim();
-                if (address.isEmpty()) continue;
-
-                try {
-                    LinkAddress la;
-                    // Check if the address contains prefix length. If yes, LinkAddress
-                    // can parse that.
-                    if (address.split("/").length == 2) {
-                        la = new LinkAddress(address);
-                    } else {
-                        InetAddress ia = NetworkUtils.numericToInetAddress(address);
-                        la = new LinkAddress(ia, (ia instanceof Inet4Address) ? 32 : 128);
-                    }
-
-                    laList.add(la);
-                } catch (IllegalArgumentException e) {
-                    Rlog.e(RILJ_LOG_TAG, "Unknown address: " + address + ", " + e);
-                }
-            }
-        }
-
-        // Process dns
-        String[] dnses = null;
-        if (!TextUtils.isEmpty(dcResult.dnses)) {
-            dnses = dcResult.dnses.split("\\s+");
-        }
-
-        List<InetAddress> dnsList = new ArrayList<>();
-        if (dnses != null) {
-            for (String dns : dnses) {
-                dns = dns.trim();
-                InetAddress ia;
-                try {
-                    ia = NetworkUtils.numericToInetAddress(dns);
-                    dnsList.add(ia);
-                } catch (IllegalArgumentException e) {
-                    Rlog.e(RILJ_LOG_TAG, "Unknown dns: " + dns + ", exception = " + e);
-                }
-            }
-        }
-
-        // Process gateway
-        String[] gateways = null;
-        if (!TextUtils.isEmpty(dcResult.gateways)) {
-            gateways = dcResult.gateways.split("\\s+");
-        }
-
-        List<InetAddress> gatewayList = new ArrayList<>();
-        if (gateways != null) {
-            for (String gateway : gateways) {
-                gateway = gateway.trim();
-                InetAddress ia;
-                try {
-                    ia = NetworkUtils.numericToInetAddress(gateway);
-                    gatewayList.add(ia);
-                } catch (IllegalArgumentException e) {
-                    Rlog.e(RILJ_LOG_TAG, "Unknown gateway: " + gateway + ", exception = " + e);
-                }
-            }
-        }
-
-        return new DataCallResponse(dcResult.status,
-                dcResult.suggestedRetryTime,
-                dcResult.cid,
-                dcResult.active,
-                dcResult.type,
-                dcResult.ifname,
-                laList,
-                dnsList,
-                gatewayList,
-                new ArrayList<>(Arrays.asList(dcResult.pcscf.trim().split("\\s+"))),
-                dcResult.mtu
-        );
-    }
-
     @Override
     public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
                               boolean allowRoaming, int reason, LinkProperties linkProperties,
                               Message result) {
 
         IRadio radioProxy = getRadioProxy(result);
+
         if (radioProxy != null) {
 
             RILRequest rr = obtainRequest(RIL_REQUEST_SETUP_DATA_CALL, result,
diff --git a/src/java/com/android/internal/telephony/RadioConfig.java b/src/java/com/android/internal/telephony/RadioConfig.java
index 914acd6..02c7177 100644
--- a/src/java/com/android/internal/telephony/RadioConfig.java
+++ b/src/java/com/android/internal/telephony/RadioConfig.java
@@ -166,7 +166,7 @@
         }
 
         try {
-            mRadioConfigProxy = IRadioConfig.getService(RADIO_CONFIG_SERVICE_NAME);
+            mRadioConfigProxy = IRadioConfig.getService(RADIO_CONFIG_SERVICE_NAME, true);
             if (mRadioConfigProxy != null) {
                 mRadioConfigProxy.linkToDeath(mServiceDeathRecipient,
                         mRadioConfigProxyCookie.incrementAndGet());
@@ -177,7 +177,7 @@
             }
         } catch (RemoteException | RuntimeException e) {
             mRadioConfigProxy = null;
-            loge("getRadioConfigProxy: RadioProxy getService/setResponseFunctions: " + e);
+            loge("getRadioConfigProxy: RadioConfigProxy getService/setResponseFunctions: " + e);
         }
 
         if (mRadioConfigProxy == null) {
diff --git a/src/java/com/android/internal/telephony/RadioIndication.java b/src/java/com/android/internal/telephony/RadioIndication.java
index 8d77982..d2ad279 100644
--- a/src/java/com/android/internal/telephony/RadioIndication.java
+++ b/src/java/com/android/internal/telephony/RadioIndication.java
@@ -89,7 +89,6 @@
 import android.telephony.PcoData;
 import android.telephony.SignalStrength;
 import android.telephony.SmsMessage;
-import android.telephony.data.DataCallResponse;
 
 import com.android.internal.telephony.cdma.CdmaCallWaitingNotification;
 import com.android.internal.telephony.cdma.CdmaInformationRecords;
@@ -254,16 +253,10 @@
     public void dataCallListChanged(int indicationType, ArrayList<SetupDataCallResult> dcList) {
         mRil.processIndication(indicationType);
 
-        ArrayList<DataCallResponse> response = new ArrayList<>();
-
-        for (SetupDataCallResult dcResult : dcList) {
-            response.add(RIL.convertDataCallResult(dcResult));
-        }
-
-        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_DATA_CALL_LIST_CHANGED, response);
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_DATA_CALL_LIST_CHANGED, dcList);
 
         mRil.mDataCallListChangedRegistrants.notifyRegistrants(
-                new AsyncResult(null, response, null));
+                new AsyncResult(null, dcList, null));
     }
 
     public void suppSvcNotify(int indicationType, SuppSvcNotification suppSvcNotification) {
diff --git a/src/java/com/android/internal/telephony/RadioResponse.java b/src/java/com/android/internal/telephony/RadioResponse.java
index b636917..d608bda 100644
--- a/src/java/com/android/internal/telephony/RadioResponse.java
+++ b/src/java/com/android/internal/telephony/RadioResponse.java
@@ -45,7 +45,6 @@
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
-import android.telephony.data.DataCallResponse;
 import android.text.TextUtils;
 
 import com.android.internal.telephony.dataconnection.KeepaliveStatus;
@@ -1672,11 +1671,10 @@
         RILRequest rr = mRil.processResponse(responseInfo);
 
         if (rr != null) {
-            DataCallResponse ret = RIL.convertDataCallResult(setupDataCallResult);
             if (responseInfo.error == RadioError.NONE) {
-                sendMessageResponse(rr.mResult, ret);
+                sendMessageResponse(rr.mResult, setupDataCallResult);
             }
-            mRil.processResponseDone(rr, responseInfo, ret);
+            mRil.processResponseDone(rr, responseInfo, setupDataCallResult);
         }
     }
 
@@ -1767,14 +1765,10 @@
         RILRequest rr = mRil.processResponse(responseInfo);
 
         if (rr != null) {
-            ArrayList<DataCallResponse> dcResponseList = new ArrayList<>();
-            for (SetupDataCallResult dcResult : dataCallResultList) {
-                dcResponseList.add(RIL.convertDataCallResult(dcResult));
-            }
             if (responseInfo.error == RadioError.NONE) {
-                sendMessageResponse(rr.mResult, dcResponseList);
+                sendMessageResponse(rr.mResult, dataCallResultList);
             }
-            mRil.processResponseDone(rr, responseInfo, dcResponseList);
+            mRil.processResponseDone(rr, responseInfo, dataCallResultList);
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index 7c5842f..2b6acdb 100644
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -33,9 +33,6 @@
 import android.content.SharedPreferences;
 import android.content.res.Resources;
 import android.hardware.radio.V1_0.CellInfoType;
-import android.hardware.radio.V1_0.DataRegStateResult;
-import android.hardware.radio.V1_0.RegState;
-import android.hardware.radio.V1_0.VoiceRegStateResult;
 import android.os.AsyncResult;
 import android.os.BaseBundle;
 import android.os.Build;
@@ -50,33 +47,42 @@
 import android.os.WorkSource;
 import android.preference.PreferenceManager;
 import android.provider.Settings;
+import android.telephony.AccessNetworkConstants;
 import android.telephony.CarrierConfigManager;
+import android.telephony.CellIdentity;
+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.CellInfoGsm;
 import android.telephony.CellInfoLte;
 import android.telephony.CellInfoWcdma;
 import android.telephony.CellLocation;
+import android.telephony.DataSpecificRegistrationStates;
+import android.telephony.NetworkRegistrationState;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
 import android.telephony.TelephonyManager;
+import android.telephony.VoiceSpecificRegistrationStates;
 import android.telephony.cdma.CdmaCellLocation;
 import android.telephony.gsm.GsmCellLocation;
 import android.text.TextUtils;
 import android.util.EventLog;
 import android.util.LocalLog;
 import android.util.Pair;
+import android.util.SparseArray;
 import android.util.TimeUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
 import com.android.internal.telephony.cdma.EriInfo;
 import com.android.internal.telephony.dataconnection.DcTracker;
+import com.android.internal.telephony.dataconnection.TransportManager;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
 import com.android.internal.telephony.uicc.IccRecords;
@@ -452,6 +458,9 @@
     private String mRegistrationDeniedReason;
     private String mCurrentCarrier = null;
 
+    private final TransportManager mTransportManager;
+    private final SparseArray<NetworkRegistrationManager> mRegStateManagers = new SparseArray<>();
+
     /* list of LTE EARFCNs (E-UTRA Absolute Radio Frequency Channel Number,
      * Reference: 3GPP TS 36.104 5.4.3)
      * inclusive ranges for which the lte rsrp boost is applied */
@@ -482,9 +491,17 @@
                 .addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
         mRestrictedState = new RestrictedState();
 
+        mTransportManager = new TransportManager();
+
+        for (int transportType : mTransportManager.getAvailableTransports()) {
+            mRegStateManagers.append(transportType, new NetworkRegistrationManager(
+                    transportType, phone));
+            mRegStateManagers.get(transportType).registerForNetworkRegistrationStateChanged(
+                    this, EVENT_NETWORK_STATE_CHANGED, null);
+        }
+
         mCi.registerForImsNetworkStateChanged(this, EVENT_IMS_STATE_CHANGED, null);
         mCi.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null);
-        mCi.registerForNetworkStateChanged(this, EVENT_NETWORK_STATE_CHANGED, null);
         mCi.setOnNITZTime(this, EVENT_NITZ_TIME, null);
 
         mCr = phone.getContext().getContentResolver();
@@ -854,55 +871,37 @@
         }
     }
 
-    private void processCellLocationInfo(CellLocation cellLocation,
-                                         VoiceRegStateResult voiceRegStateResult) {
+    private void processCellLocationInfo(CellLocation cellLocation, CellIdentity cellIdentity) {
         if (mPhone.isPhoneTypeGsm()) {
             int psc = -1;
             int cid = -1;
             int lac = -1;
-            switch(voiceRegStateResult.cellIdentity.cellInfoType) {
-                case CellInfoType.GSM: {
-                    if (voiceRegStateResult.cellIdentity.cellIdentityGsm.size() == 1) {
-                        android.hardware.radio.V1_0.CellIdentityGsm cellIdentityGsm =
-                                voiceRegStateResult.cellIdentity.cellIdentityGsm.get(0);
-                        cid = cellIdentityGsm.cid;
-                        lac = cellIdentityGsm.lac;
+            if (cellIdentity != null) {
+                switch (cellIdentity.getType()) {
+                    case CellInfoType.GSM: {
+                        cid = ((CellIdentityGsm) cellIdentity).getCid();
+                        lac = ((CellIdentityGsm) cellIdentity).getLac();
+                        break;
                     }
-                    break;
-                }
-                case CellInfoType.WCDMA: {
-                    if (voiceRegStateResult.cellIdentity.cellIdentityWcdma.size() == 1) {
-                        android.hardware.radio.V1_0.CellIdentityWcdma cellIdentityWcdma =
-                                voiceRegStateResult.cellIdentity.cellIdentityWcdma.get(0);
-                        cid = cellIdentityWcdma.cid;
-                        lac = cellIdentityWcdma.lac;
-                        psc = cellIdentityWcdma.psc;
+                    case CellInfoType.WCDMA: {
+                        cid = ((CellIdentityWcdma) cellIdentity).getCid();
+                        lac = ((CellIdentityWcdma) cellIdentity).getLac();
+                        psc = ((CellIdentityWcdma) cellIdentity).getPsc();
+                        break;
                     }
-                    break;
-                }
-                case CellInfoType.TD_SCDMA: {
-                    if (voiceRegStateResult.cellIdentity.cellIdentityTdscdma.size() == 1) {
-                        android.hardware.radio.V1_0.CellIdentityTdscdma
-                                cellIdentityTdscdma =
-                                voiceRegStateResult.cellIdentity.cellIdentityTdscdma.get(0);
-                        cid = cellIdentityTdscdma.cid;
-                        lac = cellIdentityTdscdma.lac;
+                    case CellInfoType.TD_SCDMA: {
+                        cid = ((CellIdentityTdscdma) cellIdentity).getCid();
+                        lac = ((CellIdentityTdscdma) cellIdentity).getLac();
+                        break;
                     }
-                    break;
-                }
-                case CellInfoType.LTE: {
-                    if (voiceRegStateResult.cellIdentity.cellIdentityLte.size() == 1) {
-                        android.hardware.radio.V1_0.CellIdentityLte cellIdentityLte =
-                                voiceRegStateResult.cellIdentity.cellIdentityLte.get(0);
-                        cid = cellIdentityLte.ci;
-
-                        /* Continuing the historical behaviour of using tac as lac. */
-                        lac = cellIdentityLte.tac;
+                    case CellInfoType.LTE: {
+                        cid = ((CellIdentityLte) cellIdentity).getCi();
+                        lac = ((CellIdentityLte) cellIdentity).getTac();
+                        break;
                     }
-                    break;
-                }
-                default: {
-                    break;
+                    default: {
+                        break;
+                    }
                 }
             }
             // LAC and CID are -1 if not avail
@@ -915,21 +914,19 @@
             int systemId = 0;
             int networkId = 0;
 
-            switch(voiceRegStateResult.cellIdentity.cellInfoType) {
-                case CellInfoType.CDMA: {
-                    if (voiceRegStateResult.cellIdentity.cellIdentityCdma.size() == 1) {
-                        android.hardware.radio.V1_0.CellIdentityCdma cellIdentityCdma =
-                                voiceRegStateResult.cellIdentity.cellIdentityCdma.get(0);
-                        baseStationId = cellIdentityCdma.baseStationId;
-                        baseStationLatitude = cellIdentityCdma.latitude;
-                        baseStationLongitude = cellIdentityCdma.longitude;
-                        systemId = cellIdentityCdma.systemId;
-                        networkId = cellIdentityCdma.networkId;
+            if (cellIdentity != null) {
+                switch (cellIdentity.getType()) {
+                    case CellInfoType.CDMA: {
+                        baseStationId = ((CellIdentityCdma) cellIdentity).getBasestationId();
+                        baseStationLatitude = ((CellIdentityCdma) cellIdentity).getLatitude();
+                        baseStationLongitude = ((CellIdentityCdma) cellIdentity).getLongitude();
+                        systemId = ((CellIdentityCdma) cellIdentity).getSystemId();
+                        networkId = ((CellIdentityCdma) cellIdentity).getNetworkId();
+                        break;
                     }
-                    break;
-                }
-                default: {
-                    break;
+                    default: {
+                        break;
+                    }
                 }
             }
 
@@ -945,19 +942,17 @@
         }
     }
 
-    private int getLteEarfcn(DataRegStateResult dataRegStateResult) {
+    private int getLteEarfcn(CellIdentity cellIdentity) {
         int lteEarfcn = INVALID_LTE_EARFCN;
-        switch(dataRegStateResult.cellIdentity.cellInfoType) {
-            case CellInfoType.LTE: {
-                if (dataRegStateResult.cellIdentity.cellIdentityLte.size() == 1) {
-                    android.hardware.radio.V1_0.CellIdentityLte cellIdentityLte =
-                            dataRegStateResult.cellIdentity.cellIdentityLte.get(0);
-                    lteEarfcn = cellIdentityLte.earfcn;
+        if (cellIdentity != null) {
+            switch (cellIdentity.getType()) {
+                case CellInfoType.LTE: {
+                    lteEarfcn = ((CellIdentityLte) cellIdentity).getEarfcn();
+                    break;
                 }
-                break;
-            }
-            default: {
-                break;
+                default: {
+                    break;
+                }
             }
         }
 
@@ -1103,7 +1098,9 @@
             case EVENT_GET_LOC_DONE:
                 ar = (AsyncResult) msg.obj;
                 if (ar.exception == null) {
-                    processCellLocationInfo(mCellLoc, (VoiceRegStateResult) ar.result);
+                    CellIdentity cellIdentity = ((NetworkRegistrationState) ar.result)
+                            .getCellIdentity();
+                    processCellLocationInfo(mCellLoc, cellIdentity);
                     mPhone.notifyLocationChanged();
                 }
 
@@ -1176,7 +1173,9 @@
                 ar = (AsyncResult) msg.obj;
 
                 if (ar.exception == null) {
-                    mCi.getVoiceRegistrationState(obtainMessage(EVENT_GET_LOC_DONE, null));
+                    mRegStateManagers.get(AccessNetworkConstants.TransportType.WWAN)
+                            .getNetworkRegistrationState(NetworkRegistrationState.DOMAIN_CS,
+                            obtainMessage(EVENT_GET_LOC_DONE, null));
                 }
                 break;
 
@@ -1721,47 +1720,25 @@
         return cdmaRoaming && !isSameOperatorNameFromSimAndSS(s);
     }
 
-    private int getRegStateFromHalRegState(int regState) {
-        switch (regState) {
-            case RegState.NOT_REG_MT_NOT_SEARCHING_OP:
-                return ServiceState.RIL_REG_STATE_NOT_REG;
-            case RegState.REG_HOME:
-                return ServiceState.RIL_REG_STATE_HOME;
-            case RegState.NOT_REG_MT_SEARCHING_OP:
-                return ServiceState.RIL_REG_STATE_SEARCHING;
-            case RegState.REG_DENIED:
-                return ServiceState.RIL_REG_STATE_DENIED;
-            case RegState.UNKNOWN:
-                return ServiceState.RIL_REG_STATE_UNKNOWN;
-            case RegState.REG_ROAMING:
-                return ServiceState.RIL_REG_STATE_ROAMING;
-            case RegState.NOT_REG_MT_NOT_SEARCHING_OP_EM:
-                return ServiceState.RIL_REG_STATE_NOT_REG_EMERGENCY_CALL_ENABLED;
-            case RegState.NOT_REG_MT_SEARCHING_OP_EM:
-                return ServiceState.RIL_REG_STATE_SEARCHING_EMERGENCY_CALL_ENABLED;
-            case RegState.REG_DENIED_EM:
-                return ServiceState.RIL_REG_STATE_DENIED_EMERGENCY_CALL_ENABLED;
-            case RegState.UNKNOWN_EM:
-                return ServiceState.RIL_REG_STATE_UNKNOWN_EMERGENCY_CALL_ENABLED;
-            default:
-                return ServiceState.REGISTRATION_STATE_NOT_REGISTERED_AND_NOT_SEARCHING;
-        }
-    }
-
     void handlePollStateResultMessage(int what, AsyncResult ar) {
         int ints[];
         switch (what) {
             case EVENT_POLL_STATE_REGISTRATION: {
-                VoiceRegStateResult voiceRegStateResult = (VoiceRegStateResult) ar.result;
-                int registrationState = getRegStateFromHalRegState(voiceRegStateResult.regState);
-                int cssIndicator = voiceRegStateResult.cssSupported ? 1 : 0;
+                NetworkRegistrationState regStates = (NetworkRegistrationState) ar.result;
+                VoiceSpecificRegistrationStates voiceSpecificStates =
+                        regStates.getVoiceSpecificStates();
+
+                int registrationState = regStates.getRegState();
+                int cssIndicator = voiceSpecificStates.cssSupported ? 1 : 0;
+                int newVoiceRat = ServiceState.networkTypeToRilRadioTechnology(
+                        regStates.getAccessNetworkTechnology());
 
                 mNewSS.setVoiceRegState(regCodeToServiceState(registrationState));
                 mNewSS.setCssIndicator(cssIndicator);
-                mNewSS.setRilVoiceRadioTechnology(voiceRegStateResult.rat);
+                mNewSS.setRilVoiceRadioTechnology(newVoiceRat);
 
                 //Denial reason if registrationState = 3
-                int reasonForDenial = voiceRegStateResult.reasonForDenial;
+                int reasonForDenial = regStates.getReasonForDenial();
                 if (mPhone.isPhoneTypeGsm()) {
 
                     mGsmRoaming = regCodeIsRoaming(registrationState);
@@ -1769,27 +1746,15 @@
 
                     boolean isVoiceCapable = mPhone.getContext().getResources()
                             .getBoolean(com.android.internal.R.bool.config_voice_capable);
-                    if (((registrationState
-                            == ServiceState.RIL_REG_STATE_DENIED_EMERGENCY_CALL_ENABLED)
-                            || (registrationState
-                            == ServiceState.RIL_REG_STATE_NOT_REG_EMERGENCY_CALL_ENABLED)
-                            || (registrationState
-                            == ServiceState.RIL_REG_STATE_SEARCHING_EMERGENCY_CALL_ENABLED)
-                            || (registrationState
-                            == ServiceState.RIL_REG_STATE_UNKNOWN_EMERGENCY_CALL_ENABLED))
-                            && isVoiceCapable) {
-                        mEmergencyOnly = true;
-                    } else {
-                        mEmergencyOnly = false;
-                    }
+                    mEmergencyOnly = regStates.isEmergencyEnabled();
                 } else {
-                    int roamingIndicator = voiceRegStateResult.roamingIndicator;
+                    int roamingIndicator = voiceSpecificStates.roamingIndicator;
 
                     //Indicates if current system is in PR
-                    int systemIsInPrl = voiceRegStateResult.systemIsInPrl;
+                    int systemIsInPrl = voiceSpecificStates.systemIsInPrl;
 
                     //Is default roaming indicator from PRL
-                    int defaultRoamingIndicator = voiceRegStateResult.defaultRoamingIndicator;
+                    int defaultRoamingIndicator = voiceSpecificStates.defaultRoamingIndicator;
 
                     mRegistrationState = registrationState;
                     // When registration state is roaming and TSB58
@@ -1806,12 +1771,10 @@
 
                     int systemId = 0;
                     int networkId = 0;
-                    if (voiceRegStateResult.cellIdentity.cellInfoType == CellInfoType.CDMA
-                            && voiceRegStateResult.cellIdentity.cellIdentityCdma.size() == 1) {
-                        android.hardware.radio.V1_0.CellIdentityCdma cellIdentityCdma =
-                                voiceRegStateResult.cellIdentity.cellIdentityCdma.get(0);
-                        systemId = cellIdentityCdma.systemId;
-                        networkId = cellIdentityCdma.networkId;
+                    CellIdentity cellIdentity = regStates.getCellIdentity();
+                    if (cellIdentity != null && cellIdentity.getType() == CellInfoType.CDMA) {
+                        systemId = ((CellIdentityCdma) cellIdentity).getSystemId();
+                        networkId = ((CellIdentityCdma) cellIdentity).getNetworkId();
                     }
                     mNewSS.setSystemAndNetworkId(systemId, networkId);
 
@@ -1828,34 +1791,37 @@
                     }
                 }
 
-                processCellLocationInfo(mNewCellLoc, voiceRegStateResult);
+                processCellLocationInfo(mNewCellLoc, regStates.getCellIdentity());
 
                 if (DBG) {
                     log("handlPollVoiceRegResultMessage: regState=" + registrationState
-                            + " radioTechnology=" + voiceRegStateResult.rat);
+                            + " radioTechnology=" + newVoiceRat);
                 }
                 break;
             }
 
             case EVENT_POLL_STATE_GPRS: {
-                DataRegStateResult dataRegStateResult = (DataRegStateResult) ar.result;
-                int regState = getRegStateFromHalRegState(dataRegStateResult.regState);
-                int dataRegState = regCodeToServiceState(regState);
-                int newDataRat = dataRegStateResult.rat;
-                mNewSS.setDataRegState(dataRegState);
+                NetworkRegistrationState regStates = (NetworkRegistrationState) ar.result;
+                DataSpecificRegistrationStates dataSpecificStates =
+                        regStates.getDataSpecificStates();
+                int regState = regStates.getRegState();
+                int serviceState = regCodeToServiceState(regState);
+                int newDataRat = ServiceState.networkTypeToRilRadioTechnology(
+                        regStates.getAccessNetworkTechnology());
+                mNewSS.setDataRegState(serviceState);
                 mNewSS.setRilDataRadioTechnology(newDataRat);
 
                 if (mPhone.isPhoneTypeGsm()) {
 
-                    mNewReasonDataDenied = dataRegStateResult.reasonDataDenied;
-                    mNewMaxDataCalls = dataRegStateResult.maxDataCalls;
+                    mNewReasonDataDenied = regStates.getReasonForDenial();
+                    mNewMaxDataCalls = dataSpecificStates.maxDataCalls;
                     mDataRoaming = regCodeIsRoaming(regState);
                     // Save the data roaming state reported by modem registration before resource
                     // overlay or carrier config possibly overrides it.
                     mNewSS.setDataRoamingFromRegistration(mDataRoaming);
 
                     if (DBG) {
-                        log("handlPollStateResultMessage: GsmSST setDataRegState=" + dataRegState
+                        log("handlPollStateResultMessage: GsmSST dataServiceState=" + serviceState
                                 + " regState=" + regState
                                 + " dataRadioTechnology=" + newDataRat);
                     }
@@ -1868,7 +1834,7 @@
                     mNewSS.setDataRoamingFromRegistration(isDataRoaming);
 
                     if (DBG) {
-                        log("handlPollStateResultMessage: cdma setDataRegState=" + dataRegState
+                        log("handlPollStateResultMessage: cdma dataServiceState=" + serviceState
                                 + " regState=" + regState
                                 + " dataRadioTechnology=" + newDataRat);
                     }
@@ -1896,13 +1862,13 @@
                     // overlay or carrier config possibly overrides it.
                     mNewSS.setDataRoamingFromRegistration(isDataRoaming);
                     if (DBG) {
-                        log("handlPollStateResultMessage: CdmaLteSST setDataRegState="
-                                + dataRegState + " regState=" + regState + " dataRadioTechnology="
+                        log("handlPollStateResultMessage: CdmaLteSST dataServiceState="
+                                + serviceState + " regState=" + regState + " dataRadioTechnology="
                                 + newDataRat);
                     }
                 }
 
-                updateServiceStateLteEarfcnBoost(mNewSS, getLteEarfcn(dataRegStateResult));
+                updateServiceStateLteEarfcnBoost(mNewSS, getLteEarfcn(regStates.getCellIdentity()));
                 break;
             }
 
@@ -2567,15 +2533,19 @@
             default:
                 // Issue all poll-related commands at once then count down the responses, which
                 // are allowed to arrive out-of-order
+                // TODO: Add WLAN support.
                 mPollingContext[0]++;
                 mCi.getOperator(obtainMessage(EVENT_POLL_STATE_OPERATOR, mPollingContext));
 
                 mPollingContext[0]++;
-                mCi.getDataRegistrationState(obtainMessage(EVENT_POLL_STATE_GPRS, mPollingContext));
+                mRegStateManagers.get(AccessNetworkConstants.TransportType.WWAN)
+                        .getNetworkRegistrationState(NetworkRegistrationState.DOMAIN_PS,
+                        obtainMessage(EVENT_POLL_STATE_GPRS, mPollingContext));
 
                 mPollingContext[0]++;
-                mCi.getVoiceRegistrationState(obtainMessage(EVENT_POLL_STATE_REGISTRATION,
-                        mPollingContext));
+                mRegStateManagers.get(AccessNetworkConstants.TransportType.WWAN)
+                        .getNetworkRegistrationState(NetworkRegistrationState.DOMAIN_CS,
+                        obtainMessage(EVENT_POLL_STATE_REGISTRATION, mPollingContext));
 
                 if (mPhone.isPhoneTypeGsm()) {
                     mPollingContext[0]++;
@@ -3122,8 +3092,8 @@
      * to service state */
     private int regCodeToServiceState(int code) {
         switch (code) {
-            case ServiceState.RIL_REG_STATE_HOME:
-            case ServiceState.RIL_REG_STATE_ROAMING:
+            case NetworkRegistrationState.REG_STATE_HOME:
+            case NetworkRegistrationState.REG_STATE_ROAMING:
                 return ServiceState.STATE_IN_SERVICE;
             default:
                 return ServiceState.STATE_OUT_OF_SERVICE;
@@ -3135,7 +3105,7 @@
      * returns true if registered roam, false otherwise
      */
     private boolean regCodeIsRoaming (int code) {
-        return ServiceState.RIL_REG_STATE_ROAMING == code;
+        return NetworkRegistrationState.REG_STATE_ROAMING == code;
     }
 
     private boolean isSameOperatorNameFromSimAndSS(ServiceState s) {
diff --git a/src/java/com/android/internal/telephony/SubscriptionMonitor.java b/src/java/com/android/internal/telephony/SubscriptionMonitor.java
index 8ccec6f..4307875 100644
--- a/src/java/com/android/internal/telephony/SubscriptionMonitor.java
+++ b/src/java/com/android/internal/telephony/SubscriptionMonitor.java
@@ -70,7 +70,7 @@
     public SubscriptionMonitor(ITelephonyRegistry tr, Context context,
             SubscriptionController subscriptionController, int numPhones) {
         try {
-            tr.addOnSubscriptionsChangedListener(context.getOpPackageName(),
+            tr.addOnSubscriptionsChangedListener("SubscriptionMonitor",
                     mSubscriptionsChangedListener);
         } catch (RemoteException e) {
         }
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
index 8ef4a2a..e379a69 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -21,6 +21,7 @@
 import android.os.Handler;
 import android.os.IDeviceIdleController;
 import android.os.ServiceManager;
+import android.telephony.AccessNetworkConstants.TransportType;
 
 import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
 import com.android.internal.telephony.cdma.EriManager;
@@ -71,19 +72,12 @@
         return new NitzStateMachine(phone);
     }
 
-    /**
-     * Returns a new {@link TimeServiceHelper} instance.
-     */
-    public TimeServiceHelper makeTimeServiceHelper(Context context) {
-        return new TimeServiceHelper(context);
-    }
-
     public SimActivationTracker makeSimActivationTracker(Phone phone) {
         return new SimActivationTracker(phone);
     }
 
     public DcTracker makeDcTracker(Phone phone) {
-        return new DcTracker(phone);
+        return new DcTracker(phone, TransportType.WWAN);
     }
 
     public CarrierSignalAgent makeCarrierSignalAgent(Phone phone) {
diff --git a/src/java/com/android/internal/telephony/TelephonyTester.java b/src/java/com/android/internal/telephony/TelephonyTester.java
index b59fcb6..16fac4d 100644
--- a/src/java/com/android/internal/telephony/TelephonyTester.java
+++ b/src/java/com/android/internal/telephony/TelephonyTester.java
@@ -23,14 +23,15 @@
 import android.net.Uri;
 import android.os.BadParcelableException;
 import android.os.Build;
+import android.telephony.NetworkRegistrationState;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsConferenceState;
+import android.telephony.ims.ImsExternalCallState;
+import android.telephony.ims.ImsReasonInfo;
 
 import com.android.ims.ImsCall;
-import com.android.ims.ImsCallProfile;
-import com.android.ims.ImsConferenceState;
-import com.android.ims.ImsExternalCallState;
-import com.android.ims.ImsReasonInfo;
 import com.android.internal.telephony.gsm.SuppServiceNotification;
 import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
 import com.android.internal.telephony.imsphone.ImsPhone;
@@ -338,12 +339,12 @@
         }
         if (mServiceStateTestIntent.hasExtra(EXTRA_VOICE_REG_STATE)) {
             ss.setVoiceRegState(mServiceStateTestIntent.getIntExtra(EXTRA_VOICE_REG_STATE,
-                    ServiceState.RIL_REG_STATE_UNKNOWN));
+                    NetworkRegistrationState.REG_STATE_UNKNOWN));
             log("Override voice reg state with " + ss.getVoiceRegState());
         }
         if (mServiceStateTestIntent.hasExtra(EXTRA_DATA_REG_STATE)) {
             ss.setDataRegState(mServiceStateTestIntent.getIntExtra(EXTRA_DATA_REG_STATE,
-                    ServiceState.RIL_REG_STATE_UNKNOWN));
+                    NetworkRegistrationState.REG_STATE_UNKNOWN));
             log("Override data reg state with " + ss.getDataRegState());
         }
         if (mServiceStateTestIntent.hasExtra(EXTRA_VOICE_RAT)) {
diff --git a/src/java/com/android/internal/telephony/TimeServiceHelper.java b/src/java/com/android/internal/telephony/TimeServiceHelper.java
index fb1efd0..94b094f 100644
--- a/src/java/com/android/internal/telephony/TimeServiceHelper.java
+++ b/src/java/com/android/internal/telephony/TimeServiceHelper.java
@@ -91,6 +91,20 @@
     }
 
     /**
+     * Returns the same value as {@link System#currentTimeMillis()}.
+     */
+    public long currentTimeMillis() {
+        return System.currentTimeMillis();
+    }
+
+    /**
+     * Returns the same value as {@link SystemClock#elapsedRealtime()}.
+     */
+    public long elapsedRealtime() {
+        return SystemClock.elapsedRealtime();
+    }
+
+    /**
      * Returns true if the device has an explicit time zone set.
      */
     public boolean isTimeZoneSettingInitialized() {
diff --git a/src/java/com/android/internal/telephony/TimeZoneLookupHelper.java b/src/java/com/android/internal/telephony/TimeZoneLookupHelper.java
index 8b3276b..101fddd 100644
--- a/src/java/com/android/internal/telephony/TimeZoneLookupHelper.java
+++ b/src/java/com/android/internal/telephony/TimeZoneLookupHelper.java
@@ -16,12 +16,11 @@
 
 package com.android.internal.telephony;
 
-import android.util.TimeUtils;
+import android.text.TextUtils;
 
 import libcore.util.CountryTimeZones;
 import libcore.util.TimeZoneFinder;
 
-import java.util.Arrays;
 import java.util.Date;
 import java.util.TimeZone;
 
@@ -30,72 +29,150 @@
  */
 // Non-final to allow mocking.
 public class TimeZoneLookupHelper {
-    private static final int MS_PER_HOUR = 60 * 60 * 1000;
 
     /**
-     * List of ISO codes for countries that can have an offset of
-     * GMT+0 when not in daylight savings time.  This ignores some
-     * small places such as the Canary Islands (Spain) and
-     * Danmarkshavn (Denmark).  The list must be sorted by code.
+     * The result of looking up a time zone using offset information (and possibly more).
      */
-    private static final String[] GMT_COUNTRY_CODES = {
-            "bf", // Burkina Faso
-            "ci", // Cote d'Ivoire
-            "eh", // Western Sahara
-            "fo", // Faroe Islands, Denmark
-            "gb", // United Kingdom of Great Britain and Northern Ireland
-            "gh", // Ghana
-            "gm", // Gambia
-            "gn", // Guinea
-            "gw", // Guinea Bissau
-            "ie", // Ireland
-            "lr", // Liberia
-            "is", // Iceland
-            "ma", // Morocco
-            "ml", // Mali
-            "mr", // Mauritania
-            "pt", // Portugal
-            "sl", // Sierra Leone
-            "sn", // Senegal
-            "st", // Sao Tome and Principe
-            "tg", // Togo
-    };
+    public static final class OffsetResult {
+
+        /** A zone that matches the supplied criteria. See also {@link #isOnlyMatch}. */
+        public final String zoneId;
+
+        /** True if there is only one matching time zone for the supplied criteria. */
+        public final boolean isOnlyMatch;
+
+        public OffsetResult(String zoneId, boolean isOnlyMatch) {
+            this.zoneId = zoneId;
+            this.isOnlyMatch = isOnlyMatch;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            OffsetResult result = (OffsetResult) o;
+
+            if (isOnlyMatch != result.isOnlyMatch) {
+                return false;
+            }
+            return zoneId.equals(result.zoneId);
+        }
+
+        @Override
+        public int hashCode() {
+            int result = zoneId.hashCode();
+            result = 31 * result + (isOnlyMatch ? 1 : 0);
+            return result;
+        }
+
+        @Override
+        public String toString() {
+            return "Result{"
+                    + "zoneId='" + zoneId + '\''
+                    + ", isOnlyMatch=" + isOnlyMatch
+                    + '}';
+        }
+    }
+
+    /**
+     * The result of looking up a time zone using country information.
+     */
+    public static final class CountryResult {
+
+        /** A time zone for the country. */
+        public final String zoneId;
+
+        /**
+         * True if all the time zones in the country have the same offset at {@link #whenMillis}.
+         */
+        public final boolean allZonesHaveSameOffset;
+
+        /** The time associated with {@link #allZonesHaveSameOffset}. */
+        public final long whenMillis;
+
+        public CountryResult(String zoneId, boolean allZonesHaveSameOffset, long whenMillis) {
+            this.zoneId = zoneId;
+            this.allZonesHaveSameOffset = allZonesHaveSameOffset;
+            this.whenMillis = whenMillis;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            CountryResult that = (CountryResult) o;
+
+            if (allZonesHaveSameOffset != that.allZonesHaveSameOffset) {
+                return false;
+            }
+            if (whenMillis != that.whenMillis) {
+                return false;
+            }
+            return zoneId.equals(that.zoneId);
+        }
+
+        @Override
+        public int hashCode() {
+            int result = zoneId.hashCode();
+            result = 31 * result + (allZonesHaveSameOffset ? 1 : 0);
+            result = 31 * result + (int) (whenMillis ^ (whenMillis >>> 32));
+            return result;
+        }
+
+        @Override
+        public String toString() {
+            return "CountryResult{"
+                    + "zoneId='" + zoneId + '\''
+                    + ", allZonesHaveSameOffset=" + allZonesHaveSameOffset
+                    + ", whenMillis=" + whenMillis
+                    + '}';
+        }
+    }
+
+    private static final int MS_PER_HOUR = 60 * 60 * 1000;
+
+    /** The last CountryTimeZones object retrieved. */
+    private CountryTimeZones mLastCountryTimeZones;
 
     public TimeZoneLookupHelper() {}
 
     /**
-     * Finds a time zone ID that fits the supplied NITZ and country information.
+     * Looks for a time zone for the supplied NITZ and country information.
      *
      * <p><em>Note:</em> When there are multiple matching zones then one of the matching candidates
-     * will be returned. If the current device default zone matches it will be returned in
-     * preference to other candidates. This method can return {@code null} if no matching time
-     * zones are found.
+     * will be returned in the result. If the current device default zone matches it will be
+     * returned in preference to other candidates. This method can return {@code null} if no
+     * matching time zones are found.
      */
-    public String guessZoneIdByNitzCountry(NitzData nitzData, String isoCountryCode) {
-        return guessZoneIdByInstantOffsetDstCountry(
-                nitzData.getCurrentTimeInMillis(),
-                nitzData.getLocalOffsetMillis(),
-                nitzData.isDst(),
-                isoCountryCode);
+    public OffsetResult lookupByNitzCountry(NitzData nitzData, String isoCountryCode) {
+        CountryTimeZones countryTimeZones = getCountryTimeZones(isoCountryCode);
+        if (countryTimeZones == null) {
+            return null;
+        }
+        android.icu.util.TimeZone bias = android.icu.util.TimeZone.getDefault();
+
+        CountryTimeZones.OffsetResult offsetResult = countryTimeZones.lookupByOffsetWithBias(
+                nitzData.getLocalOffsetMillis(), nitzData.isDst(),
+                nitzData.getCurrentTimeInMillis(), bias);
+
+        if (offsetResult == null) {
+            return null;
+        }
+        return new OffsetResult(offsetResult.mTimeZone.getID(), offsetResult.mOneMatch);
     }
 
     /**
-     * Finds a time zone ID that fits the supplied time / offset and country information.
-     *
-     * <p><em>Note:</em> When there are multiple matching zones then one of the matching candidates
-     * will be returned. If the current device default zone matches it will be returned in
-     * preference to other candidates. This method can return {@code null} if no matching time
-     * zones are found.
-     */
-    public String guessZoneIdByInstantOffsetDstCountry(
-            long timeMillis, int utcOffsetMillis, boolean isDst, String isoCountryCode) {
-        TimeZone timeZone =
-                TimeUtils.getTimeZone(utcOffsetMillis, isDst, timeMillis, isoCountryCode);
-        return timeZone == null ? null : timeZone.getID();
-    }
-
-    /**
-     * Finds a time zone ID using only information present in the supplied {@link NitzData} object.
+     * Looks for a time zone using only information present in the supplied {@link NitzData} object.
      *
      * <p><em>Note:</em> Because multiple time zones can have the same offset / DST state at a given
      * time this process is error prone; an arbitrary match is returned when there are multiple
@@ -103,9 +180,8 @@
      * information provided by NITZ is incorrect. This method can return {@code null} if no matching
      * time zones are found.
      */
-    public String guessZoneIdByNitz(NitzData nitzData) {
-        TimeZone zone = guessZoneByNitzStatic(nitzData);
-        return zone == null ? null : zone.getID();
+    public OffsetResult lookupByNitz(NitzData nitzData) {
+        return lookupByNitzStatic(nitzData);
     }
 
     /**
@@ -115,59 +191,108 @@
      * according to the device's current system clock time). If this is not the case then
      * {@code null} can be returned.
      */
-    public String guessZoneIdByCountry(String isoCountryCode, long whenMillis) {
-        CountryTimeZones countryTimeZones =
-                TimeZoneFinder.getInstance().lookupCountryTimeZones(isoCountryCode);
+    public CountryResult lookupByCountry(String isoCountryCode, long whenMillis) {
+        CountryTimeZones countryTimeZones = getCountryTimeZones(isoCountryCode);
         if (countryTimeZones == null) {
             // Unknown country code.
             return null;
         }
-
-        if (countryTimeZones.isDefaultOkForCountryTimeZoneDetection(whenMillis)) {
-            return countryTimeZones.getDefaultTimeZoneId();
+        if (countryTimeZones.getDefaultTimeZoneId() == null) {
+            return null;
         }
-        return null;
+
+        return new CountryResult(
+                countryTimeZones.getDefaultTimeZoneId(),
+                countryTimeZones.isDefaultOkForCountryTimeZoneDetection(whenMillis),
+                whenMillis);
     }
 
-    /** Static method for use by {@link ServiceStateTracker}. */
+    /**
+     * Finds a time zone using only information present in the supplied {@link NitzData} object.
+     * This is a static method for use by {@link ServiceStateTracker}.
+     *
+     * <p><em>Note:</em> Because multiple time zones can have the same offset / DST state at a given
+     * time this process is error prone; an arbitrary match is returned when there are multiple
+     * candidates. The algorithm can also return a non-exact match by assuming that the DST
+     * information provided by NITZ is incorrect. This method can return {@code null} if no matching
+     * time zones are found.
+     */
     static TimeZone guessZoneByNitzStatic(NitzData nitzData) {
+        OffsetResult result = lookupByNitzStatic(nitzData);
+        return result != null ? TimeZone.getTimeZone(result.zoneId) : null;
+    }
+
+    private static OffsetResult lookupByNitzStatic(NitzData nitzData) {
         int utcOffsetMillis = nitzData.getLocalOffsetMillis();
         boolean isDst = nitzData.isDst();
         long timeMillis = nitzData.getCurrentTimeInMillis();
 
-        TimeZone guess = guessByInstantOffsetDst(timeMillis, utcOffsetMillis, isDst);
-        if (guess == null) {
+        OffsetResult match = lookupByInstantOffsetDst(timeMillis, utcOffsetMillis, isDst);
+        if (match == null) {
             // Couldn't find a proper timezone.  Perhaps the DST data is wrong.
-            guess = guessByInstantOffsetDst(timeMillis, utcOffsetMillis, !isDst);
+            match = lookupByInstantOffsetDst(timeMillis, utcOffsetMillis, !isDst);
         }
-        return guess;
+        return match;
     }
 
-    private static TimeZone guessByInstantOffsetDst(long timeMillis, int utcOffsetMillis,
+    private static OffsetResult lookupByInstantOffsetDst(long timeMillis, int utcOffsetMillis,
             boolean isDst) {
         int rawOffset = utcOffsetMillis;
         if (isDst) {
             rawOffset -= MS_PER_HOUR;
         }
         String[] zones = TimeZone.getAvailableIDs(rawOffset);
-        TimeZone guess = null;
+        TimeZone match = null;
         Date d = new Date(timeMillis);
+        boolean isOnlyMatch = true;
         for (String zone : zones) {
             TimeZone tz = TimeZone.getTimeZone(zone);
             if (tz.getOffset(timeMillis) == utcOffsetMillis && tz.inDaylightTime(d) == isDst) {
-                guess = tz;
-                break;
+                if (match == null) {
+                    match = tz;
+                } else {
+                    isOnlyMatch = false;
+                    break;
+                }
             }
         }
 
-        return guess;
+        if (match == null) {
+            return null;
+        }
+        return new OffsetResult(match.getID(), isOnlyMatch);
     }
 
     /**
      * Returns {@code true} if the supplied (lower-case) ISO country code is for a country known to
-     * use a raw offset of zero from UTC.
+     * use a raw offset of zero from UTC at the time specified.
      */
-    public boolean countryUsesUtc(String isoCountryCode) {
-        return Arrays.binarySearch(GMT_COUNTRY_CODES, isoCountryCode) >= 0;
+    public boolean countryUsesUtc(String isoCountryCode, long whenMillis) {
+        if (TextUtils.isEmpty(isoCountryCode)) {
+            return false;
+        }
+
+        CountryTimeZones countryTimeZones = getCountryTimeZones(isoCountryCode);
+        return countryTimeZones != null && countryTimeZones.hasUtcZone(whenMillis);
+    }
+
+    private CountryTimeZones getCountryTimeZones(String isoCountryCode) {
+        // A single entry cache of the last CountryTimeZones object retrieved since there should
+        // be strong consistency across calls.
+        synchronized (this) {
+            if (mLastCountryTimeZones != null) {
+                if (mLastCountryTimeZones.isForCountryCode(isoCountryCode)) {
+                    return mLastCountryTimeZones;
+                }
+            }
+
+            // Perform the lookup. It's very unlikely to return null, but we won't cache null.
+            CountryTimeZones countryTimeZones =
+                    TimeZoneFinder.getInstance().lookupCountryTimeZones(isoCountryCode);
+            if (countryTimeZones != null) {
+                mLastCountryTimeZones = countryTimeZones;
+            }
+            return countryTimeZones;
+        }
     }
 }
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
index b94c8a1..e7ced2e 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
@@ -45,6 +45,7 @@
 import android.telephony.data.DataCallResponse;
 import android.telephony.data.DataProfile;
 import android.telephony.data.DataService;
+import android.telephony.data.DataServiceCallback;
 import android.text.TextUtils;
 import android.util.LocalLog;
 import android.util.Pair;
@@ -54,7 +55,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.CallTracker;
 import com.android.internal.telephony.CarrierSignalAgent;
-import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.DctConstants;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
@@ -174,6 +174,7 @@
     private DcFailCause mDcFailCause;
 
     private Phone mPhone;
+    private DataServiceManager mDataServiceManager;
     private LinkProperties mLinkProperties = new LinkProperties();
     private long mCreateTime;
     private long mLastFailTime;
@@ -197,7 +198,6 @@
     static final int BASE = Protocol.BASE_DATA_CONNECTION;
     static final int EVENT_CONNECT = BASE + 0;
     static final int EVENT_SETUP_DATA_CONNECTION_DONE = BASE + 1;
-    static final int EVENT_GET_LAST_FAIL_DONE = BASE + 2;
     static final int EVENT_DEACTIVATE_DONE = BASE + 3;
     static final int EVENT_DISCONNECT = BASE + 4;
     static final int EVENT_RIL_CONNECTED = BASE + 5;
@@ -226,7 +226,6 @@
         sCmdToString[EVENT_CONNECT - BASE] = "EVENT_CONNECT";
         sCmdToString[EVENT_SETUP_DATA_CONNECTION_DONE - BASE] =
                 "EVENT_SETUP_DATA_CONNECTION_DONE";
-        sCmdToString[EVENT_GET_LAST_FAIL_DONE - BASE] = "EVENT_GET_LAST_FAIL_DONE";
         sCmdToString[EVENT_DEACTIVATE_DONE - BASE] = "EVENT_DEACTIVATE_DONE";
         sCmdToString[EVENT_DISCONNECT - BASE] = "EVENT_DISCONNECT";
         sCmdToString[EVENT_RIL_CONNECTED - BASE] = "EVENT_RIL_CONNECTED";
@@ -273,11 +272,13 @@
      * @param id the connection id
      * @return DataConnection that was created.
      */
-    public static DataConnection makeDataConnection(Phone phone, int id,
-            DcTracker dct, DcTesterFailBringUpAll failBringUpAll,
-            DcController dcc) {
+    public static DataConnection makeDataConnection(Phone phone, int id, DcTracker dct,
+                                                    DataServiceManager dataServiceManager,
+                                                    DcTesterFailBringUpAll failBringUpAll,
+                                                    DcController dcc) {
         DataConnection dc = new DataConnection(phone,
-                "DC-" + mInstanceNumber.incrementAndGet(), id, dct, failBringUpAll, dcc);
+                "DC-" + mInstanceNumber.incrementAndGet(), id, dct, dataServiceManager,
+                failBringUpAll, dcc);
         dc.start();
         if (DBG) dc.log("Made " + dc.getName());
         return dc;
@@ -325,11 +326,10 @@
      */
     public enum SetupResult {
         SUCCESS,
-        ERR_BadCommand,
-        ERR_UnacceptableParameter,
-        ERR_GetLastErrorFromRil,
-        ERR_Stale,
-        ERR_RilError;
+        ERROR_RADIO_NOT_AVAILABLE,
+        ERROR_INVALID_ARG,
+        ERROR_STALE,
+        ERROR_DATA_SERVICE_SPECIFIC_ERROR;
 
         public DcFailCause mFailCause;
 
@@ -377,12 +377,12 @@
         return ret;
     }
 
+    @VisibleForTesting
     public UpdateLinkPropertyResult updateLinkProperty(DataCallResponse newState) {
         UpdateLinkPropertyResult result = new UpdateLinkPropertyResult(mLinkProperties);
 
         if (newState == null) return result;
 
-        SetupResult setupResult;
         result.newLp = new LinkProperties();
 
         // set link properties based on data call response
@@ -444,7 +444,8 @@
 
     //***** Constructor (NOTE: uses dcc.getHandler() as its Handler)
     private DataConnection(Phone phone, String name, int id,
-                DcTracker dct, DcTesterFailBringUpAll failBringUpAll,
+                           DcTracker dct, DataServiceManager dataServiceManager,
+                           DcTesterFailBringUpAll failBringUpAll,
                 DcController dcc) {
         super(name, dcc.getHandler());
         setLogRecSize(300);
@@ -453,6 +454,7 @@
 
         mPhone = phone;
         mDct = dct;
+        mDataServiceManager = dataServiceManager;
         mDcTesterFailBringUpAll = failBringUpAll;
         mDcController = dcc;
         mId = id;
@@ -480,7 +482,7 @@
     /**
      * Begin setting up a data connection, calls setupDataCall
      * and the ConnectionParams will be returned with the
-     * EVENT_SETUP_DATA_CONNECTION_DONE AsyncResult.userObj.
+     * EVENT_SETUP_DATA_CONNECTION_DONE
      *
      * @param cp is the connection parameters
      */
@@ -512,7 +514,6 @@
         mLastFailTime = -1;
         mLastFailCause = DcFailCause.NONE;
 
-        // msg.obj will be returned in AsyncResult.userObj;
         Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
         msg.obj = cp;
 
@@ -529,8 +530,9 @@
         boolean allowRoaming = mPhone.getDataRoamingEnabled()
                 || (isModemRoaming && !mPhone.getServiceState().getDataRoaming());
 
-        mPhone.mCi.setupDataCall(ServiceState.rilRadioTechnologyToAccessNetworkType(cp.mRilRat), dp,
-                isModemRoaming, allowRoaming, DataService.REQUEST_REASON_NORMAL, null, msg);
+        mDataServiceManager.setupDataCall(
+                ServiceState.rilRadioTechnologyToAccessNetworkType(cp.mRilRat), dp, isModemRoaming,
+                allowRoaming, DataService.REQUEST_REASON_NORMAL, null, msg);
         TelephonyMetrics.getInstance().writeSetupDataCall(mPhone.getPhoneId(), cp.mRilRat,
                 dp.getProfileId(), dp.getApn(), dp.getProtocol());
     }
@@ -543,8 +545,7 @@
 
     /**
      * TearDown the data connection when the deactivation is complete a Message with
-     * msg.what == EVENT_DEACTIVATE_DONE and msg.obj == AsyncResult with AsyncResult.obj
-     * containing the parameter o.
+     * msg.what == EVENT_DEACTIVATE_DONE
      *
      * @param o is the object returned in the AsyncResult.obj.
      */
@@ -552,7 +553,7 @@
         int discReason = DataService.REQUEST_REASON_NORMAL;
         ApnContext apnContext = null;
         if ((o != null) && (o instanceof DisconnectParams)) {
-            DisconnectParams dp = (DisconnectParams)o;
+            DisconnectParams dp = (DisconnectParams) o;
             apnContext = dp.mApnContext;
             if (TextUtils.equals(dp.mReason, Phone.REASON_RADIO_TURNED_OFF)
                     || TextUtils.equals(dp.mReason, Phone.REASON_PDP_RESET)) {
@@ -563,7 +564,7 @@
         String str = "tearDownData. mCid=" + mCid + ", reason=" + discReason;
         if (DBG) log(str);
         if (apnContext != null) apnContext.requestLog(str);
-        mPhone.mCi.deactivateDataCall(mCid, discReason,
+        mDataServiceManager.deactivateDataCall(mCid, discReason,
                 obtainMessage(EVENT_DEACTIVATE_DONE, mTag, 0, o));
     }
 
@@ -719,39 +720,34 @@
     }
 
     /**
-     * Process setup completion.
+     * Process setup data completion result from data service
      *
-     * @param ar is the result
-     * @return SetupResult.
+     * @param resultCode The result code returned by data service
+     * @param response Data call setup response from data service
+     * @param cp The original connection params used for data call setup
+     * @return Setup result
      */
-    private SetupResult onSetupConnectionCompleted(AsyncResult ar) {
-        DataCallResponse response = (DataCallResponse) ar.result;
-        ConnectionParams cp = (ConnectionParams) ar.userObj;
+    private SetupResult onSetupConnectionCompleted(@DataServiceCallback.ResultCode int resultCode,
+                                                   DataCallResponse response,
+                                                   ConnectionParams cp) {
         SetupResult result;
 
         if (cp.mTag != mTag) {
             if (DBG) {
                 log("onSetupConnectionCompleted stale cp.tag=" + cp.mTag + ", mtag=" + mTag);
             }
-            result = SetupResult.ERR_Stale;
-        } else if (ar.exception != null) {
-            if (DBG) {
-                log("onSetupConnectionCompleted failed, ar.exception=" + ar.exception +
-                    " response=" + response);
-            }
-
-            if (ar.exception instanceof CommandException
-                    && ((CommandException) (ar.exception)).getCommandError()
-                    == CommandException.Error.RADIO_NOT_AVAILABLE) {
-                result = SetupResult.ERR_BadCommand;
+            result = SetupResult.ERROR_STALE;
+        } else if (resultCode == DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE) {
+            result = SetupResult.ERROR_RADIO_NOT_AVAILABLE;
+            result.mFailCause = DcFailCause.RADIO_NOT_AVAILABLE;
+        } else if (response.getStatus() != 0) {
+            if (response.getStatus() == DcFailCause.RADIO_NOT_AVAILABLE.getErrorCode()) {
+                result = SetupResult.ERROR_RADIO_NOT_AVAILABLE;
                 result.mFailCause = DcFailCause.RADIO_NOT_AVAILABLE;
             } else {
-                result = SetupResult.ERR_RilError;
+                result = SetupResult.ERROR_DATA_SERVICE_SPECIFIC_ERROR;
                 result.mFailCause = DcFailCause.fromInt(response.getStatus());
             }
-        } else if (response.getStatus() != 0) {
-            result = SetupResult.ERR_RilError;
-            result.mFailCause = DcFailCause.fromInt(response.getStatus());
         } else {
             if (DBG) log("onSetupConnectionCompleted received successful DataCallResponse");
             mCid = response.getCallId();
@@ -1134,10 +1130,10 @@
                 result = SetupResult.SUCCESS;
             } catch (UnknownHostException e) {
                 log("setLinkProperties: UnknownHostException " + e);
-                result = SetupResult.ERR_UnacceptableParameter;
+                result = SetupResult.ERROR_INVALID_ARG;
             }
         } else {
-            result = SetupResult.ERR_RilError;
+            result = SetupResult.ERROR_DATA_SERVICE_SPECIFIC_ERROR;
         }
 
         // An error occurred so clear properties
@@ -1232,6 +1228,7 @@
             mDct = null;
             mApnSetting = null;
             mPhone = null;
+            mDataServiceManager = null;
             mLinkProperties = null;
             mLastFailCause = null;
             mUserData = null;
@@ -1338,7 +1335,8 @@
                     break;
                 case EVENT_TEAR_DOWN_NOW:
                     if (DBG) log("DcDefaultState EVENT_TEAR_DOWN_NOW");
-                    mPhone.mCi.deactivateDataCall(mCid, DataService.REQUEST_REASON_NORMAL,  null);
+                    mDataServiceManager.deactivateDataCall(mCid, DataService.REQUEST_REASON_NORMAL,
+                            null);
                     break;
                 case EVENT_LOST_CONNECTION:
                     if (DBG) {
@@ -1568,11 +1566,12 @@
                     break;
 
                 case EVENT_SETUP_DATA_CONNECTION_DONE:
-                    ar = (AsyncResult) msg.obj;
-                    cp = (ConnectionParams) ar.userObj;
+                    cp = (ConnectionParams) msg.obj;
 
-                    SetupResult result = onSetupConnectionCompleted(ar);
-                    if (result != SetupResult.ERR_Stale) {
+                    DataCallResponse dataCallResponse =
+                            msg.getData().getParcelable(DataServiceManager.DATA_CALL_RESPONSE);
+                    SetupResult result = onSetupConnectionCompleted(msg.arg1, dataCallResponse, cp);
+                    if (result != SetupResult.ERROR_STALE) {
                         if (mConnectionParams != cp) {
                             loge("DcActivatingState: WEIRD mConnectionsParams:"+ mConnectionParams
                                     + " != cp:" + cp);
@@ -1591,28 +1590,29 @@
                             mDcFailCause = DcFailCause.NONE;
                             transitionTo(mActiveState);
                             break;
-                        case ERR_BadCommand:
+                        case ERROR_RADIO_NOT_AVAILABLE:
                             // Vendor ril rejected the command and didn't connect.
                             // Transition to inactive but send notifications after
                             // we've entered the mInactive state.
                             mInactiveState.setEnterNotificationParams(cp, result.mFailCause);
                             transitionTo(mInactiveState);
                             break;
-                        case ERR_UnacceptableParameter:
+                        case ERROR_INVALID_ARG:
                             // The addresses given from the RIL are bad
                             tearDownData(cp);
                             transitionTo(mDisconnectingErrorCreatingConnection);
                             break;
-                        case ERR_RilError:
+                        case ERROR_DATA_SERVICE_SPECIFIC_ERROR:
 
                             // Retrieve the suggested retry delay from the modem and save it.
                             // If the modem want us to retry the current APN again, it will
                             // suggest a positive delay value (in milliseconds). Otherwise we'll get
                             // NO_SUGGESTED_RETRY_DELAY here.
-                            long delay = getSuggestedRetryDelay(ar);
+
+                            long delay = getSuggestedRetryDelay(dataCallResponse);
                             cp.mApnContext.setModemSuggestedDelay(delay);
 
-                            String str = "DcActivatingState: ERR_RilError "
+                            String str = "DcActivatingState: ERROR_DATA_SERVICE_SPECIFIC_ERROR "
                                     + " delay=" + delay
                                     + " result=" + result
                                     + " result.isRestartRadioFail=" +
@@ -1629,7 +1629,7 @@
                             mInactiveState.setEnterNotificationParams(cp, result.mFailCause);
                             transitionTo(mInactiveState);
                             break;
-                        case ERR_Stale:
+                        case ERROR_STALE:
                             loge("DcActivatingState: stale EVENT_SETUP_DATA_CONNECTION_DONE"
                                     + " tag:" + cp.mTag + " != mTag:" + mTag);
                             break;
@@ -1638,46 +1638,6 @@
                     }
                     retVal = HANDLED;
                     break;
-
-                case EVENT_GET_LAST_FAIL_DONE:
-                    ar = (AsyncResult) msg.obj;
-                    cp = (ConnectionParams) ar.userObj;
-                    if (cp.mTag == mTag) {
-                        if (mConnectionParams != cp) {
-                            loge("DcActivatingState: WEIRD mConnectionsParams:" + mConnectionParams
-                                    + " != cp:" + cp);
-                        }
-
-                        DcFailCause cause = DcFailCause.UNKNOWN;
-
-                        if (ar.exception == null) {
-                            int rilFailCause = ((int[]) (ar.result))[0];
-                            cause = DcFailCause.fromInt(rilFailCause);
-                            if (cause == DcFailCause.NONE) {
-                                if (DBG) {
-                                    log("DcActivatingState msg.what=EVENT_GET_LAST_FAIL_DONE"
-                                            + " BAD: error was NONE, change to UNKNOWN");
-                                }
-                                cause = DcFailCause.UNKNOWN;
-                            }
-                        }
-                        mDcFailCause = cause;
-
-                        if (DBG) {
-                            log("DcActivatingState msg.what=EVENT_GET_LAST_FAIL_DONE"
-                                    + " cause=" + cause + " dc=" + DataConnection.this);
-                        }
-
-                        mInactiveState.setEnterNotificationParams(cp, cause);
-                        transitionTo(mInactiveState);
-                    } else {
-                        loge("DcActivatingState: stale EVENT_GET_LAST_FAIL_DONE"
-                                + " tag:" + cp.mTag + " != mTag:" + mTag);
-                    }
-
-                    retVal = HANDLED;
-                    break;
-
                 default:
                     if (VDBG) {
                         log("DcActivatingState not handled msg.what=" +
@@ -1978,8 +1938,7 @@
                     break;
 
                 case EVENT_DEACTIVATE_DONE:
-                    AsyncResult ar = (AsyncResult) msg.obj;
-                    DisconnectParams dp = (DisconnectParams) ar.userObj;
+                    DisconnectParams dp = (DisconnectParams) msg.obj;
 
                     String str = "DcDisconnectingState msg.what=EVENT_DEACTIVATE_DONE RefCount="
                             + mApnContexts.size();
@@ -1989,7 +1948,7 @@
                     if (dp.mTag == mTag) {
                         // Transition to inactive but send notifications after
                         // we've entered the mInactive state.
-                        mInactiveState.setEnterNotificationParams((DisconnectParams) ar.userObj);
+                        mInactiveState.setEnterNotificationParams(dp);
                         transitionTo(mInactiveState);
                     } else {
                         if (DBG) log("DcDisconnectState stale EVENT_DEACTIVATE_DONE"
@@ -2021,8 +1980,7 @@
 
             switch (msg.what) {
                 case EVENT_DEACTIVATE_DONE:
-                    AsyncResult ar = (AsyncResult) msg.obj;
-                    ConnectionParams cp = (ConnectionParams) ar.userObj;
+                    ConnectionParams cp = (ConnectionParams) msg.obj;
                     if (cp.mTag == mTag) {
                         String str = "DcDisconnectionErrorCreatingConnection" +
                                 " msg.what=EVENT_DEACTIVATE_DONE";
@@ -2274,14 +2232,11 @@
     /**
      * Using the result of the SETUP_DATA_CALL determine the retry delay.
      *
-     * @param ar is the result from SETUP_DATA_CALL
+     * @param response The response from setup data call
      * @return NO_SUGGESTED_RETRY_DELAY if no retry is needed otherwise the delay to the
      *         next SETUP_DATA_CALL
      */
-    private long getSuggestedRetryDelay(AsyncResult ar) {
-
-        DataCallResponse response = (DataCallResponse) ar.result;
-
+    private long getSuggestedRetryDelay(DataCallResponse response) {
         /** According to ril.h
          * The value < 0 means no value is suggested
          * The value 0 means retry should be done ASAP.
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java b/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java
index 185864c..8c3f751 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java
@@ -41,9 +41,9 @@
 
 import com.android.internal.telephony.Phone;
 
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * Data service manager manages handling data requests and responses on data services (e.g.
@@ -51,7 +51,7 @@
  */
 public class DataServiceManager {
     private static final String TAG = DataServiceManager.class.getSimpleName();
-    private static final boolean DBG = true;
+    private static final boolean DBG = false;
 
     static final String DATA_CALL_RESPONSE = "data_call_response";
 
@@ -69,7 +69,7 @@
 
     private final RegistrantList mServiceBindingChangedRegistrants = new RegistrantList();
 
-    private final Map<CellularDataServiceCallback, Message> mMessageMap = new HashMap<>();
+    private final Map<IBinder, Message> mMessageMap = new ConcurrentHashMap<>();
 
     private final RegistrantList mDataCallListChangedRegistrants = new RegistrantList();
 
@@ -128,29 +128,33 @@
                 log("onSetupDataCallComplete. resultCode = " + resultCode + ", response = "
                         + response);
             }
-            Message msg = mMessageMap.remove(CellularDataServiceCallback.this);
-            msg.getData().putParcelable(DATA_CALL_RESPONSE, response);
-            sendCompleteMessage(msg, resultCode);
+            Message msg = mMessageMap.remove(asBinder());
+            if (msg != null) {
+                msg.getData().putParcelable(DATA_CALL_RESPONSE, response);
+                sendCompleteMessage(msg, resultCode);
+            } else {
+                loge("Unable to find the message for setup call response.");
+            }
         }
 
         @Override
         public void onDeactivateDataCallComplete(@DataServiceCallback.ResultCode int resultCode) {
             if (DBG) log("onDeactivateDataCallComplete. resultCode = " + resultCode);
-            Message msg = mMessageMap.remove(CellularDataServiceCallback.this);
+            Message msg = mMessageMap.remove(asBinder());
             sendCompleteMessage(msg, resultCode);
         }
 
         @Override
         public void onSetInitialAttachApnComplete(@DataServiceCallback.ResultCode int resultCode) {
             if (DBG) log("onSetInitialAttachApnComplete. resultCode = " + resultCode);
-            Message msg = mMessageMap.remove(CellularDataServiceCallback.this);
+            Message msg = mMessageMap.remove(asBinder());
             sendCompleteMessage(msg, resultCode);
         }
 
         @Override
         public void onSetDataProfileComplete(@DataServiceCallback.ResultCode int resultCode) {
             if (DBG) log("onSetDataProfileComplete. resultCode = " + resultCode);
-            Message msg = mMessageMap.remove(CellularDataServiceCallback.this);
+            Message msg = mMessageMap.remove(asBinder());
             sendCompleteMessage(msg, resultCode);
         }
 
@@ -158,7 +162,7 @@
         public void onGetDataCallListComplete(@DataServiceCallback.ResultCode int resultCode,
                                               List<DataCallResponse> dataCallList) {
             if (DBG) log("onGetDataCallListComplete. resultCode = " + resultCode);
-            Message msg = mMessageMap.remove(CellularDataServiceCallback.this);
+            Message msg = mMessageMap.remove(asBinder());
             sendCompleteMessage(msg, resultCode);
         }
 
@@ -266,22 +270,28 @@
     public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
                               boolean allowRoaming, int reason, LinkProperties linkProperties,
                               Message onCompleteMessage) {
+        if (DBG) log("setupDataCall");
         if (!mBound) {
             loge("Data service not bound.");
             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
             return;
         }
 
-        CellularDataServiceCallback callback = new CellularDataServiceCallback();
+        CellularDataServiceCallback callback = null;
+        if (onCompleteMessage != null) {
+            callback = new CellularDataServiceCallback();
+            mMessageMap.put(callback.asBinder(), onCompleteMessage);
+        }
         try {
             mIDataService.setupDataCall(mPhone.getPhoneId(), accessNetworkType, dataProfile,
                     isRoaming, allowRoaming, reason, linkProperties, callback);
         } catch (RemoteException e) {
             loge("Cannot invoke setupDataCall on data service.");
+            if (callback != null) {
+                mMessageMap.remove(callback.asBinder());
+            }
             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
-            return;
         }
-        mMessageMap.put(callback, onCompleteMessage);
     }
 
     /**
@@ -298,21 +308,27 @@
      *        care about the result.
      */
     public void deactivateDataCall(int cid, int reason, Message onCompleteMessage) {
+        if (DBG) log("deactivateDataCall");
         if (!mBound) {
             loge("Data service not bound.");
             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
             return;
         }
 
-        CellularDataServiceCallback callback = new CellularDataServiceCallback();
+        CellularDataServiceCallback callback = null;
+        if (onCompleteMessage != null) {
+            callback = new CellularDataServiceCallback();
+            mMessageMap.put(callback.asBinder(), onCompleteMessage);
+        }
         try {
             mIDataService.deactivateDataCall(mPhone.getPhoneId(), cid, reason, callback);
         } catch (RemoteException e) {
             loge("Cannot invoke deactivateDataCall on data service.");
+            if (callback != null) {
+                mMessageMap.remove(callback.asBinder());
+            }
             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
-            return;
         }
-        mMessageMap.put(callback, onCompleteMessage);
     }
 
     /**
@@ -325,22 +341,28 @@
      */
     public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming,
                                     Message onCompleteMessage) {
+        if (DBG) log("setInitialAttachApn");
         if (!mBound) {
             loge("Data service not bound.");
             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
             return;
         }
 
-        CellularDataServiceCallback callback = new CellularDataServiceCallback();
+        CellularDataServiceCallback callback = null;
+        if (onCompleteMessage != null) {
+            callback = new CellularDataServiceCallback();
+            mMessageMap.put(callback.asBinder(), onCompleteMessage);
+        }
         try {
             mIDataService.setInitialAttachApn(mPhone.getPhoneId(), dataProfile, isRoaming,
                     callback);
         } catch (RemoteException e) {
             loge("Cannot invoke setInitialAttachApn on data service.");
+            if (callback != null) {
+                mMessageMap.remove(callback.asBinder());
+            }
             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
-            return;
         }
-        mMessageMap.put(callback, onCompleteMessage);
     }
 
     /**
@@ -355,21 +377,27 @@
      */
     public void setDataProfile(List<DataProfile> dps, boolean isRoaming,
                                Message onCompleteMessage) {
+        if (DBG) log("setDataProfile");
         if (!mBound) {
             loge("Data service not bound.");
             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
             return;
         }
 
-        CellularDataServiceCallback callback = new CellularDataServiceCallback();
+        CellularDataServiceCallback callback = null;
+        if (onCompleteMessage != null) {
+            callback = new CellularDataServiceCallback();
+            mMessageMap.put(callback.asBinder(), onCompleteMessage);
+        }
         try {
             mIDataService.setDataProfile(mPhone.getPhoneId(), dps, isRoaming, callback);
         } catch (RemoteException e) {
             loge("Cannot invoke setDataProfile on data service.");
+            if (callback != null) {
+                mMessageMap.remove(callback.asBinder());
+            }
             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
-            return;
         }
-        mMessageMap.put(callback, onCompleteMessage);
     }
 
     /**
@@ -379,21 +407,27 @@
      *        care about the result.
      */
     public void getDataCallList(Message onCompleteMessage) {
+        if (DBG) log("getDataCallList");
         if (!mBound) {
             loge("Data service not bound.");
             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
             return;
         }
 
-        CellularDataServiceCallback callback = new CellularDataServiceCallback();
+        CellularDataServiceCallback callback = null;
+        if (onCompleteMessage != null) {
+            callback = new CellularDataServiceCallback();
+            mMessageMap.put(callback.asBinder(), onCompleteMessage);
+        }
         try {
             mIDataService.getDataCallList(mPhone.getPhoneId(), callback);
         } catch (RemoteException e) {
             loge("Cannot invoke getDataCallList on data service.");
+            if (callback != null) {
+                mMessageMap.remove(callback.asBinder());
+            }
             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
-            return;
         }
-        mMessageMap.put(callback, onCompleteMessage);
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcController.java b/src/java/com/android/internal/telephony/dataconnection/DcController.java
index b4ceff6..8d05be7 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcController.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcController.java
@@ -26,6 +26,7 @@
 import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
+import android.telephony.AccessNetworkConstants.TransportType;
 import android.telephony.PhoneStateListener;
 import android.telephony.Rlog;
 import android.telephony.TelephonyManager;
@@ -52,9 +53,10 @@
     private static final boolean DBG = true;
     private static final boolean VDBG = false;
 
-    private Phone mPhone;
-    private DcTracker mDct;
-    private DcTesterDeactivateAll mDcTesterDeactivateAll;
+    private final Phone mPhone;
+    private final DcTracker mDct;
+    private final DataServiceManager mDataServiceManager;
+    private final DcTesterDeactivateAll mDcTesterDeactivateAll;
 
     // package as its used by Testing code
     // @GuardedBy("mDcListAll")
@@ -90,15 +92,17 @@
      * @param name to be used for the Controller
      * @param phone the phone associated with Dcc and Dct
      * @param dct the DataConnectionTracker associated with Dcc
+     * @param dataServiceManager the data service manager that manages data services
      * @param handler defines the thread/looper to be used with Dcc
      */
     private DcController(String name, Phone phone, DcTracker dct,
-            Handler handler) {
+                         DataServiceManager dataServiceManager, Handler handler) {
         super(name, handler);
         setLogRecSize(300);
         log("E ctor");
         mPhone = phone;
         mDct = dct;
+        mDataServiceManager = dataServiceManager;
         addState(mDccDefaultState);
         setInitialState(mDccDefaultState);
         log("X ctor");
@@ -115,15 +119,19 @@
         mNetworkPolicyManager = (NetworkPolicyManager) phone.getContext()
                 .getSystemService(Context.NETWORK_POLICY_SERVICE);
 
+        mDcTesterDeactivateAll = (Build.IS_DEBUGGABLE)
+                ? new DcTesterDeactivateAll(mPhone, DcController.this, getHandler())
+                : null;
+
         if (mTelephonyManager != null) {
             mTelephonyManager.listen(mPhoneStateListener,
                     PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE);
         }
     }
 
-    public static DcController makeDcc(Phone phone, DcTracker dct, Handler handler) {
-        DcController dcc = new DcController("Dcc", phone, dct, handler);
-        dcc.start();
+    public static DcController makeDcc(Phone phone, DcTracker dct,
+                                       DataServiceManager dataServiceManager, Handler handler) {
+        DcController dcc = new DcController("Dcc", phone, dct, dataServiceManager, handler);
         return dcc;
     }
 
@@ -192,14 +200,15 @@
     private class DccDefaultState extends State {
         @Override
         public void enter() {
-            mPhone.mCi.registerForRilConnected(getHandler(),
-                    DataConnection.EVENT_RIL_CONNECTED, null);
-            mPhone.mCi.registerForDataCallListChanged(getHandler(),
-                    DataConnection.EVENT_DATA_STATE_CHANGED, null);
-            if (Build.IS_DEBUGGABLE) {
-                mDcTesterDeactivateAll =
-                        new DcTesterDeactivateAll(mPhone, DcController.this, getHandler());
+            if (mPhone != null && mDataServiceManager.getTransportType()
+                    == TransportType.WWAN) {
+                mPhone.mCi.registerForRilConnected(getHandler(),
+                        DataConnection.EVENT_RIL_CONNECTED, null);
             }
+
+            mDataServiceManager.registerForDataCallListChanged(getHandler(),
+                    DataConnection.EVENT_DATA_STATE_CHANGED);
+
             if (mNetworkPolicyManager != null) {
                 mNetworkPolicyManager.registerListener(mListener);
             }
@@ -207,10 +216,12 @@
 
         @Override
         public void exit() {
-            if (mPhone != null) {
+            if (mPhone != null & mDataServiceManager.getTransportType()
+                    == TransportType.WWAN) {
                 mPhone.mCi.unregisterForRilConnected(getHandler());
-                mPhone.mCi.unregisterForDataCallListChanged(getHandler());
             }
+            mDataServiceManager.unregisterForDataCallListChanged(getHandler());
+
             if (mDcTesterDeactivateAll != null) {
                 mDcTesterDeactivateAll.dispose();
             }
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
index dd20592..eb6735b 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -54,6 +54,7 @@
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.provider.Telephony;
+import android.telephony.AccessNetworkConstants.TransportType;
 import android.telephony.CarrierConfigManager;
 import android.telephony.CellLocation;
 import android.telephony.PcoData;
@@ -563,14 +564,17 @@
     private BroadcastReceiver mProvisionBroadcastReceiver;
     private ProgressDialog mProvisioningSpinner;
 
-    public boolean mImsRegistrationState = false;
+    private final DataServiceManager mDataServiceManager;
+
+    private final int mTransportType;
 
     //***** Constructor
-    public DcTracker(Phone phone) {
+    public DcTracker(Phone phone, int transportType) {
         super();
         mPhone = phone;
-
         if (DBG) log("DCT.constructor");
+        mTransportType = transportType;
+        mDataServiceManager = new DataServiceManager(phone, transportType);
 
         mResolver = mPhone.getContext().getContentResolver();
         mUiccController = UiccController.getInstance();
@@ -601,7 +605,7 @@
         HandlerThread dcHandlerThread = new HandlerThread("DcHandlerThread");
         dcHandlerThread.start();
         Handler dcHandler = new Handler(dcHandlerThread.getLooper());
-        mDcc = DcController.makeDcc(mPhone, this, dcHandler);
+        mDcc = DcController.makeDcc(mPhone, this, mDataServiceManager, dcHandler);
         mDcTesterFailBringUpAll = new DcTesterFailBringUpAll(mPhone, dcHandler);
 
         mDataConnectionTracker = this;
@@ -640,6 +644,8 @@
         mProvisionActionName = null;
         mSettingsObserver = new SettingsObserver(null, this);
         mDataEnabledSettings = null;
+        mTransportType = 0;
+        mDataServiceManager = null;
     }
 
     public void registerServiceStateTrackerEvents() {
@@ -670,11 +676,13 @@
     }
 
     private void registerForAllEvents() {
-        mPhone.mCi.registerForAvailable(this, DctConstants.EVENT_RADIO_AVAILABLE, null);
-        mPhone.mCi.registerForOffOrNotAvailable(this,
-                DctConstants.EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
-        mPhone.mCi.registerForDataCallListChanged(this,
-                DctConstants.EVENT_DATA_STATE_CHANGED, null);
+        if (mTransportType == TransportType.WWAN) {
+            mPhone.mCi.registerForAvailable(this, DctConstants.EVENT_RADIO_AVAILABLE, null);
+            mPhone.mCi.registerForOffOrNotAvailable(this,
+                    DctConstants.EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
+            mPhone.mCi.registerForPcoData(this, DctConstants.EVENT_PCO_DATA_RECEIVED, null);
+        }
+
         // Note, this is fragile - the Phone is now presenting a merged picture
         // of PS (volte) & CS and by diving into its internals you're just seeing
         // the CS data.  This works well for the purposes this is currently used for
@@ -685,12 +693,12 @@
         mPhone.getCallTracker().registerForVoiceCallStarted(this,
                 DctConstants.EVENT_VOICE_CALL_STARTED, null);
         registerServiceStateTrackerEvents();
-     //   SubscriptionManager.registerForDdsSwitch(this,
-     //          DctConstants.EVENT_CLEAN_UP_ALL_CONNECTIONS, null);
         mPhone.mCi.registerForPcoData(this, DctConstants.EVENT_PCO_DATA_RECEIVED, null);
         mPhone.getCarrierActionAgent().registerForCarrierAction(
                 CarrierActionAgent.CARRIER_ACTION_SET_METERED_APNS_ENABLED, this,
                 DctConstants.EVENT_SET_CARRIER_DATA_ENABLED, null, false);
+        mDataServiceManager.registerForServiceBindingChanged(this,
+                DctConstants.EVENT_DATA_SERVICE_BINDING_CHANGED, null);
     }
 
     public void dispose() {
@@ -732,37 +740,24 @@
 
     private void unregisterForAllEvents() {
          //Unregister for all events
-        mPhone.mCi.unregisterForAvailable(this);
-        mPhone.mCi.unregisterForOffOrNotAvailable(this);
+        if (mTransportType == TransportType.WWAN) {
+            mPhone.mCi.unregisterForAvailable(this);
+            mPhone.mCi.unregisterForOffOrNotAvailable(this);
+            mPhone.mCi.unregisterForPcoData(this);
+        }
+
         IccRecords r = mIccRecords.get();
         if (r != null) {
             r.unregisterForRecordsLoaded(this);
             mIccRecords.set(null);
         }
-        mPhone.mCi.unregisterForDataCallListChanged(this);
         mPhone.getCallTracker().unregisterForVoiceCallEnded(this);
         mPhone.getCallTracker().unregisterForVoiceCallStarted(this);
         unregisterServiceStateTrackerEvents();
-        //SubscriptionManager.unregisterForDdsSwitch(this);
         mPhone.mCi.unregisterForPcoData(this);
         mPhone.getCarrierActionAgent().unregisterForCarrierAction(this,
                 CarrierActionAgent.CARRIER_ACTION_SET_METERED_APNS_ENABLED);
-    }
-
-    /**
-     * Called when EVENT_RESET_DONE is received so goto
-     * IDLE state and send notifications to those interested.
-     *
-     * TODO - currently unused.  Needs to be hooked into DataConnection cleanup
-     * TODO - needs to pass some notion of which connection is reset..
-     */
-    private void onResetDone(AsyncResult ar) {
-        if (DBG) log("EVENT_RESET_DONE");
-        String reason = null;
-        if (ar.userObj instanceof String) {
-            reason = (String) ar.userObj;
-        }
-        gotoIdleAndNotifyDataConnection(reason);
+        mDataServiceManager.unregisterForServiceBindingChanged(this);
     }
 
     /**
@@ -2080,7 +2075,7 @@
         } else {
             if (DBG) log("setInitialAttachApn: X selected Apn=" + initialAttachApnSetting);
 
-            mPhone.mCi.setInitialAttachApn(createDataProfile(initialAttachApnSetting),
+            mDataServiceManager.setInitialAttachApn(createDataProfile(initialAttachApnSetting),
                     mPhone.getServiceState().getDataRoamingFromRegistration(), null);
         }
     }
@@ -2124,12 +2119,6 @@
         return null;
     }
 
-    // TODO: For multiple Active APNs not exactly sure how to do this.
-    private void gotoIdleAndNotifyDataConnection(String reason) {
-        if (DBG) log("gotoIdleAndNotifyDataConnection: reason=" + reason);
-        notifyDataConnection(reason);
-    }
-
     /**
      * "Active" here means ApnContext isEnabled() and not in FAILED state
      * @param apnContext to compare with
@@ -3277,7 +3266,7 @@
                 }
             }
             if (dps.size() > 0) {
-                mPhone.mCi.setDataProfile(dps.toArray(new DataProfile[0]),
+                mDataServiceManager.setDataProfile(dps,
                         mPhone.getServiceState().getDataRoamingFromRegistration(), null);
             }
         }
@@ -3397,8 +3386,8 @@
         if (DBG) log("createDataConnection E");
 
         int id = mUniqueIdGenerator.getAndIncrement();
-        DataConnection conn = DataConnection.makeDataConnection(mPhone, id,
-                                                this, mDcTesterFailBringUpAll, mDcc);
+        DataConnection conn = DataConnection.makeDataConnection(mPhone, id, this,
+                mDataServiceManager, mDcTesterFailBringUpAll, mDcc);
         mDataConnections.put(id, conn);
         DcAsyncChannel dcac = new DcAsyncChannel(conn, LOG_TAG);
         int status = dcac.fullyConnectSync(mPhone.getContext(), this, conn.getHandler());
@@ -3783,12 +3772,6 @@
             case DctConstants.EVENT_VOICE_CALL_ENDED:
                 onVoiceCallEnded();
                 break;
-
-            case DctConstants.EVENT_RESET_DONE: {
-                if (DBG) log("EVENT_RESET_DONE");
-                onResetDone((AsyncResult) msg.obj);
-                break;
-            }
             case DctConstants.CMD_SET_USER_DATA_ENABLE: {
                 final boolean enabled = (msg.arg1 == DctConstants.ENABLED) ? true : false;
                 if (DBG) log("CMD_SET_USER_DATA_ENABLE enabled=" + enabled);
@@ -3933,11 +3916,6 @@
                 }
                 break;
             }
-            case DctConstants.EVENT_DATA_STATE_CHANGED: {
-                // no longer do anything, but still registered - clean up log
-                // TODO - why are we still registering?
-                break;
-            }
             case DctConstants.EVENT_PCO_DATA_RECEIVED: {
                 handlePcoData((AsyncResult)msg.obj);
                 break;
@@ -3948,6 +3926,8 @@
             case DctConstants.EVENT_DATA_RECONNECT:
                 onDataReconnect(msg.getData());
                 break;
+            case DctConstants.EVENT_DATA_SERVICE_BINDING_CHANGED:
+                onDataServiceBindingChanged((Boolean) ((AsyncResult) msg.obj).result);
             default:
                 Rlog.e("DcTracker", "Unhandled event=" + msg);
                 break;
@@ -4571,36 +4551,37 @@
             final int recoveryAction = getRecoveryAction();
             TelephonyMetrics.getInstance().writeDataStallEvent(mPhone.getPhoneId(), recoveryAction);
             switch (recoveryAction) {
-            case RecoveryAction.GET_DATA_CALL_LIST:
-                EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_GET_DATA_CALL_LIST,
-                        mSentSinceLastRecv);
-                if (DBG) log("doRecovery() get data call list");
-                mPhone.mCi.getDataCallList(obtainMessage(DctConstants.EVENT_DATA_STATE_CHANGED));
-                putRecoveryAction(RecoveryAction.CLEANUP);
-                break;
-            case RecoveryAction.CLEANUP:
-                EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_CLEANUP, mSentSinceLastRecv);
-                if (DBG) log("doRecovery() cleanup all connections");
-                cleanUpAllConnections(Phone.REASON_PDP_RESET);
-                putRecoveryAction(RecoveryAction.REREGISTER);
-                break;
-            case RecoveryAction.REREGISTER:
-                EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_REREGISTER,
-                        mSentSinceLastRecv);
-                if (DBG) log("doRecovery() re-register");
-                mPhone.getServiceStateTracker().reRegisterNetwork(null);
-                putRecoveryAction(RecoveryAction.RADIO_RESTART);
-                break;
-            case RecoveryAction.RADIO_RESTART:
-                EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RADIO_RESTART,
-                        mSentSinceLastRecv);
-                if (DBG) log("restarting radio");
-                restartRadio();
-                putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST);
-                break;
-            default:
-                throw new RuntimeException("doRecovery: Invalid recoveryAction=" +
-                    recoveryAction);
+                case RecoveryAction.GET_DATA_CALL_LIST:
+                    EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_GET_DATA_CALL_LIST,
+                            mSentSinceLastRecv);
+                    if (DBG) log("doRecovery() get data call list");
+                    mDataServiceManager.getDataCallList(obtainMessage());
+                    putRecoveryAction(RecoveryAction.CLEANUP);
+                    break;
+                case RecoveryAction.CLEANUP:
+                    EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_CLEANUP,
+                            mSentSinceLastRecv);
+                    if (DBG) log("doRecovery() cleanup all connections");
+                    cleanUpAllConnections(Phone.REASON_PDP_RESET);
+                    putRecoveryAction(RecoveryAction.REREGISTER);
+                    break;
+                case RecoveryAction.REREGISTER:
+                    EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_REREGISTER,
+                            mSentSinceLastRecv);
+                    if (DBG) log("doRecovery() re-register");
+                    mPhone.getServiceStateTracker().reRegisterNetwork(null);
+                    putRecoveryAction(RecoveryAction.RADIO_RESTART);
+                    break;
+                case RecoveryAction.RADIO_RESTART:
+                    EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RADIO_RESTART,
+                            mSentSinceLastRecv);
+                    if (DBG) log("restarting radio");
+                    restartRadio();
+                    putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST);
+                    break;
+                default:
+                    throw new RuntimeException("doRecovery: Invalid recoveryAction="
+                            + recoveryAction);
             }
             mSentSinceLastRecv = 0;
         }
@@ -4827,4 +4808,12 @@
                 apn.roamingProtocol, bearerBitmap, apn.mtu, apn.mvnoType, apn.mvnoMatchData,
                 apn.modemCognitive);
     }
+
+    private void onDataServiceBindingChanged(boolean bound) {
+        if (bound) {
+            mDcc.start();
+        } else {
+            mDcc.dispose();
+        }
+    }
 }
diff --git a/src/java/com/android/internal/telephony/ims/ImsConfigCompatAdapter.java b/src/java/com/android/internal/telephony/ims/ImsConfigCompatAdapter.java
new file mode 100644
index 0000000..90d415e
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ims/ImsConfigCompatAdapter.java
@@ -0,0 +1,88 @@
+/*
+ * 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.ims;
+
+import android.os.RemoteException;
+import android.telephony.ims.stub.ImsConfigImplBase;
+import android.util.Log;
+
+import com.android.ims.internal.IImsConfig;
+
+public class ImsConfigCompatAdapter extends ImsConfigImplBase {
+
+    private static final String TAG = "ImsConfigCompatAdapter";
+
+    private final IImsConfig mOldConfigInterface;
+
+    // Compat constants
+    public static final int UNKNOWN = -1;
+    public static final int SUCCESS = 0;
+    public static final int FAILED =  1;
+
+    public ImsConfigCompatAdapter(IImsConfig config) {
+        mOldConfigInterface = config;
+    }
+
+    @Override
+    public int setConfig(int item, int value) {
+        try {
+            if (mOldConfigInterface.setProvisionedValue(item, value) == SUCCESS) {
+                return CONFIG_RESULT_SUCCESS;
+            }
+        } catch (RemoteException e) {
+            Log.w(TAG, "setConfig: item=" + item + " value=" + value + "failed: "
+                    + e.getMessage());
+        }
+        return CONFIG_RESULT_FAILED;
+    }
+
+    @Override
+    public int setConfig(int item, String value) {
+        try {
+            if (mOldConfigInterface.setProvisionedStringValue(item, value) == SUCCESS) {
+                return CONFIG_RESULT_SUCCESS;
+            }
+        } catch (RemoteException e) {
+            Log.w(TAG, "setConfig: item=" + item + " value=" + value + "failed: "
+                    + e.getMessage());
+        }
+        return CONFIG_RESULT_FAILED;
+    }
+
+    @Override
+    public int getConfigInt(int item) {
+        try {
+            int value = mOldConfigInterface.getProvisionedValue(item);
+            if (value != UNKNOWN) {
+                return value;
+            }
+        } catch (RemoteException e) {
+            Log.w(TAG, "getConfigInt: item=" + item + "failed: " + e.getMessage());
+        }
+        return CONFIG_RESULT_UNKNOWN;
+    }
+
+    @Override
+    public String getConfigString(int item) {
+        try {
+            return mOldConfigInterface.getProvisionedStringValue(item);
+        } catch (RemoteException e) {
+            Log.w(TAG, "getConfigInt: item=" + item + "failed: " + e.getMessage());
+        }
+        return null;
+    }
+}
diff --git a/src/java/com/android/internal/telephony/ims/ImsRegistrationCompatAdapter.java b/src/java/com/android/internal/telephony/ims/ImsRegistrationCompatAdapter.java
new file mode 100644
index 0000000..5a51fd7
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ims/ImsRegistrationCompatAdapter.java
@@ -0,0 +1,114 @@
+/*
+ * 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.ims;
+
+import static android.telephony.ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
+import static android.telephony.ServiceState.RIL_RADIO_TECHNOLOGY_LTE;
+
+import android.net.Uri;
+import android.os.RemoteException;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.ArrayMap;
+
+import com.android.ims.internal.IImsRegistrationListener;
+
+import java.util.Map;
+
+public class ImsRegistrationCompatAdapter extends ImsRegistrationImplBase {
+
+    // Maps "RAT" based radio technologies to ImsRegistrationImplBase definitions.
+    private static final Map<Integer, Integer> RADIO_TECH_MAPPER = new ArrayMap<>(2);
+    static {
+        RADIO_TECH_MAPPER.put(RIL_RADIO_TECHNOLOGY_LTE, REGISTRATION_TECH_LTE);
+        RADIO_TECH_MAPPER.put(RIL_RADIO_TECHNOLOGY_IWLAN, REGISTRATION_TECH_IWLAN);
+    }
+
+    // Trampolines "old" listener events to the new interface.
+    private final IImsRegistrationListener mListener = new IImsRegistrationListener.Stub() {
+        @Override
+        public void registrationConnected() throws RemoteException {
+            onRegistered(REGISTRATION_TECH_NONE);
+        }
+
+        @Override
+        public void registrationProgressing() throws RemoteException {
+            onRegistering(REGISTRATION_TECH_NONE);
+        }
+
+        @Override
+        public void registrationConnectedWithRadioTech(int imsRadioTech) throws RemoteException {
+            onRegistered(RADIO_TECH_MAPPER.getOrDefault(imsRadioTech, REGISTRATION_TECH_NONE));
+        }
+
+        @Override
+        public void registrationProgressingWithRadioTech(int imsRadioTech) throws RemoteException {
+            onRegistering(RADIO_TECH_MAPPER.getOrDefault(imsRadioTech, REGISTRATION_TECH_NONE));
+        }
+
+        @Override
+        public void registrationDisconnected(ImsReasonInfo imsReasonInfo) throws RemoteException {
+            onDeregistered(imsReasonInfo);
+        }
+
+        @Override
+        public void registrationResumed() throws RemoteException {
+            // Don't care
+        }
+
+        @Override
+        public void registrationSuspended() throws RemoteException {
+            // Don't care
+        }
+
+        @Override
+        public void registrationServiceCapabilityChanged(int serviceClass, int event)
+                throws RemoteException {
+            // Don't care
+        }
+
+        @Override
+        public void registrationFeatureCapabilityChanged(int serviceClass, int[] enabledFeatures,
+                int[] disabledFeatures) throws RemoteException {
+            // Implemented in the MMTel Adapter
+        }
+
+        @Override
+        public void voiceMessageCountUpdate(int count) throws RemoteException {
+            // Implemented in the MMTel Adapter
+        }
+
+        @Override
+        public void registrationAssociatedUriChanged(Uri[] uris) throws RemoteException {
+            onSubscriberAssociatedUriChanged(uris);
+        }
+
+        @Override
+        public void registrationChangeFailed(int targetAccessTech, ImsReasonInfo imsReasonInfo)
+                throws RemoteException {
+            onTechnologyChangeFailed(RADIO_TECH_MAPPER.getOrDefault(targetAccessTech,
+                    REGISTRATION_TECH_NONE), imsReasonInfo);
+        }
+    };
+
+    /**
+     * Need access to the listener in order to register for events in MMTelFeature adapter
+     */
+    public IImsRegistrationListener getRegistrationListener() {
+        return mListener;
+    }
+}
diff --git a/src/java/com/android/internal/telephony/ims/ImsResolver.java b/src/java/com/android/internal/telephony/ims/ImsResolver.java
index bb785e6..a7eac71 100644
--- a/src/java/com/android/internal/telephony/ims/ImsResolver.java
+++ b/src/java/com/android/internal/telephony/ims/ImsResolver.java
@@ -32,6 +32,11 @@
 import android.os.UserHandle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
+import android.telephony.ims.ImsService;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.aidl.IImsRcsFeature;
+import android.telephony.ims.aidl.IImsRegistration;
 import android.telephony.ims.feature.ImsFeature;
 import android.text.TextUtils;
 import android.util.ArraySet;
@@ -39,9 +44,6 @@
 import android.util.Pair;
 import android.util.SparseArray;
 
-import com.android.ims.internal.IImsMMTelFeature;
-import com.android.ims.internal.IImsRcsFeature;
-import com.android.ims.internal.IImsRegistration;
 import com.android.ims.internal.IImsServiceFeatureCallback;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.PhoneConstants;
@@ -72,7 +74,6 @@
 
     private static final String TAG = "ImsResolver";
 
-    public static final String SERVICE_INTERFACE = "android.telephony.ims.ImsService";
     public static final String METADATA_EMERGENCY_MMTEL_FEATURE =
             "android.telephony.ims.EMERGENCY_MMTEL_FEATURE";
     public static final String METADATA_MMTEL_FEATURE = "android.telephony.ims.MMTEL_FEATURE";
@@ -93,6 +94,8 @@
     public static class ImsServiceInfo {
         public ComponentName name;
         public Set<Integer> supportedFeatures;
+        public boolean supportsEmergencyMmTel = false;
+        public ImsServiceControllerFactory controllerFactory;
 
         @Override
         public boolean equals(Object o) {
@@ -102,15 +105,19 @@
             ImsServiceInfo that = (ImsServiceInfo) o;
 
             if (name != null ? !name.equals(that.name) : that.name != null) return false;
-            return supportedFeatures != null ? supportedFeatures.equals(that.supportedFeatures)
-                    : that.supportedFeatures == null;
-
+            if (supportedFeatures != null ? !supportedFeatures.equals(that.supportedFeatures)
+                    : that.supportedFeatures != null) {
+                return false;
+            }
+            return controllerFactory != null ? controllerFactory.equals(that.controllerFactory)
+                    : that.controllerFactory == null;
         }
 
         @Override
         public int hashCode() {
             int result = name != null ? name.hashCode() : 0;
             result = 31 * result + (supportedFeatures != null ? supportedFeatures.hashCode() : 0);
+            result = 31 * result + (controllerFactory != null ? controllerFactory.hashCode() : 0);
             return result;
         }
     }
@@ -195,14 +202,59 @@
     @VisibleForTesting
     public interface ImsServiceControllerFactory {
         /**
-         * Returns the ImsServiceController created usiing the context and componentName supplied.
-         * Used for DI when testing.
+         * @return the Service Interface String used for binding the ImsService.
          */
-        ImsServiceController get(Context context, ComponentName componentName);
+        String getServiceInterface();
+        /**
+         * @return the ImsServiceController created using the context and componentName supplied.
+         */
+        ImsServiceController create(Context context, ComponentName componentName,
+                ImsServiceController.ImsServiceControllerCallbacks callbacks);
     }
 
-    private ImsServiceControllerFactory mImsServiceControllerFactory = (context, componentName) ->
-            new ImsServiceController(context, componentName, this);
+    private ImsServiceControllerFactory mImsServiceControllerFactory =
+            new ImsServiceControllerFactory() {
+
+        @Override
+        public String getServiceInterface() {
+            return ImsService.SERVICE_INTERFACE;
+        }
+
+        @Override
+        public ImsServiceController create(Context context, ComponentName componentName,
+                ImsServiceController.ImsServiceControllerCallbacks callbacks) {
+            return new ImsServiceController(context, componentName, callbacks);
+        }
+    };
+
+    private ImsServiceControllerFactory mImsServiceControllerFactoryCompat =
+            new ImsServiceControllerFactory() {
+                @Override
+                public String getServiceInterface() {
+                    return android.telephony.ims.compat.ImsService.SERVICE_INTERFACE;
+                }
+
+                @Override
+                public ImsServiceController create(Context context, ComponentName componentName,
+                        ImsServiceController.ImsServiceControllerCallbacks callbacks) {
+                    return new ImsServiceControllerCompat(context, componentName, callbacks);
+                }
+            };
+
+    private ImsServiceControllerFactory mImsServiceControllerFactoryStaticBindingCompat =
+            new ImsServiceControllerFactory() {
+                @Override
+                public String getServiceInterface() {
+                    // The static method of binding does not use service interfaces.
+                    return null;
+                }
+
+                @Override
+                public ImsServiceController create(Context context, ComponentName componentName,
+                        ImsServiceController.ImsServiceControllerCallbacks callbacks) {
+                    return new ImsServiceControllerStaticCompat(context, componentName, callbacks);
+                }
+            };
 
     private final CarrierConfigManager mCarrierConfigManager;
     private final Context mContext;
@@ -210,6 +262,7 @@
     // ImsServiceController callbacks.
     private final Object mBoundServicesLock = new Object();
     private final int mNumSlots;
+    private final boolean mIsDynamicBinding;
 
     // Synchronize all messages on a handler to ensure that the cache includes the most recent
     // version of the installed ImsServices.
@@ -247,27 +300,40 @@
     private Set<ImsServiceInfo> mInstalledServicesCache = new ArraySet<>();
     // not locked, only accessed on a handler thread.
     private Set<ImsServiceController> mActiveControllers = new ArraySet<>();
+    // Only used as the
+    private final ComponentName mStaticComponent;
 
-    public ImsResolver(Context context, String defaultImsPackageName, int numSlots) {
+    public ImsResolver(Context context, String defaultImsPackageName, int numSlots,
+            boolean isDynamicBinding) {
         mContext = context;
         mDeviceService = defaultImsPackageName;
         mNumSlots = numSlots;
+        mIsDynamicBinding = isDynamicBinding;
+        mStaticComponent = new ComponentName(mContext, ImsResolver.class);
+        if (!mIsDynamicBinding) {
+            Log.i(TAG, "ImsResolver initialized with static binding.");
+            mDeviceService = mStaticComponent.getPackageName();
+        }
         mCarrierConfigManager = (CarrierConfigManager) mContext.getSystemService(
                 Context.CARRIER_CONFIG_SERVICE);
         mCarrierServices = new String[numSlots];
         mBoundImsServicesByFeature = Stream.generate(SparseArray<ImsServiceController>::new)
                 .limit(mNumSlots).collect(Collectors.toList());
 
-        IntentFilter appChangedFilter = new IntentFilter();
-        appChangedFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-        appChangedFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        appChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
-        appChangedFilter.addDataScheme("package");
-        context.registerReceiverAsUser(mAppChangedReceiver, UserHandle.ALL, appChangedFilter, null,
-                null);
+        // Only register for Package/CarrierConfig updates if dynamic binding.
+        if(mIsDynamicBinding) {
+            IntentFilter appChangedFilter = new IntentFilter();
+            appChangedFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+            appChangedFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+            appChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+            appChangedFilter.addDataScheme("package");
+            context.registerReceiverAsUser(mAppChangedReceiver, UserHandle.ALL, appChangedFilter,
+                    null,
+                    null);
 
-        context.registerReceiver(mConfigChangedReceiver, new IntentFilter(
-                CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+            context.registerReceiver(mConfigChangedReceiver, new IntentFilter(
+                    CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+        }
     }
 
     @VisibleForTesting
@@ -298,51 +364,62 @@
     }
 
     /**
-     * Returns the {@link IImsMMTelFeature} that corresponds to the given slot Id or {@link null} if
+     * Notify ImsService to enable IMS for the framework. This will trigger IMS registration and
+     * trigger ImsFeature status updates.
+     */
+    public void enableIms(int slotId) {
+        SparseArray<ImsServiceController> controllers = getImsServiceControllers(slotId);
+        if (controllers != null) {
+            for (int i = 0; i < controllers.size(); i++) {
+                int key = controllers.keyAt(i);
+                controllers.get(key).enableIms(slotId);
+            }
+        }
+    }
+
+    /**
+     * Notify ImsService to disable IMS for the framework. This will trigger IMS de-registration and
+     * trigger ImsFeature capability status to become false.
+     */
+    public void disableIms(int slotId) {
+        SparseArray<ImsServiceController> controllers = getImsServiceControllers(slotId);
+        if (controllers != null) {
+            for (int i = 0; i < controllers.size(); i++) {
+                int key = controllers.keyAt(i);
+                controllers.get(key).disableIms(slotId);
+            }
+        }
+    }
+
+    /**
+     * Returns the {@link IImsMmTelFeature} that corresponds to the given slot Id or {@link null} if
      * the service is not available. If an IImsMMTelFeature is available, the
      * {@link IImsServiceFeatureCallback} callback is registered as a listener for feature updates.
-     * @param slotId The SIM slot that we are requesting the {@link IImsMMTelFeature} for.
+     * @param slotId The SIM slot that we are requesting the {@link IImsMmTelFeature} for.
      * @param callback Listener that will send updates to ImsManager when there are updates to
      * the feature.
-     * @return {@link IImsMMTelFeature} interface or {@link null} if it is unavailable.
+     * @return {@link IImsMmTelFeature} interface or {@link null} if it is unavailable.
      */
-    public IImsMMTelFeature getMMTelFeatureAndListen(int slotId,
-            IImsServiceFeatureCallback callback) {
-        ImsServiceController controller = getImsServiceControllerAndListen(slotId, ImsFeature.MMTEL,
-                callback);
-        return (controller != null) ? controller.getMMTelFeature(slotId) : null;
-    }
-
-    /**
-     * Returns the {@link IImsMMTelFeature} that corresponds to the given slot Id for emergency
-     * calling or {@link null} if the service is not available. If an IImsMMTelFeature is
-     * available, the {@link IImsServiceFeatureCallback} callback is registered as a listener for
-     * feature updates.
-     * @param slotId The SIM slot that we are requesting the {@link IImsMMTelFeature} for.
-     * @param callback listener that will send updates to ImsManager when there are updates to
-     * the feature.
-     * @return {@link IImsMMTelFeature} interface or {@link null} if it is unavailable.
-     */
-    public IImsMMTelFeature getEmergencyMMTelFeatureAndListen(int slotId,
+    public IImsMmTelFeature getMmTelFeatureAndListen(int slotId,
             IImsServiceFeatureCallback callback) {
         ImsServiceController controller = getImsServiceControllerAndListen(slotId,
-                ImsFeature.EMERGENCY_MMTEL, callback);
-        return (controller != null) ? controller.getEmergencyMMTelFeature(slotId) : null;
+                ImsFeature.FEATURE_MMTEL, callback);
+        return (controller != null) ? controller.getMmTelFeature(slotId) : null;
     }
 
     /**
-     * Returns the {@link IImsMMTelFeature} that corresponds to the given slot Id for emergency
+     * Returns the {@link IImsRcsFeature} that corresponds to the given slot Id for emergency
      * calling or {@link null} if the service is not available. If an IImsMMTelFeature is
      * available, the {@link IImsServiceFeatureCallback} callback is registered as a listener for
      * feature updates.
-     * @param slotId The SIM slot that we are requesting the {@link IImsMMTelFeature} for.
+     * @param slotId The SIM slot that we are requesting the {@link IImsRcsFeature} for.
      * @param callback listener that will send updates to ImsManager when there are updates to
      * the feature.
-     * @return {@link IImsMMTelFeature} interface or {@link null} if it is unavailable.
+     * @return {@link IImsRcsFeature} interface or {@link null} if it is unavailable.
      */
     public IImsRcsFeature getRcsFeatureAndListen(int slotId, IImsServiceFeatureCallback callback) {
-        ImsServiceController controller = getImsServiceControllerAndListen(slotId, ImsFeature.RCS,
-                callback);
+        ImsServiceController controller = getImsServiceControllerAndListen(slotId,
+                ImsFeature.FEATURE_RCS, callback);
         return (controller != null) ? controller.getRcsFeature(slotId) : null;
     }
 
@@ -358,6 +435,18 @@
         return null;
     }
 
+    /**
+     * Returns the ImsConfig structure associated with the slotId and feature specified.
+     */
+    public @Nullable IImsConfig getImsConfig(int slotId, int feature)
+            throws RemoteException {
+        ImsServiceController controller = getImsServiceController(slotId, feature);
+        if (controller != null) {
+            return controller.getConfig(slotId);
+        }
+        return null;
+    }
+
     @VisibleForTesting
     public ImsServiceController getImsServiceController(int slotId, int feature) {
         if (slotId < 0 || slotId >= mNumSlots) {
@@ -374,6 +463,19 @@
         return controller;
     }
 
+    private  SparseArray<ImsServiceController> getImsServiceControllers(int slotId) {
+        if (slotId < 0 || slotId >= mNumSlots) {
+            return null;
+        }
+        synchronized (mBoundServicesLock) {
+            SparseArray<ImsServiceController> services = mBoundImsServicesByFeature.get(slotId);
+            if (services == null) {
+                return null;
+            }
+            return services;
+        }
+    }
+
     @VisibleForTesting
     public ImsServiceController getImsServiceControllerAndListen(int slotId, int feature,
             IImsServiceFeatureCallback callback) {
@@ -387,8 +489,8 @@
     }
 
     private void putImsController(int slotId, int feature, ImsServiceController controller) {
-        if (slotId < 0 || slotId >= mNumSlots || feature <= ImsFeature.INVALID
-                || feature >= ImsFeature.MAX) {
+        if (slotId < 0 || slotId >= mNumSlots || feature <= ImsFeature.FEATURE_INVALID
+                || feature >= ImsFeature.FEATURE_MAX) {
             Log.w(TAG, "putImsController received invalid parameters - slot: " + slotId
                     + ", feature: " + feature);
             return;
@@ -406,8 +508,8 @@
     }
 
     private ImsServiceController removeImsController(int slotId, int feature) {
-        if (slotId < 0 || slotId >= mNumSlots || feature <= ImsFeature.INVALID
-                || feature >= ImsFeature.MAX) {
+        if (slotId < 0 || slotId >= mNumSlots || feature <= ImsFeature.FEATURE_INVALID
+                || feature >= ImsFeature.FEATURE_MAX) {
             Log.w(TAG, "removeImsController received invalid parameters - slot: " + slotId
                     + ", feature: " + feature);
             return null;
@@ -427,7 +529,6 @@
         }
     }
 
-
     // Update the current cache with the new ImsService(s) if it has been added or update the
     // supported IMS features if they have changed.
     // Called from the handler ONLY
@@ -568,7 +669,7 @@
         if (info == null) {
             return;
         }
-        ImsServiceController controller = mImsServiceControllerFactory.get(mContext, info.name);
+        ImsServiceController controller = info.controllerFactory.create(mContext, info.name, this);
         HashSet<Pair<Integer, Integer>> features = calculateFeaturesToCreate(info);
         // Only bind if there are features that will be created by the service.
         if (features.size() > 0) {
@@ -710,8 +811,36 @@
     // get all packages that support ImsServices.
     private List<ImsServiceInfo> getImsServiceInfo(String packageName) {
         List<ImsServiceInfo> infos = new ArrayList<>();
+        if (!mIsDynamicBinding) {
+            // always return the same ImsService info.
+            infos.addAll(getStaticImsService());
+        } else {
+            // Search for Current ImsService implementations
+            infos.addAll(searchForImsServices(packageName, mImsServiceControllerFactory));
+            // Search for compat ImsService Implementations
+            infos.addAll(searchForImsServices(packageName, mImsServiceControllerFactoryCompat));
+        }
+        return infos;
+    }
 
-        Intent serviceIntent = new Intent(SERVICE_INTERFACE);
+    private List<ImsServiceInfo> getStaticImsService() {
+        List<ImsServiceInfo> infos = new ArrayList<>();
+
+        ImsServiceInfo info = new ImsServiceInfo();
+        info.name = mStaticComponent;
+        info.supportedFeatures = new HashSet<>(ImsFeature.FEATURE_MAX);
+        info.controllerFactory = mImsServiceControllerFactoryStaticBindingCompat;
+        info.supportsEmergencyMmTel = true;
+        info.supportedFeatures.add(ImsFeature.FEATURE_MMTEL);
+        infos.add(info);
+        return infos;
+    }
+
+    private List<ImsServiceInfo> searchForImsServices(String packageName,
+            ImsServiceControllerFactory controllerFactory) {
+        List<ImsServiceInfo> infos = new ArrayList<>();
+
+        Intent serviceIntent = new Intent(controllerFactory.getServiceInterface());
         serviceIntent.setPackage(packageName);
 
         PackageManager packageManager = mContext.getPackageManager();
@@ -724,25 +853,26 @@
             if (serviceInfo != null) {
                 ImsServiceInfo info = new ImsServiceInfo();
                 info.name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
-                info.supportedFeatures = new HashSet<>(ImsFeature.MAX);
+                info.supportedFeatures = new HashSet<>(ImsFeature.FEATURE_MAX);
+                info.controllerFactory = controllerFactory;
                 // Add all supported features
                 if (serviceInfo.metaData != null) {
                     if (serviceInfo.metaData.getBoolean(METADATA_EMERGENCY_MMTEL_FEATURE, false)) {
-                        info.supportedFeatures.add(ImsFeature.EMERGENCY_MMTEL);
+                        info.supportsEmergencyMmTel = true;
                     }
                     if (serviceInfo.metaData.getBoolean(METADATA_MMTEL_FEATURE, false)) {
-                        info.supportedFeatures.add(ImsFeature.MMTEL);
+                        info.supportedFeatures.add(ImsFeature.FEATURE_MMTEL);
                     }
                     if (serviceInfo.metaData.getBoolean(METADATA_RCS_FEATURE, false)) {
-                        info.supportedFeatures.add(ImsFeature.RCS);
+                        info.supportedFeatures.add(ImsFeature.FEATURE_RCS);
                     }
                 }
                 // Check manifest permission to be sure that the service declares the correct
                 // permissions.
                 if (TextUtils.equals(serviceInfo.permission,
                         Manifest.permission.BIND_IMS_SERVICE)) {
-                    Log.d(TAG, "ImsService added to cache: " + info.name + " with features: "
-                            + info.supportedFeatures);
+                    Log.d(TAG, "ImsService (" + serviceIntent + ") added to cache: "
+                            + info.name + " with features: " + info.supportedFeatures);
                     infos.add(info);
                 } else {
                     Log.w(TAG, "ImsService does not have 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 284c9de..34fcb81 100644
--- a/src/java/com/android/internal/telephony/ims/ImsServiceController.java
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
@@ -27,15 +27,17 @@
 import android.os.IInterface;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.telephony.ims.ImsService;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.aidl.IImsRcsFeature;
+import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.aidl.IImsServiceController;
 import android.telephony.ims.feature.ImsFeature;
 import android.util.Log;
 import android.util.Pair;
 
 import com.android.ims.internal.IImsFeatureStatusCallback;
-import com.android.ims.internal.IImsMMTelFeature;
-import com.android.ims.internal.IImsRcsFeature;
-import com.android.ims.internal.IImsRegistration;
-import com.android.ims.internal.IImsServiceController;
 import com.android.ims.internal.IImsServiceFeatureCallback;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.ExponentialBackoff;
@@ -94,7 +96,7 @@
                     try {
                         service.linkToDeath(mImsDeathRecipient, 0);
                         mImsServiceControllerBinder = service;
-                        mIImsServiceController = IImsServiceController.Stub.asInterface(service);
+                        setServiceController(service);
                         // create all associated features in the ImsService
                         for (Pair<Integer, Integer> i : mImsFeatures) {
                             addImsServiceFeature(i);
@@ -118,7 +120,7 @@
             synchronized (mLock) {
                 mIsBinding = false;
             }
-            if (mIImsServiceController != null) {
+            if (isServiceControllerAvailable()) {
                 mImsServiceControllerBinder.unlinkToDeath(mImsDeathRecipient, 0);
             }
             notifyAllFeaturesRemoved();
@@ -164,9 +166,7 @@
     private static final String LOG_TAG = "ImsServiceController";
     private static final int REBIND_START_DELAY_MS = 2 * 1000; // 2 seconds
     private static final int REBIND_MAXIMUM_DELAY_MS = 60 * 1000; // 1 minute
-    private final Context mContext;
     private final ComponentName mComponentName;
-    private final Object mLock = new Object();
     private final HandlerThread mHandlerThread = new HandlerThread("ImsServiceControllerHandler");
     private final IPackageManager mPackageManager;
     private ImsServiceControllerCallbacks mCallbacks;
@@ -187,6 +187,9 @@
     // Only added or removed, never accessed on purpose.
     private Set<ImsFeatureStatusCallback> mFeatureStatusCallbacks = new HashSet<>();
 
+    protected final Object mLock = new Object();
+    protected final Context mContext;
+
     private class ImsFeatureContainer {
         public int slotId;
         public int featureType;
@@ -324,15 +327,19 @@
             if (!mIsBound && !mIsBinding) {
                 mIsBinding = true;
                 mImsFeatures = imsFeatureSet;
-                Intent imsServiceIntent = new Intent(ImsResolver.SERVICE_INTERFACE).setComponent(
+                Intent imsServiceIntent = new Intent(getServiceInterface()).setComponent(
                         mComponentName);
                 mImsServiceConnection = new ImsServiceConnection();
                 int serviceFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
                         | Context.BIND_IMPORTANT;
                 Log.i(LOG_TAG, "Binding ImsService:" + mComponentName);
                 try {
-                    return mContext.bindService(imsServiceIntent, mImsServiceConnection,
-                            serviceFlags);
+                    boolean bindSucceeded = startBindToService(imsServiceIntent,
+                            mImsServiceConnection, serviceFlags);
+                    if (!bindSucceeded) {
+                        mBackoff.notifyFailed();
+                    }
+                    return bindSucceeded;
                 } catch (Exception e) {
                     mBackoff.notifyFailed();
                     Log.e(LOG_TAG, "Error binding (" + mComponentName + ") with exception: "
@@ -347,6 +354,15 @@
     }
 
     /**
+     * Starts the bind to the ImsService. Overridden by subclasses that need to access the service
+     * in a different fashion.
+     */
+    protected boolean startBindToService(Intent intent, ImsServiceConnection connection,
+            int flags) {
+        return mContext.bindService(intent, connection, flags);
+    }
+
+    /**
      * Calls {@link IImsServiceController#removeImsFeature} on all features that the
      * ImsService supports and then unbinds the service.
      */
@@ -421,33 +437,34 @@
         }
     }
 
-    /**
-     * Return the {@Link MMTelFeature} binder on the slot associated with the slotId.
-     * Used for normal calling.
-     */
-    public IImsMMTelFeature getMMTelFeature(int slotId) {
-        synchronized (mLock) {
-            ImsFeatureContainer f = getImsFeatureContainer(slotId, ImsFeature.MMTEL);
-            if (f == null) {
-                Log.w(LOG_TAG, "Requested null MMTelFeature on slot " + slotId);
-                return null;
-            }
-            return f.resolve(IImsMMTelFeature.class);
+    public void enableIms(int slotId) {
+        try {
+            mIImsServiceController.enableIms(slotId);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Couldn't enable IMS: " + e.getMessage());
+        }
+    }
+
+    public void disableIms(int slotId) {
+        try {
+            mIImsServiceController.disableIms(slotId);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Couldn't disable IMS: " + e.getMessage());
         }
     }
 
     /**
      * Return the {@Link MMTelFeature} binder on the slot associated with the slotId.
-     * Used for emergency calling only.
+     * Used for normal calling.
      */
-    public IImsMMTelFeature getEmergencyMMTelFeature(int slotId) {
+    public IImsMmTelFeature getMmTelFeature(int slotId) {
         synchronized (mLock) {
-            ImsFeatureContainer f = getImsFeatureContainer(slotId, ImsFeature.EMERGENCY_MMTEL);
+            ImsFeatureContainer f = getImsFeatureContainer(slotId, ImsFeature.FEATURE_MMTEL);
             if (f == null) {
-                Log.w(LOG_TAG, "Requested null Emergency MMTelFeature on slot " + slotId);
+                Log.w(LOG_TAG, "Requested null MMTelFeature on slot " + slotId);
                 return null;
             }
-            return f.resolve(IImsMMTelFeature.class);
+            return f.resolve(IImsMmTelFeature.class);
         }
     }
 
@@ -456,7 +473,7 @@
      */
     public IImsRcsFeature getRcsFeature(int slotId) {
         synchronized (mLock) {
-            ImsFeatureContainer f = getImsFeatureContainer(slotId, ImsFeature.RCS);
+            ImsFeatureContainer f = getImsFeatureContainer(slotId, ImsFeature.FEATURE_RCS);
             if (f == null) {
                 Log.w(LOG_TAG, "Requested null RcsFeature on slot " + slotId);
                 return null;
@@ -474,6 +491,35 @@
         }
     }
 
+    /**
+     * @return the IImsConfig that corresponds to the slot id specified.
+     */
+    public IImsConfig getConfig(int slotId) throws RemoteException {
+        synchronized (mLock) {
+            return mIImsServiceController.getConfig(slotId);
+        }
+    }
+
+    protected String getServiceInterface() {
+        return ImsService.SERVICE_INTERFACE;
+    }
+
+    /**
+     * Sets the IImsServiceController instance. Overridden by compat layers to set compatibility
+     * versions of this service controller.
+     */
+    protected void setServiceController(IBinder serviceController) {
+        mIImsServiceController = IImsServiceController.Stub.asInterface(serviceController);
+    }
+
+    /**
+     * Check to see if the service controller is available, overridden for compat versions,
+     * @return true if available, false otherwise;
+     */
+    protected boolean isServiceControllerAvailable() {
+        return mIImsServiceController != null;
+    }
+
     private void removeImsServiceFeatureListener() {
         synchronized (mLock) {
             mImsStatusCallbacks.clear();
@@ -553,7 +599,7 @@
 
     // This method should only be called when synchronized on mLock
     private void addImsServiceFeature(Pair<Integer, Integer> featurePair) throws RemoteException {
-        if (mIImsServiceController == null || mCallbacks == null) {
+        if (!isServiceControllerAvailable() || mCallbacks == null) {
             Log.w(LOG_TAG, "addImsServiceFeature called with null values.");
             return;
         }
@@ -571,7 +617,7 @@
     // This method should only be called when synchronized on mLock
     private void removeImsServiceFeature(Pair<Integer, Integer> featurePair)
             throws RemoteException {
-        if (mIImsServiceController == null || mCallbacks == null) {
+        if (!isServiceControllerAvailable() || mCallbacks == null) {
             Log.w(LOG_TAG, "removeImsServiceFeature called with null values.");
             return;
         }
@@ -582,7 +628,7 @@
         if (callbackToRemove != null) {
             mFeatureStatusCallbacks.remove(callbackToRemove);
         }
-        mIImsServiceController.removeImsFeature(featurePair.first, featurePair.second,
+        removeImsFeature(featurePair.first, featurePair.second,
                 (callbackToRemove != null ? callbackToRemove.getCallback() : null));
         removeImsFeatureBinder(featurePair.first, featurePair.second);
         // Signal ImsResolver to change supported ImsFeatures for this ImsServiceController
@@ -594,17 +640,15 @@
         sendImsFeatureRemovedCallback(featurePair.first, featurePair.second);
     }
 
-    // This method should only be called when already synchronized on mLock;
-    private IInterface createImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
+    // This method should only be called when already synchronized on mLock.
+    // overridden by compat layer to create features
+    protected IInterface createImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
             throws RemoteException {
         switch (featureType) {
-            case ImsFeature.EMERGENCY_MMTEL: {
-                return mIImsServiceController.createEmergencyMMTelFeature(slotId, c);
+            case ImsFeature.FEATURE_MMTEL: {
+                return mIImsServiceController.createMmTelFeature(slotId, c);
             }
-            case ImsFeature.MMTEL: {
-                return mIImsServiceController.createMMTelFeature(slotId, c);
-            }
-            case ImsFeature.RCS: {
+            case ImsFeature.FEATURE_RCS: {
                 return mIImsServiceController.createRcsFeature(slotId, c);
             }
             default:
@@ -612,6 +656,12 @@
         }
     }
 
+    // overridden by compat layer to remove features
+    protected void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
+            throws RemoteException {
+        mIImsServiceController.removeImsFeature(slotId, featureType, c);
+    }
+
     // This method should only be called when synchronized on mLock
     private void addImsFeatureBinder(int slotId, int featureType, IInterface b) {
         mImsFeatureBinders.add(new ImsFeatureContainer(slotId, featureType, b));
@@ -651,7 +701,7 @@
             mImsDeathRecipient = null;
             mImsServiceConnection = null;
             mImsServiceControllerBinder = null;
-            mIImsServiceController = null;
+            setServiceController(null);
             mIsBound = false;
         }
     }
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceControllerCompat.java b/src/java/com/android/internal/telephony/ims/ImsServiceControllerCompat.java
new file mode 100644
index 0000000..094be71
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceControllerCompat.java
@@ -0,0 +1,191 @@
+/*
+ * 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.ims;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.RemoteException;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.aidl.IImsRcsFeature;
+import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.compat.ImsService;
+import android.telephony.ims.compat.feature.ImsFeature;
+import android.telephony.ims.compat.feature.MMTelFeature;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.ims.internal.IImsMMTelFeature;
+import com.android.ims.internal.IImsServiceController;
+
+/**
+ * Manages the Binding lifecycle of one ImsService as well as the relevant ImsFeatures that the
+ * ImsService will support.
+ *
+ * Compatibility interface for interacting with older implementations of ImsService. The older
+ * ImsService implementation is contained within the android.telephony.ims.compat.* namspace.
+ * Newer implementations of ImsService should use the current APIs contained in
+ * android.telephony.ims.compat.*.
+ */
+public class ImsServiceControllerCompat extends ImsServiceController {
+
+    private static final String TAG = "ImsSCCompat";
+
+    private IImsServiceController mServiceController;
+
+    private final SparseArray<MmTelFeatureCompatAdapter> mMmTelCompatAdapters = new SparseArray<>();
+    private final SparseArray<ImsConfigCompatAdapter> mConfigCompatAdapters = new SparseArray<>();
+    private final SparseArray<ImsRegistrationCompatAdapter> mRegCompatAdapters =
+            new SparseArray<>();
+
+    public ImsServiceControllerCompat(Context context, ComponentName componentName,
+            ImsServiceController.ImsServiceControllerCallbacks callbacks) {
+        super(context, componentName, callbacks);
+    }
+
+    @Override
+    protected String getServiceInterface() {
+        // Return compatibility version of String.
+        return ImsService.SERVICE_INTERFACE;
+    }
+
+    /**
+     * Converts the new command to {@link MMTelFeature#turnOnIms()}.
+     */
+    @Override
+    public void enableIms(int slotId) {
+        MmTelFeatureCompatAdapter adapter = mMmTelCompatAdapters.get(slotId);
+        if (adapter == null) {
+            Log.w(TAG, "enableIms: adapter null for slot :" + slotId);
+            return;
+        }
+        try {
+            adapter.enableIms();
+        } catch (RemoteException e) {
+            Log.w(TAG, "Couldn't enable IMS: " + e.getMessage());
+        }
+    }
+
+    /**
+     * Converts the new command to {@link MMTelFeature#turnOffIms()}.
+     */
+    @Override
+    public void disableIms(int slotId) {
+        MmTelFeatureCompatAdapter adapter = mMmTelCompatAdapters.get(slotId);
+        if (adapter == null) {
+            Log.w(TAG, "enableIms: adapter null for slot :" + slotId);
+            return;
+        }
+        try {
+            adapter.disableIms();
+        } catch (RemoteException e) {
+            Log.w(TAG, "Couldn't enable IMS: " + e.getMessage());
+        }
+    }
+
+    /**
+     * @return the IImsRegistration that corresponds to the slot id specified.
+     */
+    public IImsRegistration getRegistration(int slotId) throws RemoteException {
+        ImsRegistrationCompatAdapter adapter = mRegCompatAdapters.get(slotId);
+        if (adapter == null) {
+            Log.w(TAG, "getRegistration: Registration does not exist for slot " + slotId);
+            return null;
+        }
+        return adapter.getBinder();
+    }
+
+    /**
+     * @return the IImsConfig that corresponds to the slot id specified.
+     */
+    public IImsConfig getConfig(int slotId) throws RemoteException {
+        ImsConfigCompatAdapter adapter = mConfigCompatAdapters.get(slotId);
+        if (adapter == null) {
+            Log.w(TAG, "getConfig: Config does not exist for slot " + slotId);
+            return null;
+        }
+        return adapter.getIImsConfig();
+    }
+
+    @Override
+    protected IInterface createImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
+            throws RemoteException {
+        switch (featureType) {
+            case ImsFeature.MMTEL: {
+                return createMMTelCompat(slotId, c);
+            }
+            case ImsFeature.RCS: {
+                return createRcsFeature(slotId, c);
+            }
+            default:
+                return null;
+        }
+    }
+
+    @Override
+    protected void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
+            throws RemoteException {
+        if (featureType == ImsFeature.MMTEL) {
+            mMmTelCompatAdapters.remove(slotId);
+            mRegCompatAdapters.remove(slotId);
+            mConfigCompatAdapters.remove(slotId);
+        }
+        mServiceController.removeImsFeature(slotId, featureType, c);
+    }
+
+    @Override
+    protected void setServiceController(IBinder serviceController) {
+        mServiceController = IImsServiceController.Stub.asInterface(serviceController);
+    }
+
+    @Override
+    protected boolean isServiceControllerAvailable() {
+        return mServiceController != null;
+    }
+
+    protected MmTelInterfaceAdapter getInterface(int slotId, IImsFeatureStatusCallback c)
+            throws RemoteException {
+        IImsMMTelFeature feature = mServiceController.createMMTelFeature(slotId, c);
+        if (feature == null) {
+            Log.w(TAG, "createMMTelCompat: createMMTelFeature returned null.");
+            return null;
+        }
+        return new MmTelInterfaceAdapter(slotId, feature.asBinder());
+    }
+
+    private IImsMmTelFeature createMMTelCompat(int slotId, IImsFeatureStatusCallback c)
+            throws RemoteException {
+        MmTelInterfaceAdapter interfaceAdapter = getInterface(slotId, c);
+        MmTelFeatureCompatAdapter mmTelAdapter = new MmTelFeatureCompatAdapter(mContext, slotId,
+                interfaceAdapter);
+        mMmTelCompatAdapters.put(slotId, mmTelAdapter);
+        ImsRegistrationCompatAdapter regAdapter = new ImsRegistrationCompatAdapter();
+        mmTelAdapter.addRegistrationAdapter(regAdapter);
+        mRegCompatAdapters.put(slotId, regAdapter);
+        mConfigCompatAdapters.put(slotId, new ImsConfigCompatAdapter(
+                mmTelAdapter.getOldConfigInterface()));
+        return mmTelAdapter.getBinder();
+    }
+
+    private IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) {
+        // Return non-null if there is a custom RCS implementation that needs a compatability layer.
+        return null;
+    }
+}
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceControllerStaticCompat.java b/src/java/com/android/internal/telephony/ims/ImsServiceControllerStaticCompat.java
new file mode 100644
index 0000000..5b080d1
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceControllerStaticCompat.java
@@ -0,0 +1,77 @@
+/*
+ * 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.ims;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.ims.internal.IImsService;
+
+/**
+ * A compat layer for communicating with older devices that still used the ServiceManager to get
+ * the ImsService.
+ */
+
+public class ImsServiceControllerStaticCompat extends ImsServiceControllerCompat {
+
+    private static final String TAG = "ImsSCStaticCompat";
+
+    private static final String IMS_SERVICE_NAME = "ims";
+
+    private IImsService mImsServiceCompat = null;
+
+    public ImsServiceControllerStaticCompat(Context context, ComponentName componentName,
+            ImsServiceController.ImsServiceControllerCallbacks callbacks) {
+        super(context, componentName, callbacks);
+    }
+
+    @Override
+    public boolean startBindToService(Intent intent, ImsServiceConnection connection, int flags) {
+        IBinder binder = ServiceManager.checkService(IMS_SERVICE_NAME);
+
+        if (binder == null) {
+            return false;
+        }
+        // This is a little hacky, but we are going to call the onServiceConnected to "pretend" like
+        // bindService has completed here, which will pass the binder to setServiceController and
+        // set up all supporting structures.
+        connection.onServiceConnected(new ComponentName(mContext,
+                ImsServiceControllerStaticCompat.class), binder);
+        return true;
+    }
+
+    @Override
+    protected void setServiceController(IBinder serviceController) {
+        mImsServiceCompat = IImsService.Stub.asInterface(serviceController);
+    }
+
+    @Override
+    protected MmTelInterfaceAdapter getInterface(int slotId, IImsFeatureStatusCallback c)
+            throws RemoteException {
+        if (mImsServiceCompat == null) {
+            Log.w(TAG, "getInterface: IImsService returned null.");
+            return null;
+        }
+        return new ImsServiceInterfaceAdapter(slotId, mImsServiceCompat.asBinder());
+    }
+}
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceInterfaceAdapter.java b/src/java/com/android/internal/telephony/ims/ImsServiceInterfaceAdapter.java
new file mode 100644
index 0000000..f554e6f
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceInterfaceAdapter.java
@@ -0,0 +1,128 @@
+/*
+ * 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.ims;
+
+import android.app.PendingIntent;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.compat.feature.ImsFeature;
+
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsConfig;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsRegistrationListener;
+import com.android.ims.internal.IImsService;
+import com.android.ims.internal.IImsUt;
+
+/**
+ * Compatibility layer for IImsService implementations of IMS. Converts "generic" MMTel commands
+ * to implementation.
+ */
+
+public class ImsServiceInterfaceAdapter extends MmTelInterfaceAdapter {
+
+    private static final int SERVICE_ID = ImsFeature.MMTEL;
+
+    public ImsServiceInterfaceAdapter(int slotId, IBinder binder) {
+        super(slotId, binder);
+    }
+
+    public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
+            throws RemoteException {
+        return getInterface().open(mSlotId, ImsFeature.MMTEL, incomingCallIntent, listener);
+    }
+
+    public void endSession(int sessionId) throws RemoteException {
+        getInterface().close(sessionId);
+    }
+
+    public boolean isConnected(int callSessionType, int callType) throws RemoteException {
+        return getInterface().isConnected(SERVICE_ID, callSessionType, callType);
+    }
+
+    public boolean isOpened() throws RemoteException {
+        return getInterface().isOpened(SERVICE_ID);
+    }
+
+    public int getFeatureState() throws RemoteException {
+        return ImsFeature.STATE_READY;
+    }
+
+    public void addRegistrationListener(IImsRegistrationListener listener) throws RemoteException {
+        getInterface().addRegistrationListener(mSlotId, ImsFeature.MMTEL, listener);
+    }
+
+    public void removeRegistrationListener(IImsRegistrationListener listener)
+            throws RemoteException {
+        // Not Implemented in the old ImsService. If the registration listener becomes invalid, the
+        // ImsService will remove it.
+    }
+
+    public ImsCallProfile createCallProfile(int sessionId, int callSessionType, int callType)
+            throws RemoteException {
+        return getInterface().createCallProfile(sessionId, callSessionType, callType);
+    }
+
+    public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile)
+            throws RemoteException {
+        return getInterface().createCallSession(sessionId, profile, null);
+    }
+
+    public IImsCallSession getPendingCallSession(int sessionId, String callId)
+            throws RemoteException {
+        return getInterface().getPendingCallSession(sessionId, callId);
+    }
+
+    public IImsUt getUtInterface() throws RemoteException {
+        return getInterface().getUtInterface(SERVICE_ID);
+    }
+
+    public IImsConfig getConfigInterface() throws RemoteException {
+        return getInterface().getConfigInterface(mSlotId);
+    }
+
+    public void turnOnIms() throws RemoteException {
+        getInterface().turnOnIms(mSlotId);
+    }
+
+    public void turnOffIms() throws RemoteException {
+        getInterface().turnOffIms(mSlotId);
+    }
+
+    public IImsEcbm getEcbmInterface() throws RemoteException {
+        return getInterface().getEcbmInterface(SERVICE_ID);
+    }
+
+    public void setUiTTYMode(int uiTtyMode, Message onComplete) throws RemoteException {
+        getInterface().setUiTTYMode(SERVICE_ID, uiTtyMode, onComplete);
+    }
+
+    public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
+        return getInterface().getMultiEndpointInterface(SERVICE_ID);
+    }
+
+    private IImsService getInterface() throws RemoteException {
+        IImsService feature = IImsService.Stub.asInterface(mBinder);
+        if (feature == null) {
+            throw new RemoteException("Binder not Available");
+        }
+        return feature;
+    }
+}
diff --git a/src/java/com/android/internal/telephony/ims/MmTelFeatureCompatAdapter.java b/src/java/com/android/internal/telephony/ims/MmTelFeatureCompatAdapter.java
new file mode 100644
index 0000000..3236e9a
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ims/MmTelFeatureCompatAdapter.java
@@ -0,0 +1,542 @@
+/*
+ * 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.ims;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.feature.CapabilityChangeRequest;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.Log;
+
+import com.android.ims.ImsConfigListener;
+import com.android.ims.ImsManager;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsConfig;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsRegistrationListener;
+import com.android.ims.internal.IImsUt;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class MmTelFeatureCompatAdapter extends MmTelFeature {
+
+    private static final String TAG = "MmTelFeatureCompat";
+
+    public static final String ACTION_IMS_INCOMING_CALL = "com.android.ims.IMS_INCOMING_CALL";
+
+    private static final int WAIT_TIMEOUT_MS = 5000;
+
+    private final MmTelInterfaceAdapter mCompatFeature;
+    private ImsRegistrationCompatAdapter mRegCompatAdapter;
+    private int mSessionId = -1;
+
+    private static final Map<Integer, Integer> REG_TECH_TO_NET_TYPE = new HashMap<>(2);
+
+    static {
+        REG_TECH_TO_NET_TYPE.put(ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
+                TelephonyManager.NETWORK_TYPE_LTE);
+        REG_TECH_TO_NET_TYPE.put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
+                TelephonyManager.NETWORK_TYPE_IWLAN);
+    }
+
+    // Feature Type for compatibility with old "feature" updates
+    public static final int FEATURE_TYPE_UNKNOWN = -1;
+    public static final int FEATURE_TYPE_VOICE_OVER_LTE = 0;
+    public static final int FEATURE_TYPE_VIDEO_OVER_LTE = 1;
+    public static final int FEATURE_TYPE_VOICE_OVER_WIFI = 2;
+    public static final int FEATURE_TYPE_VIDEO_OVER_WIFI = 3;
+    public static final int FEATURE_TYPE_UT_OVER_LTE = 4;
+    public static final int FEATURE_TYPE_UT_OVER_WIFI = 5;
+
+    public static final int FEATURE_UNKNOWN = -1;
+    public static final int FEATURE_DISABLED = 0;
+    public static final int FEATURE_ENABLED = 1;
+
+    private static class ConfigListener extends ImsConfigListener.Stub {
+
+        private final int mCapability;
+        private final int mTech;
+        private final CountDownLatch mLatch;
+
+        public ConfigListener(int capability, int tech, CountDownLatch latch) {
+            mCapability = capability;
+            mTech = tech;
+            mLatch = latch;
+        }
+
+        @Override
+        public void onGetFeatureResponse(int feature, int network, int value, int status)
+                throws RemoteException {
+            if (feature == mCapability && network == mTech) {
+                mLatch.countDown();
+                getFeatureValueReceived(value);
+            } else {
+                Log.i(TAG, "onGetFeatureResponse: response different than requested: feature="
+                        + feature + " and network=" + network);
+            }
+        }
+
+        @Override
+        public void onSetFeatureResponse(int feature, int network, int value, int status)
+                throws RemoteException {
+            if (feature == mCapability && network == mTech) {
+                mLatch.countDown();
+                setFeatureValueReceived(value);
+            } else {
+                Log.i(TAG, "onSetFeatureResponse: response different than requested: feature="
+                        + feature + " and network=" + network);
+            }
+        }
+
+        @Override
+        public void onGetVideoQuality(int status, int quality) throws RemoteException {
+        }
+
+        @Override
+        public void onSetVideoQuality(int status) throws RemoteException {
+        }
+
+        public void getFeatureValueReceived(int value) {
+        }
+
+        public void setFeatureValueReceived(int value) {
+        }
+    }
+
+    // Trampolines "old" listener events to the new interface.
+    private final IImsRegistrationListener mListener = new IImsRegistrationListener.Stub() {
+        @Override
+        public void registrationConnected() throws RemoteException {
+            // Implemented in the Registration Adapter
+        }
+
+        @Override
+        public void registrationProgressing() throws RemoteException {
+            // Implemented in the Registration Adapter
+        }
+
+        @Override
+        public void registrationConnectedWithRadioTech(int imsRadioTech) throws RemoteException {
+            // Implemented in the Registration Adapter
+        }
+
+        @Override
+        public void registrationProgressingWithRadioTech(int imsRadioTech) throws RemoteException {
+            // Implemented in the Registration Adapter
+        }
+
+        @Override
+        public void registrationDisconnected(ImsReasonInfo imsReasonInfo) throws RemoteException {
+            // Implemented in the Registration Adapter
+        }
+
+        @Override
+        public void registrationResumed() throws RemoteException {
+            // Don't care
+        }
+
+        @Override
+        public void registrationSuspended() throws RemoteException {
+            // Don't care
+        }
+
+        @Override
+        public void registrationServiceCapabilityChanged(int serviceClass, int event)
+                throws RemoteException {
+            // Don't care
+        }
+
+        @Override
+        public void registrationFeatureCapabilityChanged(int serviceClass, int[] enabledFeatures,
+                int[] disabledFeatures) throws RemoteException {
+            notifyCapabilitiesStatusChanged(convertCapabilities(enabledFeatures));
+        }
+
+        @Override
+        public void voiceMessageCountUpdate(int count) throws RemoteException {
+            notifyVoiceMessageCountUpdate(count);
+        }
+
+        @Override
+        public void registrationAssociatedUriChanged(Uri[] uris) throws RemoteException {
+            // Implemented in the Registration Adapter
+        }
+
+        @Override
+        public void registrationChangeFailed(int targetAccessTech, ImsReasonInfo imsReasonInfo)
+                throws RemoteException {
+            // Implemented in the Registration Adapter
+        }
+    };
+
+    /**
+     * Stub implementation of the "old" Registration listener interface that provides no
+     * functionality. Instead, it is used to ensure compatibility with older devices that require
+     * a listener on startSession. The actual Registration Listener Interface is added separately
+     * in ImsRegistration.
+     */
+    private class ImsRegistrationListenerBase extends IImsRegistrationListener.Stub {
+
+        @Override
+        public void registrationConnected() throws RemoteException {
+        }
+
+        @Override
+        public void registrationProgressing() throws RemoteException {
+        }
+
+        @Override
+        public void registrationConnectedWithRadioTech(int imsRadioTech) throws RemoteException {
+        }
+
+        @Override
+        public void registrationProgressingWithRadioTech(int imsRadioTech) throws RemoteException {
+        }
+
+        @Override
+        public void registrationDisconnected(ImsReasonInfo imsReasonInfo) throws RemoteException {
+        }
+
+        @Override
+        public void registrationResumed() throws RemoteException {
+        }
+
+        @Override
+        public void registrationSuspended() throws RemoteException {
+        }
+
+        @Override
+        public void registrationServiceCapabilityChanged(int serviceClass, int event)
+                throws RemoteException {
+        }
+
+        @Override
+        public void registrationFeatureCapabilityChanged(int serviceClass, int[] enabledFeatures,
+                int[] disabledFeatures) throws RemoteException {
+        }
+
+        @Override
+        public void voiceMessageCountUpdate(int count) throws RemoteException {
+        }
+
+        @Override
+        public void registrationAssociatedUriChanged(Uri[] uris) throws RemoteException {
+        }
+
+        @Override
+        public void registrationChangeFailed(int targetAccessTech, ImsReasonInfo imsReasonInfo)
+                throws RemoteException {
+        }
+    }
+
+    // Handle Incoming Call as PendingIntent, the old method
+    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Log.i(TAG, "onReceive");
+            if (intent.getAction().equals(ACTION_IMS_INCOMING_CALL)) {
+                Log.i(TAG, "onReceive : incoming call intent.");
+
+                String callId = intent.getStringExtra("android:imsCallID");
+                try {
+                    IImsCallSession session = mCompatFeature.getPendingCallSession(mSessionId,
+                            callId);
+                    notifyIncomingCallSession(session, intent.getExtras());
+                } catch (RemoteException e) {
+                    Log.w(TAG, "onReceive: Couldn't get Incoming call session.");
+                }
+            }
+        }
+    };
+
+    public MmTelFeatureCompatAdapter(Context context, int slotId,
+            MmTelInterfaceAdapter compatFeature) {
+        initialize(context, slotId);
+        mCompatFeature = compatFeature;
+    }
+
+    @Override
+    public boolean queryCapabilityConfiguration(int capability, int radioTech) {
+        int capConverted = convertCapability(capability, radioTech);
+        // Wait for the result from the ImsService
+        CountDownLatch latch = new CountDownLatch(1);
+        final int[] returnValue = new int[1];
+        returnValue[0] = FEATURE_UNKNOWN;
+        int regTech = REG_TECH_TO_NET_TYPE.getOrDefault(radioTech,
+                ImsRegistrationImplBase.REGISTRATION_TECH_NONE);
+        try {
+            mCompatFeature.getConfigInterface().getFeatureValue(capConverted, regTech,
+                    new ConfigListener(capConverted, regTech, latch) {
+                        @Override
+                        public void getFeatureValueReceived(int value) {
+                            returnValue[0] = value;
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.w(TAG, "queryCapabilityConfiguration");
+        }
+        try {
+            latch.await(WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            Log.w(TAG, "queryCapabilityConfiguration - error waiting: " + e.getMessage());
+        }
+        return returnValue[0] == FEATURE_ENABLED;
+    }
+
+    @Override
+    public void changeEnabledCapabilities(CapabilityChangeRequest request,
+            CapabilityCallbackProxy c) {
+        if (request == null) {
+            return;
+        }
+        try {
+            IImsConfig imsConfig = mCompatFeature.getConfigInterface();
+            CountDownLatch latch = new CountDownLatch(1);
+            // Disable Capabilities
+            for (CapabilityChangeRequest.CapabilityPair cap : request.getCapabilitiesToDisable()) {
+                int capConverted = convertCapability(cap.getCapability(), cap.getRadioTech());
+                int radioTechConverted = REG_TECH_TO_NET_TYPE.getOrDefault(cap.getRadioTech(),
+                        ImsRegistrationImplBase.REGISTRATION_TECH_NONE);
+                Log.i(TAG, "changeEnabledCapabilities - cap: " + capConverted + " radioTech: "
+                        + radioTechConverted + " disabled");
+                imsConfig.setFeatureValue(capConverted, radioTechConverted, FEATURE_DISABLED,
+                        new ConfigListener(capConverted, radioTechConverted, latch) {
+                            @Override
+                            public void setFeatureValueReceived(int value) {
+                                if (value != FEATURE_DISABLED) {
+                                    if (c == null) {
+                                        return;
+                                    }
+                                    c.onChangeCapabilityConfigurationError(cap.getCapability(),
+                                            cap.getRadioTech(), CAPABILITY_ERROR_GENERIC);
+                                }
+                            }
+                        });
+                latch.await(WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+            }
+            // Enable Capabilities
+            for (CapabilityChangeRequest.CapabilityPair cap : request.getCapabilitiesToEnable()) {
+                int capConverted = convertCapability(cap.getCapability(), cap.getRadioTech());
+                int radioTechConverted = REG_TECH_TO_NET_TYPE.getOrDefault(cap.getRadioTech(),
+                        ImsRegistrationImplBase.REGISTRATION_TECH_NONE);
+                Log.i(TAG, "changeEnabledCapabilities - cap: " + capConverted + " radioTech: "
+                        + radioTechConverted + " enabled");
+                imsConfig.setFeatureValue(capConverted, radioTechConverted, FEATURE_ENABLED,
+                        new ConfigListener(capConverted, radioTechConverted, latch) {
+                            @Override
+                            public void setFeatureValueReceived(int value) {
+                                if (value != FEATURE_ENABLED) {
+                                    if (c == null) {
+                                        return;
+                                    }
+                                    c.onChangeCapabilityConfigurationError(cap.getCapability(),
+                                            cap.getRadioTech(), CAPABILITY_ERROR_GENERIC);
+                                }
+                            }
+                        });
+                latch.await(WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+            }
+        } catch (RemoteException | InterruptedException e) {
+            Log.w(TAG, "changeEnabledCapabilities: Error processing: " + e.getMessage());
+        }
+    }
+
+    @Override
+    public ImsCallProfile createCallProfile(int callSessionType, int callType) {
+        try {
+            return mCompatFeature.createCallProfile(mSessionId, callSessionType, callType);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e.getMessage());
+        }
+    }
+
+    @Override
+    public IImsCallSession createCallSessionInterface(ImsCallProfile profile)
+            throws RemoteException {
+        return mCompatFeature.createCallSession(mSessionId, profile);
+    }
+
+    @Override
+    public IImsUt getUtInterface() throws RemoteException {
+        return mCompatFeature.getUtInterface();
+    }
+
+    @Override
+    public IImsEcbm getEcbmInterface() throws RemoteException {
+        return mCompatFeature.getEcbmInterface();
+    }
+
+    @Override
+    public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
+        return mCompatFeature.getMultiEndpointInterface();
+    }
+
+    @Override
+    public int getFeatureState() {
+        try {
+            return mCompatFeature.getFeatureState();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e.getMessage());
+        }
+    }
+
+    @Override
+    public void setUiTtyMode(int mode, Message onCompleteMessage) {
+        try {
+            mCompatFeature.setUiTTYMode(mode, onCompleteMessage);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e.getMessage());
+        }
+    }
+
+
+    @Override
+    public void onFeatureRemoved() {
+        mContext.unregisterReceiver(mReceiver);
+        try {
+            mCompatFeature.endSession(mSessionId);
+            mCompatFeature.removeRegistrationListener(mListener);
+            if (mRegCompatAdapter != null) {
+                mCompatFeature.removeRegistrationListener(
+                        mRegCompatAdapter.getRegistrationListener());
+            }
+        } catch (RemoteException e) {
+            Log.w(TAG, "onFeatureRemoved: Couldn't end session: " + e.getMessage());
+        }
+    }
+
+    @Override
+    public void onFeatureReady() {
+        Log.i(TAG, "onFeatureReady called!");
+        // This gets called when MmTelFeature.setListener is called. We need to use this time to
+        // call openSession on the old MMTelFeature implementation.
+        IntentFilter intentFilter = new IntentFilter(ImsManager.ACTION_IMS_INCOMING_CALL);
+        mContext.registerReceiver(mReceiver, intentFilter);
+        try {
+            mSessionId = mCompatFeature.startSession(createIncomingCallPendingIntent(),
+                    new ImsRegistrationListenerBase());
+            mCompatFeature.addRegistrationListener(mListener);
+            mCompatFeature.addRegistrationListener(mRegCompatAdapter.getRegistrationListener());
+        } catch (RemoteException e) {
+            Log.e(TAG, "Couldn't start compat feature: " + e.getMessage());
+        }
+    }
+
+    public void enableIms() throws RemoteException {
+        mCompatFeature.turnOnIms();
+    }
+
+    public void disableIms() throws RemoteException {
+        mCompatFeature.turnOffIms();
+    }
+
+    public IImsConfig getOldConfigInterface() {
+        try {
+            return mCompatFeature.getConfigInterface();
+        } catch (RemoteException e) {
+            Log.w(TAG, "getOldConfigInterface(): " + e.getMessage());
+            return null;
+        }
+    }
+
+    public void addRegistrationAdapter(ImsRegistrationCompatAdapter regCompat)
+            throws RemoteException {
+        mRegCompatAdapter = regCompat;
+    }
+
+    private MmTelCapabilities convertCapabilities(int[] enabledFeatures) {
+        boolean[] featuresEnabled = new boolean[enabledFeatures.length];
+        for (int i = FEATURE_TYPE_VOICE_OVER_LTE; i <= FEATURE_TYPE_UT_OVER_WIFI
+                && i < enabledFeatures.length; i++) {
+            if (enabledFeatures[i] == i) {
+                featuresEnabled[i] = true;
+            } else if (enabledFeatures[i] == FEATURE_TYPE_UNKNOWN) {
+                // FEATURE_TYPE_UNKNOWN indicates that a feature is disabled.
+                featuresEnabled[i] = false;
+            }
+        }
+        MmTelCapabilities capabilities = new MmTelCapabilities();
+        if (featuresEnabled[FEATURE_TYPE_VOICE_OVER_LTE]
+                || featuresEnabled[FEATURE_TYPE_VOICE_OVER_WIFI]) {
+            // voice is enabled
+            capabilities.addCapabilities(MmTelCapabilities.CAPABILITY_TYPE_VOICE);
+        }
+        if (featuresEnabled[FEATURE_TYPE_VIDEO_OVER_LTE]
+                || featuresEnabled[FEATURE_TYPE_VIDEO_OVER_WIFI]) {
+            // video is enabled
+            capabilities.addCapabilities(MmTelCapabilities.CAPABILITY_TYPE_VIDEO);
+        }
+        if (featuresEnabled[FEATURE_TYPE_UT_OVER_LTE]
+                || featuresEnabled[FEATURE_TYPE_UT_OVER_WIFI]) {
+            // ut is enabled
+            capabilities.addCapabilities(MmTelCapabilities.CAPABILITY_TYPE_UT);
+        }
+        Log.i(TAG, "convertCapabilities - capabilities: " + capabilities);
+        return capabilities;
+    }
+
+    private PendingIntent createIncomingCallPendingIntent() {
+        Intent intent = new Intent(ImsManager.ACTION_IMS_INCOMING_CALL);
+        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        return PendingIntent.getBroadcast(mContext, 0, intent,
+                PendingIntent.FLAG_UPDATE_CURRENT);
+    }
+
+    private int convertCapability(int capability, int radioTech) {
+        int capConverted = FEATURE_TYPE_UNKNOWN;
+        if (radioTech == ImsRegistrationImplBase.REGISTRATION_TECH_LTE) {
+            switch (capability) {
+                case MmTelCapabilities.CAPABILITY_TYPE_VOICE:
+                    capConverted = FEATURE_TYPE_VOICE_OVER_LTE;
+                    break;
+                case MmTelCapabilities.CAPABILITY_TYPE_VIDEO:
+                    capConverted = FEATURE_TYPE_VIDEO_OVER_LTE;
+                    break;
+                case MmTelCapabilities.CAPABILITY_TYPE_UT:
+                    capConverted = FEATURE_TYPE_UT_OVER_LTE;
+                    break;
+            }
+        } else if (radioTech == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN) {
+            switch (capability) {
+                case MmTelCapabilities.CAPABILITY_TYPE_VOICE:
+                    capConverted = FEATURE_TYPE_VOICE_OVER_WIFI;
+                    break;
+                case MmTelCapabilities.CAPABILITY_TYPE_VIDEO:
+                    capConverted = FEATURE_TYPE_VIDEO_OVER_WIFI;
+                    break;
+                case MmTelCapabilities.CAPABILITY_TYPE_UT:
+                    capConverted = FEATURE_TYPE_UT_OVER_WIFI;
+                    break;
+            }
+        }
+        return capConverted;
+    }
+}
diff --git a/src/java/com/android/internal/telephony/ims/MmTelInterfaceAdapter.java b/src/java/com/android/internal/telephony/ims/MmTelInterfaceAdapter.java
new file mode 100644
index 0000000..ef08133
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ims/MmTelInterfaceAdapter.java
@@ -0,0 +1,128 @@
+/*
+ * 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.ims;
+
+import android.app.PendingIntent;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.ims.ImsCallProfile;
+
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsConfig;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsMMTelFeature;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsRegistrationListener;
+import com.android.ims.internal.IImsUt;
+
+/**
+ * Defines "generic" MmTel commands and provides a concrete implementation for compatibility
+ * purposes.
+ */
+
+public class MmTelInterfaceAdapter {
+
+    protected IBinder mBinder;
+    protected int mSlotId;
+
+    public MmTelInterfaceAdapter(int slotId, IBinder binder) {
+        mBinder = binder;
+        mSlotId = slotId;
+    }
+
+    public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
+            throws RemoteException {
+        return getInterface().startSession(incomingCallIntent, listener);
+    }
+
+    public void endSession(int sessionId) throws RemoteException {
+        getInterface().endSession(sessionId);
+    }
+
+    public boolean isConnected(int callSessionType, int callType) throws RemoteException {
+        return getInterface().isConnected(callSessionType, callType);
+    }
+
+    public boolean isOpened() throws RemoteException {
+        return getInterface().isOpened();
+    }
+
+    public int getFeatureState() throws RemoteException {
+        return getInterface().getFeatureStatus();
+    }
+
+    public void addRegistrationListener(IImsRegistrationListener listener) throws RemoteException {
+        getInterface().addRegistrationListener(listener);
+    }
+
+    public void removeRegistrationListener(IImsRegistrationListener listener)
+            throws RemoteException {
+        getInterface().removeRegistrationListener(listener);
+    }
+
+    public ImsCallProfile createCallProfile(int sessionId, int callSessionType, int callType)
+            throws RemoteException {
+        return getInterface().createCallProfile(sessionId, callSessionType, callType);
+    }
+
+    public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile)
+            throws RemoteException {
+        return getInterface().createCallSession(sessionId, profile);
+    }
+
+    public IImsCallSession getPendingCallSession(int sessionId, String callId)
+            throws RemoteException {
+        return getInterface().getPendingCallSession(sessionId, callId);
+    }
+
+    public IImsUt getUtInterface() throws RemoteException {
+        return getInterface().getUtInterface();
+    }
+
+    public IImsConfig getConfigInterface() throws RemoteException {
+        return getInterface().getConfigInterface();
+    }
+
+    public void turnOnIms() throws RemoteException {
+        getInterface().turnOnIms();
+    }
+
+    public void turnOffIms() throws RemoteException {
+        getInterface().turnOffIms();
+    }
+
+    public IImsEcbm getEcbmInterface() throws RemoteException {
+        return getInterface().getEcbmInterface();
+    }
+
+    public void setUiTTYMode(int uiTtyMode, Message onComplete) throws RemoteException {
+        getInterface().setUiTTYMode(uiTtyMode, onComplete);
+    }
+
+    public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
+        return getInterface().getMultiEndpointInterface();
+    }
+
+    private IImsMMTelFeature getInterface() throws RemoteException {
+        IImsMMTelFeature feature = IImsMMTelFeature.Stub.asInterface(mBinder);
+        if (feature == null) {
+            throw new RemoteException("Binder not Available");
+        }
+        return feature;
+    }
+}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsExternalCall.java b/src/java/com/android/internal/telephony/imsphone/ImsExternalCall.java
index b833533..03ea1c8 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsExternalCall.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsExternalCall.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony.imsphone;
 
+import android.telephony.ims.ImsExternalCallState;
+
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.CallStateException;
 import com.android.internal.telephony.Connection;
@@ -25,7 +27,7 @@
 
 /**
  * Companion class for {@link ImsExternalConnection}; represents an external call which was
- * received via {@link com.android.ims.ImsExternalCallState} info.
+ * received via {@link ImsExternalCallState} info.
  */
 public class ImsExternalCall extends Call {
 
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsExternalCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsExternalCallTracker.java
index 4bea73d..3227cfa 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsExternalCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsExternalCallTracker.java
@@ -16,8 +16,8 @@
 
 package com.android.internal.telephony.imsphone;
 
-import com.android.ims.ImsCallProfile;
-import com.android.ims.ImsExternalCallState;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsExternalCallState;
 import com.android.ims.ImsExternalCallStateListener;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.Call;
@@ -31,7 +31,6 @@
 import android.os.Message;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.VideoProfile;
-import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
 import android.util.Log;
 
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsExternalConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsExternalConnection.java
index 071aebb..a8b07cb 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsExternalConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsExternalConnection.java
@@ -28,11 +28,9 @@
 import android.net.Uri;
 import android.telecom.PhoneAccount;
 import android.telephony.PhoneNumberUtils;
-import android.telephony.Rlog;
-import android.util.Log;
+import android.telephony.ims.ImsExternalCallState;
 
 import java.util.Collections;
-import java.util.List;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -42,7 +40,7 @@
  * Package.
  *
  * Dialog event package information is received from the IMS framework via
- * {@link com.android.ims.ImsExternalCallState} instances.
+ * {@link ImsExternalCallState} instances.
  *
  * @hide
  */
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index 03b280e..706c953 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -69,14 +69,14 @@
 import android.telephony.UssdResponse;
 import android.text.TextUtils;
 
-import com.android.ims.ImsCallForwardInfo;
-import com.android.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallForwardInfo;
+import android.telephony.ims.ImsCallProfile;
 import com.android.ims.ImsEcbm;
 import com.android.ims.ImsEcbmStateListener;
 import com.android.ims.ImsException;
 import com.android.ims.ImsManager;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.ImsSsInfo;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsSsInfo;
 import com.android.ims.ImsUtInterface;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.Call;
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
index d5519dd..e573fbd 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
@@ -446,10 +446,6 @@
             Message response) {
     }
 
-    @Override
-    public void getDataCallList(Message response) {
-    }
-
     public List<DataConnection> getCurrentDataConnectionList () {
         return null;
     }
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
index b90f64f..cb5917d 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
@@ -28,7 +28,7 @@
 import com.android.internal.telephony.Phone;
 import com.android.ims.ImsCall;
 import com.android.ims.ImsException;
-import com.android.ims.ImsStreamMediaProfile;
+import android.telephony.ims.ImsStreamMediaProfile;
 
 import java.util.List;
 
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index 08543bf..13038cd 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -16,7 +16,6 @@
 
 package com.android.internal.telephony.imsphone;
 
-import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -51,6 +50,9 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsConfigImplBase;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -58,19 +60,18 @@
 import android.util.SparseIntArray;
 
 import com.android.ims.ImsCall;
-import com.android.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallProfile;
 import com.android.ims.ImsConfig;
 import com.android.ims.ImsConfigListener;
-import com.android.ims.ImsConnectionStateListener;
 import com.android.ims.ImsEcbm;
 import com.android.ims.ImsException;
 import com.android.ims.ImsManager;
 import com.android.ims.ImsMultiEndpoint;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.ImsServiceClass;
-import com.android.ims.ImsServiceProxy;
-import com.android.ims.ImsSuppServiceNotification;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsSuppServiceNotification;
 import com.android.ims.ImsUtInterface;
+import com.android.ims.MmTelFeatureConnection;
+import com.android.ims.internal.IImsCallSession;
 import com.android.ims.internal.IImsVideoCallProvider;
 import com.android.ims.internal.ImsVideoCallProviderWrapper;
 import com.android.ims.internal.VideoPauseTracker;
@@ -131,90 +132,100 @@
     private static final boolean VERBOSE_STATE_LOGGING = FORCE_VERBOSE_STATE_LOGGING ||
             Rlog.isLoggable(VERBOSE_STATE_TAG, Log.VERBOSE);
 
-    //Indices map to ImsConfig.FeatureConstants
-    private boolean[] mImsFeatureEnabled = {false, false, false, false, false, false};
-    private final String[] mImsFeatureStrings = {"VoLTE", "ViLTE", "VoWiFi", "ViWiFi",
-            "UTLTE", "UTWiFi"};
+    private MmTelFeature.MmTelCapabilities mMmTelCapabilities =
+            new MmTelFeature.MmTelCapabilities();
 
     private TelephonyMetrics mMetrics;
     private boolean mCarrierConfigLoaded = false;
 
+    private final MmTelFeatureListener mMmTelFeatureListener = new MmTelFeatureListener();
+    private class MmTelFeatureListener extends MmTelFeature.Listener {
+        @Override
+        public void onIncomingCall(IImsCallSession c, Bundle extras) {
+            if (DBG) log("onReceive : incoming call intent");
+
+            if (mImsManager == null) return;
+
+            try {
+                // Network initiated USSD will be treated by mImsUssdListener
+                boolean isUssd = extras.getBoolean(ImsManager.EXTRA_USSD, false);
+                if (isUssd) {
+                    if (DBG) log("onReceive : USSD");
+                    mUssdSession = mImsManager.takeCall(c, extras, mImsUssdListener);
+                    if (mUssdSession != null) {
+                        mUssdSession.accept(ImsCallProfile.CALL_TYPE_VOICE);
+                    }
+                    return;
+                }
+
+                boolean isUnknown = extras.getBoolean(ImsManager.EXTRA_IS_UNKNOWN_CALL, false);
+                if (DBG) {
+                    log("onReceive : isUnknown = " + isUnknown
+                            + " fg = " + mForegroundCall.getState()
+                            + " bg = " + mBackgroundCall.getState());
+                }
+
+                // Normal MT/Unknown call
+                ImsCall imsCall = mImsManager.takeCall(c, extras, mImsCallListener);
+                ImsPhoneConnection conn = new ImsPhoneConnection(mPhone, imsCall,
+                        ImsPhoneCallTracker.this,
+                        (isUnknown ? mForegroundCall : mRingingCall), isUnknown);
+
+                // If there is an active call.
+                if (mForegroundCall.hasConnections()) {
+                    ImsCall activeCall = mForegroundCall.getFirstConnection().getImsCall();
+                    if (activeCall != null && imsCall != null) {
+                        // activeCall could be null if the foreground call is in a disconnected
+                        // state.  If either of the calls is null there is no need to check if
+                        // one will be disconnected on answer.
+                        boolean answeringWillDisconnect =
+                                shouldDisconnectActiveCallOnAnswer(activeCall, imsCall);
+                        conn.setActiveCallDisconnectedOnAnswer(answeringWillDisconnect);
+                    }
+                }
+                conn.setAllowAddCallDuringVideoCall(mAllowAddCallDuringVideoCall);
+                addConnection(conn);
+
+                setVideoCallProvider(conn, imsCall);
+
+                TelephonyMetrics.getInstance().writeOnImsCallReceive(mPhone.getPhoneId(),
+                        imsCall.getSession());
+
+                if (isUnknown) {
+                    mPhone.notifyUnknownConnection(conn);
+                } else {
+                    if ((mForegroundCall.getState() != ImsPhoneCall.State.IDLE)
+                            || (mBackgroundCall.getState() != ImsPhoneCall.State.IDLE)) {
+                        conn.update(imsCall, ImsPhoneCall.State.WAITING);
+                    }
+
+                    mPhone.notifyNewRingingConnection(conn);
+                    mPhone.notifyIncomingRing();
+                }
+
+                updatePhoneState();
+                mPhone.notifyPreciseCallStateChanged();
+            } catch (ImsException e) {
+                loge("onReceive : exception " + e);
+            } catch (RemoteException e) {
+            }
+        }
+
+        @Override
+        public void onVoiceMessageCountUpdate(int count) {
+            if (mPhone != null && mPhone.mDefaultPhone != null) {
+                if (DBG) log("onVoiceMessageCountChanged :: count=" + count);
+                mPhone.mDefaultPhone.setVoiceMessageCount(count);
+            } else {
+                loge("onVoiceMessageCountUpdate: null phone");
+            }
+        }
+    }
+
     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            if (intent.getAction().equals(ImsManager.ACTION_IMS_INCOMING_CALL)) {
-                if (DBG) log("onReceive : incoming call intent");
-
-                if (mImsManager == null) return;
-
-                if (mServiceId < 0) return;
-
-                try {
-                    // Network initiated USSD will be treated by mImsUssdListener
-                    boolean isUssd = intent.getBooleanExtra(ImsManager.EXTRA_USSD, false);
-                    if (isUssd) {
-                        if (DBG) log("onReceive : USSD");
-                        mUssdSession = mImsManager.takeCall(mServiceId, intent, mImsUssdListener);
-                        if (mUssdSession != null) {
-                            mUssdSession.accept(ImsCallProfile.CALL_TYPE_VOICE);
-                        }
-                        return;
-                    }
-
-                    boolean isUnknown = intent.getBooleanExtra(ImsManager.EXTRA_IS_UNKNOWN_CALL,
-                            false);
-                    if (DBG) {
-                        log("onReceive : isUnknown = " + isUnknown +
-                                " fg = " + mForegroundCall.getState() +
-                                " bg = " + mBackgroundCall.getState());
-                    }
-
-                    // Normal MT/Unknown call
-                    ImsCall imsCall = mImsManager.takeCall(mServiceId, intent, mImsCallListener);
-                    ImsPhoneConnection conn = new ImsPhoneConnection(mPhone, imsCall,
-                            ImsPhoneCallTracker.this,
-                            (isUnknown? mForegroundCall: mRingingCall), isUnknown);
-
-                    // If there is an active call.
-                    if (mForegroundCall.hasConnections()) {
-                        ImsCall activeCall = mForegroundCall.getFirstConnection().getImsCall();
-                        if (activeCall != null && imsCall != null) {
-                            // activeCall could be null if the foreground call is in a disconnected
-                            // state.  If either of the calls is null there is no need to check if
-                            // one will be disconnected on answer.
-                            boolean answeringWillDisconnect =
-                                    shouldDisconnectActiveCallOnAnswer(activeCall, imsCall);
-                            conn.setActiveCallDisconnectedOnAnswer(answeringWillDisconnect);
-                        }
-                    }
-                    conn.setAllowAddCallDuringVideoCall(mAllowAddCallDuringVideoCall);
-                    addConnection(conn);
-
-                    setVideoCallProvider(conn, imsCall);
-
-                    TelephonyMetrics.getInstance().writeOnImsCallReceive(mPhone.getPhoneId(),
-                            imsCall.getSession());
-
-                    if (isUnknown) {
-                        mPhone.notifyUnknownConnection(conn);
-                    } else {
-                        if ((mForegroundCall.getState() != ImsPhoneCall.State.IDLE) ||
-                                (mBackgroundCall.getState() != ImsPhoneCall.State.IDLE)) {
-                            conn.update(imsCall, ImsPhoneCall.State.WAITING);
-                        }
-
-                        mPhone.notifyNewRingingConnection(conn);
-                        mPhone.notifyIncomingRing();
-                    }
-
-                    updatePhoneState();
-                    mPhone.notifyPreciseCallStateChanged();
-                } catch (ImsException e) {
-                    loge("onReceive : exception " + e);
-                } catch (RemoteException e) {
-                }
-            } else if (intent.getAction().equals(
-                    CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
+            if (intent.getAction().equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
                 int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
                 if (subId == mPhone.getSubId()) {
@@ -295,7 +306,6 @@
     private int mImsServiceRetryCount;
     private ImsManager mImsManager;
     private ImsUtInterface mUtInterface;
-    private int mServiceId = -1;
 
     private Call.SrvccState mSrvccState = Call.SrvccState.NONE;
 
@@ -628,12 +638,12 @@
     };
 
     // Callback fires when ImsManager MMTel Feature changes state
-    private ImsServiceProxy.IFeatureUpdate mNotifyStatusChangedCallback =
-            new ImsServiceProxy.IFeatureUpdate() {
+    private MmTelFeatureConnection.IFeatureUpdate mNotifyStatusChangedCallback =
+            new MmTelFeatureConnection.IFeatureUpdate() {
                 @Override
                 public void notifyStateChanged() {
                     try {
-                        int status = mImsManager.getImsServiceStatus();
+                        int status = mImsManager.getImsServiceState();
                         log("Status Changed: " + status);
                         switch (status) {
                             case ImsFeature.STATE_READY: {
@@ -642,7 +652,7 @@
                             }
                             case ImsFeature.STATE_INITIALIZING:
                                 // fall through
-                            case ImsFeature.STATE_NOT_AVAILABLE: {
+                            case ImsFeature.STATE_UNAVAILABLE: {
                                 stopListeningForCalls();
                                 break;
                             }
@@ -691,7 +701,6 @@
         mMetrics = TelephonyMetrics.getInstance();
 
         IntentFilter intentfilter = new IntentFilter();
-        intentfilter.addAction(ImsManager.ACTION_IMS_INCOMING_CALL);
         intentfilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
         intentfilter.addAction(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER);
         mPhone.getContext().registerReceiver(mReceiver, intentfilter);
@@ -751,13 +760,6 @@
         return uid;
     }
 
-    private PendingIntent createIncomingCallPendingIntent() {
-        Intent intent = new Intent(ImsManager.ACTION_IMS_INCOMING_CALL);
-        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-        return PendingIntent.getBroadcast(mPhone.getContext(), 0, intent,
-                PendingIntent.FLAG_UPDATE_CURRENT);
-    }
-
     private void getImsService() throws ImsException {
         if (DBG) log("getImsService");
         mImsManager = ImsManager.getInstance(mPhone.getContext(), mPhone.getPhoneId());
@@ -771,11 +773,13 @@
 
     private void startListeningForCalls() throws ImsException {
         mImsServiceRetryCount = 0;
-        mServiceId = mImsManager.open(ImsServiceClass.MMTEL,
-                createIncomingCallPendingIntent(),
-                mImsConnectionStateListener);
+        mImsManager.open(mMmTelFeatureListener);
+        mImsManager.addRegistrationCallback(mImsRegistrationCallback);
+        mImsManager.addCapabilitiesCallback(mImsCapabilityCallback);
 
-        mImsManager.setImsConfigListener(mImsConfigListener);
+        mImsManager.setConfigListener(mImsConfigListener);
+
+        mImsManager.getConfigInterface().addConfigCallback(mConfigCallback);
 
         // Get the ECBM interface and set IMSPhone's listener object for notifications
         getEcbmInterface().setEcbmStateListener(mPhone.getImsEcbmStateListener());
@@ -808,15 +812,15 @@
     }
 
     private void stopListeningForCalls() {
-        try {
-            resetImsCapabilities();
-            // Only close on valid session.
-            if (mImsManager != null && mServiceId > 0) {
-                mImsManager.close(mServiceId);
-                mServiceId = -1;
+        resetImsCapabilities();
+        // Only close on valid session.
+        if (mImsManager != null) {
+            try {
+                mImsManager.getConfigInterface().removeConfigCallback(mConfigCallback);
+            } catch (ImsException e) {
+                Log.w(LOG_TAG, "stopListeningForCalls: unable to remove config callback.");
             }
-        } catch (ImsException e) {
-            // If the binder is unavailable, then the ImsService doesn't need to close.
+            mImsManager.close();
         }
     }
 
@@ -1115,8 +1119,7 @@
 
         try {
             String[] callees = new String[] { conn.getAddress() };
-            ImsCallProfile profile = mImsManager.createCallProfile(mServiceId,
-                    serviceType, callType);
+            ImsCallProfile profile = mImsManager.createCallProfile(serviceType, callType);
             profile.setCallExtraInt(ImsCallProfile.EXTRA_OIR, clirMode);
 
             // Translate call subject intent-extra from Telecom-specific extra key to the
@@ -1147,8 +1150,7 @@
                 // being sent to the lower layers/to the network.
             }
 
-            ImsCall imsCall = mImsManager.makeCall(mServiceId, profile,
-                    callees, mImsCallListener);
+            ImsCall imsCall = mImsManager.makeCall(profile, callees, mImsCallListener);
             conn.setImsCall(imsCall);
 
             mMetrics.writeOnImsCallStart(mPhone.getPhoneId(),
@@ -1710,13 +1712,12 @@
             }
 
             String[] callees = new String[] { ussdString };
-            ImsCallProfile profile = mImsManager.createCallProfile(mServiceId,
+            ImsCallProfile profile = mImsManager.createCallProfile(
                     ImsCallProfile.SERVICE_TYPE_NORMAL, ImsCallProfile.CALL_TYPE_VOICE);
             profile.setCallExtraInt(ImsCallProfile.EXTRA_DIALSTRING,
                     ImsCallProfile.DIALSTRING_USSD);
 
-            mUssdSession = mImsManager.makeCall(mServiceId, profile,
-                    callees, mImsUssdListener);
+            mUssdSession = mImsManager.makeCall(profile, callees, mImsUssdListener);
         } catch (ImsException e) {
             loge("sendUSSD : " + e);
             mPhone.sendErrorResponse(response, e);
@@ -2594,7 +2595,7 @@
                                 && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN
                                 && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
                 if (isHandoverFromWifi && imsCall.isVideoCall()) {
-                    if (mNotifyHandoverVideoFromWifiToLTE && mIsDataEnabled) {
+                    if (mNotifyHandoverVideoFromWifiToLTE &&    mIsDataEnabled) {
                         if (conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) {
                             log("onCallHandover :: notifying of WIFI to LTE handover.");
                             conn.onConnectionEvent(
@@ -2760,83 +2761,60 @@
         }
     };
 
-    /**
-     * Listen to the IMS service state change
-     *
-     */
-    private ImsConnectionStateListener mImsConnectionStateListener =
-        new ImsConnectionStateListener() {
-        @Override
-        public void onImsConnected(int imsRadioTech) {
-            if (DBG) log("onImsConnected imsRadioTech=" + imsRadioTech);
-            mPhone.setServiceState(ServiceState.STATE_IN_SERVICE);
-            mPhone.setImsRegistered(true);
-            mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
-                    ImsConnectionState.State.CONNECTED, null);
-        }
+    private final ImsRegistrationImplBase.Callback mImsRegistrationCallback =
+            new ImsRegistrationImplBase.Callback() {
 
-        @Override
-        public void onImsDisconnected(ImsReasonInfo imsReasonInfo) {
-            if (DBG) log("onImsDisconnected imsReasonInfo=" + imsReasonInfo);
-            resetImsCapabilities();
-            mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
-            mPhone.setImsRegistered(false);
-            mPhone.processDisconnectReason(imsReasonInfo);
-            mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
-                    ImsConnectionState.State.DISCONNECTED, imsReasonInfo);
-        }
+                @Override
+                public void onRegistered(
+                        @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
+                    if (DBG) log("onImsConnected imsRadioTech=" + imsRadioTech);
+                    mPhone.setServiceState(ServiceState.STATE_IN_SERVICE);
+                    mPhone.setImsRegistered(true);
+                    mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
+                            ImsConnectionState.State.CONNECTED, null);
+                }
 
-        @Override
-        public void onImsProgressing(int imsRadioTech) {
-            if (DBG) log("onImsProgressing imsRadioTech=" + imsRadioTech);
-            mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
-            mPhone.setImsRegistered(false);
-            mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
-                    ImsConnectionState.State.PROGRESSING, null);
-        }
+                @Override
+                public void onRegistering(
+                        @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
+                    if (DBG) log("onImsProgressing imsRadioTech=" + imsRadioTech);
+                    mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
+                    mPhone.setImsRegistered(false);
+                    mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
+                            ImsConnectionState.State.PROGRESSING, null);
+                }
 
-        @Override
-        public void onImsResumed() {
-            if (DBG) log("onImsResumed");
-            mPhone.setServiceState(ServiceState.STATE_IN_SERVICE);
-            mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
-                    ImsConnectionState.State.RESUMED, null);
-        }
+                @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);
+                    mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
+                            ImsConnectionState.State.DISCONNECTED, imsReasonInfo);
+                }
 
-        @Override
-        public void onImsSuspended() {
-            if (DBG) log("onImsSuspended");
-            mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
-            mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
-                    ImsConnectionState.State.SUSPENDED, null);
+                @Override
+                public void onSubscriberAssociatedUriChanged(Uri[] uris) {
+                    if (DBG) log("registrationAssociatedUriChanged");
+                    mPhone.setCurrentSubscriberUris(uris);
+                }
+            };
 
-        }
-
-        @Override
-        public void onFeatureCapabilityChanged(int serviceClass,
-                int[] enabledFeatures, int[] disabledFeatures) {
-            if (DBG) log("onFeatureCapabilityChanged");
-            SomeArgs args = SomeArgs.obtain();
-            args.argi1 = serviceClass;
-            args.arg1 = enabledFeatures;
-            args.arg2 = disabledFeatures;
-            // Remove any pending updates; they're already stale, so no need to process them.
-            removeMessages(EVENT_ON_FEATURE_CAPABILITY_CHANGED);
-            obtainMessage(EVENT_ON_FEATURE_CAPABILITY_CHANGED, args).sendToTarget();
-        }
-
-        @Override
-        public void onVoiceMessageCountChanged(int count) {
-            if (DBG) log("onVoiceMessageCountChanged :: count=" + count);
-            mPhone.mDefaultPhone.setVoiceMessageCount(count);
-        }
-
-        @Override
-        public void registrationAssociatedUriChanged(Uri[] uris) {
-            if (DBG) log("registrationAssociatedUriChanged");
-            mPhone.setCurrentSubscriberUris(uris);
-        }
-    };
+    private final ImsFeature.CapabilityCallback mImsCapabilityCallback =
+            new ImsFeature.CapabilityCallback() {
+                @Override
+                public void onCapabilitiesStatusChanged(ImsFeature.Capabilities config) {
+                    if (DBG) log("onCapabilitiesStatusChanged: " + config);
+                    SomeArgs args = SomeArgs.obtain();
+                    args.arg1 = config;
+                    // Remove any pending updates; they're already stale, so no need to process
+                    // them.
+                    removeMessages(EVENT_ON_FEATURE_CAPABILITY_CHANGED);
+                    obtainMessage(EVENT_ON_FEATURE_CAPABILITY_CHANGED, args).sendToTarget();
+                }
+            };
 
     private ImsConfigListener.Stub mImsConfigListener = new ImsConfigListener.Stub() {
         @Override
@@ -2844,8 +2822,7 @@
 
         @Override
         public void onSetFeatureResponse(int feature, int network, int value, int status) {
-            mMetrics.writeImsSetFeatureValue(
-                    mPhone.getPhoneId(), feature, network, value, status);
+            mMetrics.writeImsSetFeatureValue(mPhone.getPhoneId(), feature, network, value);
         }
 
         @Override
@@ -2856,6 +2833,30 @@
 
     };
 
+    private final ImsConfigImplBase.Callback mConfigCallback = new ImsConfigImplBase.Callback() {
+        @Override
+        public void onConfigChanged(int item, int value) {
+            sendConfigChangedIntent(item, Integer.toString(value));
+        }
+
+        @Override
+        public void onConfigChanged(int item, String value) {
+            sendConfigChangedIntent(item, value);
+        }
+
+        // send IMS_CONFIG_CHANGED intent for older services that do not implement the new callback
+        // interface.
+        private void sendConfigChangedIntent(int item, String value) {
+            log("sendConfigChangedIntent - [" + item + ", " + value + "]");
+            Intent configChangedIntent = new Intent(ImsConfig.ACTION_IMS_CONFIG_CHANGED);
+            configChangedIntent.putExtra(ImsConfig.EXTRA_CHANGED_ITEM, item);
+            configChangedIntent.putExtra(ImsConfig.EXTRA_NEW_VALUE, value);
+            if (mPhone != null && mPhone.getContext() != null) {
+                mPhone.getContext().sendBroadcast(configChangedIntent);
+            }
+        }
+    };
+
     public ImsUtInterface getUtInterface() throws ImsException {
         if (mImsManager == null) {
             throw getImsManagerIsNullException();
@@ -3005,10 +3006,8 @@
             case EVENT_ON_FEATURE_CAPABILITY_CHANGED: {
                 SomeArgs args = (SomeArgs) msg.obj;
                 try {
-                    int serviceClass = args.argi1;
-                    int[] enabledFeatures = (int[]) args.arg1;
-                    int[] disabledFeatures = (int[]) args.arg2;
-                    handleFeatureCapabilityChanged(serviceClass, enabledFeatures, disabledFeatures);
+                    ImsFeature.Capabilities capabilities = (ImsFeature.Capabilities) args.arg1;
+                    handleFeatureCapabilityChanged(capabilities);
                 } finally {
                     args.recycle();
                 }
@@ -3135,10 +3134,7 @@
         pw.println(" mPhone=" + mPhone);
         pw.println(" mDesiredMute=" + mDesiredMute);
         pw.println(" mState=" + mState);
-        for (int i = 0; i < mImsFeatureEnabled.length; i++) {
-            pw.println(" " + mImsFeatureStrings[i] + ": "
-                    + ((mImsFeatureEnabled[i]) ? "enabled" : "disabled"));
-        }
+        pw.println(" mMmTelCapabilities=" + mMmTelCapabilities);
         pw.println(" mDefaultDialerUid=" + mDefaultDialerUid.get());
         pw.println(" mVtDataUsageSnapshot=" + mVtDataUsageSnapshot);
         pw.println(" mVtDataUsageUidSnapshot=" + mVtDataUsageUidSnapshot);
@@ -3172,7 +3168,7 @@
             throw getImsManagerIsNullException();
         }
 
-        ImsEcbm ecbm = mImsManager.getEcbmInterface(mServiceId);
+        ImsEcbm ecbm = mImsManager.getEcbmInterface();
         return ecbm;
     }
 
@@ -3183,7 +3179,7 @@
         }
 
         try {
-            return mImsManager.getMultiEndpointInterface(mServiceId);
+            return mImsManager.getMultiEndpointInterface();
         } catch (ImsException e) {
             if (e.getCode() == ImsReasonInfo.CODE_MULTIENDPOINT_NOT_SUPPORTED) {
                 return null;
@@ -3199,16 +3195,21 @@
     }
 
     public boolean isVolteEnabled() {
-        return mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE];
+        boolean isRadioTechLte = getImsRegistrationTech()
+                == ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
+        return isRadioTechLte && mMmTelCapabilities.isCapable(
+                MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
     }
 
     public boolean isVowifiEnabled() {
-        return mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI];
+        boolean isRadioTechIwlan = getImsRegistrationTech()
+                == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
+        return isRadioTechIwlan && mMmTelCapabilities.isCapable(
+                MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
     }
 
     public boolean isVideoCallEnabled() {
-        return (mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE]
-                || mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_WIFI]);
+        return mMmTelCapabilities.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO);
     }
 
     @Override
@@ -3216,6 +3217,13 @@
         return mState;
     }
 
+    private int getImsRegistrationTech() {
+        if (mImsManager != null) {
+            return mImsManager.getRegistrationTech();
+        }
+        return ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
+    }
+
     private void retryGetImsService() {
         // The binder connection is already up. Do not try to get it again.
         if (mImsManager.isServiceAvailable()) {
@@ -3254,8 +3262,7 @@
     }
 
     public boolean isUtEnabled() {
-        return (mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_UT_OVER_LTE]
-            || mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_UT_OVER_WIFI]);
+        return mMmTelCapabilities.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT);
     }
 
     /**
@@ -3462,8 +3469,6 @@
 
         log("onDataEnabledChanged: enabled=" + enabled + ", reason=" + reason);
 
-        ImsManager.getInstance(mPhone.getContext(), mPhone.getPhoneId()).setDataEnabled(enabled);
-
         mIsDataEnabled = enabled;
 
         if (!mIsViLteDataMetered) {
@@ -3607,9 +3612,7 @@
 
     private void resetImsCapabilities() {
         log("Resetting Capabilities...");
-        for (int i = 0; i < mImsFeatureEnabled.length; i++) {
-            mImsFeatureEnabled[i] = false;
-        }
+        mMmTelCapabilities = new MmTelFeature.MmTelCapabilities();
     }
 
     /**
@@ -3634,71 +3637,47 @@
         return mSupportDowngradeVtToAudio;
     }
 
-    private void handleFeatureCapabilityChanged(int serviceClass,
-            int[] enabledFeatures, int[] disabledFeatures) {
-        if (serviceClass == ImsServiceClass.MMTEL) {
-            boolean tmpIsVideoCallEnabled = isVideoCallEnabled();
-            // Check enabledFeatures to determine capabilities. We ignore disabledFeatures.
-            StringBuilder sb;
-            if (DBG) {
-                sb = new StringBuilder(120);
-                sb.append("handleFeatureCapabilityChanged: ");
-            }
-            for (int  i = ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE;
-                    i <= ImsConfig.FeatureConstants.FEATURE_TYPE_UT_OVER_WIFI &&
-                            i < enabledFeatures.length; i++) {
-                if (enabledFeatures[i] == i) {
-                    // If the feature is set to its own integer value it is enabled.
-                    if (DBG) {
-                        sb.append(mImsFeatureStrings[i]);
-                        sb.append(":true ");
-                    }
+    @VisibleForTesting
+    public void setDataEnabled(boolean isDataEnabled) {
+        mIsDataEnabled = isDataEnabled;
+    }
 
-                    mImsFeatureEnabled[i] = true;
-                } else if (enabledFeatures[i]
-                        == ImsConfig.FeatureConstants.FEATURE_TYPE_UNKNOWN) {
-                    // FEATURE_TYPE_UNKNOWN indicates that a feature is disabled.
-                    if (DBG) {
-                        sb.append(mImsFeatureStrings[i]);
-                        sb.append(":false ");
-                    }
+    private void handleFeatureCapabilityChanged(ImsFeature.Capabilities capabilities) {
+        boolean tmpIsVideoCallEnabled = isVideoCallEnabled();
+        // Check enabledFeatures to determine capabilities. We ignore disabledFeatures.
+        StringBuilder sb;
+        if (DBG) {
+            sb = new StringBuilder(120);
+            sb.append("handleFeatureCapabilityChanged: ");
+        }
+        sb.append(capabilities);
+        mMmTelCapabilities = new MmTelFeature.MmTelCapabilities(capabilities);
+        boolean isVideoEnabled = isVideoCallEnabled();
+        boolean isVideoEnabledStatechanged = tmpIsVideoCallEnabled != isVideoEnabled;
+        if (DBG) {
+            sb.append(" isVideoEnabledStateChanged=");
+            sb.append(isVideoEnabledStatechanged);
+        }
 
-                    mImsFeatureEnabled[i] = false;
-                } else {
-                    // Feature has unknown state; it is not its own value or -1.
-                    if (DBG) {
-                        loge("handleFeatureCapabilityChanged(" + i + ", " + mImsFeatureStrings[i]
-                                + "): unexpectedValue=" + enabledFeatures[i]);
-                    }
-                }
-            }
-            boolean isVideoEnabled = isVideoCallEnabled();
-            boolean isVideoEnabledStatechanged = tmpIsVideoCallEnabled != isVideoEnabled;
-            if (DBG) {
-                sb.append(" isVideoEnabledStateChanged=");
-                sb.append(isVideoEnabledStatechanged);
-            }
+        if (isVideoEnabledStatechanged) {
+            log("handleFeatureCapabilityChanged - notifyForVideoCapabilityChanged="
+                    + isVideoEnabled);
+            mPhone.notifyForVideoCapabilityChanged(isVideoEnabled);
+        }
 
-            if (isVideoEnabledStatechanged) {
-                log("handleFeatureCapabilityChanged - notifyForVideoCapabilityChanged=" +
-                        isVideoEnabled);
-                mPhone.notifyForVideoCapabilityChanged(isVideoEnabled);
-            }
+        if (DBG) log(sb.toString());
 
-            if (DBG) {
-                log(sb.toString());
-            }
-
-            if (DBG) log("handleFeatureCapabilityChanged: isVolteEnabled=" + isVolteEnabled()
+        if (DBG) {
+            log("handleFeatureCapabilityChanged: isVolteEnabled=" + isVolteEnabled()
                     + ", isVideoCallEnabled=" + isVideoCallEnabled()
                     + ", isVowifiEnabled=" + isVowifiEnabled()
                     + ", isUtEnabled=" + isUtEnabled());
-
-            mPhone.onFeatureCapabilityChanged();
-
-            mMetrics.writeOnImsCapabilities(
-                    mPhone.getPhoneId(), mImsFeatureEnabled);
         }
+
+        mPhone.onFeatureCapabilityChanged();
+
+        mMetrics.writeOnImsCapabilities(mPhone.getPhoneId(), getImsRegistrationTech(),
+                mMmTelCapabilities);
     }
 
     @VisibleForTesting
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
index 5a490ae..6bc0841 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
@@ -36,9 +36,9 @@
 import android.text.TextUtils;
 
 import com.android.ims.ImsCall;
-import com.android.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallProfile;
 import com.android.ims.ImsException;
-import com.android.ims.ImsStreamMediaProfile;
+import android.telephony.ims.ImsStreamMediaProfile;
 import com.android.ims.internal.ImsVideoCallProviderWrapper;
 import com.android.internal.telephony.CallStateException;
 import com.android.internal.telephony.Connection;
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
index 45b12ea..a72b904 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
@@ -40,9 +40,9 @@
 import android.text.TextUtils;
 
 import com.android.ims.ImsException;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.ImsSsData;
-import com.android.ims.ImsSsInfo;
+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;
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPullCall.java b/src/java/com/android/internal/telephony/imsphone/ImsPullCall.java
index dbb4036..3bf304e 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPullCall.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPullCall.java
@@ -16,8 +16,6 @@
 
 package com.android.internal.telephony.imsphone;
 
-import com.android.ims.ImsCallProfile;
-
 /**
  * Interface implemented by modules which are capable of performing a pull of an external call.
  * This is used to break the dependency between {@link ImsExternalCallTracker} and
diff --git a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
index a0c5630..f93dadb 100644
--- a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
+++ b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
@@ -17,6 +17,7 @@
 package com.android.internal.telephony.metrics;
 
 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ANSWER;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CDMA_SEND_SMS;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DEACTIVATE_DATA_CALL;
@@ -34,6 +35,7 @@
 import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_PPP;
 import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_UNKNOWN;
 
+import android.hardware.radio.V1_0.SetupDataCallResult;
 import android.os.Build;
 import android.os.SystemClock;
 import android.telephony.Rlog;
@@ -42,13 +44,14 @@
 import android.telephony.TelephonyManager;
 import android.telephony.data.DataCallResponse;
 import android.telephony.data.DataService;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.text.TextUtils;
 import android.util.Base64;
 import android.util.SparseArray;
 
-import com.android.ims.ImsConfig;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.internal.ImsCallSession;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsCallSession;
 import com.android.internal.telephony.GsmCdmaConnection;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.RIL;
@@ -836,26 +839,30 @@
      * @param feature IMS feature
      * @param network The IMS network type
      * @param value The settings. 0 indicates disabled, otherwise enabled.
-     * @param status IMS operation status. See OperationStatusConstants for details.
      */
-    public void writeImsSetFeatureValue(int phoneId, int feature, int network, int value,
-                                        int status) {
+    public void writeImsSetFeatureValue(int phoneId, int feature, int network, int value) {
         TelephonySettings s = new TelephonySettings();
-        switch (feature) {
-            case ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE:
-                s.isEnhanced4GLteModeEnabled = (value != 0);
-                break;
-            case ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI:
-                s.isWifiCallingEnabled = (value != 0);
-                break;
-            case ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE:
-                s.isVtOverLteEnabled = (value != 0);
-                break;
-            case ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_WIFI:
-                s.isVtOverWifiEnabled = (value != 0);
-                break;
+        if (network == ImsRegistrationImplBase.REGISTRATION_TECH_LTE) {
+            switch (feature) {
+                case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE:
+                    s.isEnhanced4GLteModeEnabled = (value != 0);
+                    break;
+                case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO:
+                    s.isVtOverLteEnabled = (value != 0);
+                    break;
+            }
+        } else if (network == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN) {
+            switch (feature) {
+                case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE:
+                    s.isWifiCallingEnabled = (value != 0);
+                    break;
+                case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO:
+                    s.isVtOverWifiEnabled = (value != 0);
+                    break;
+            }
         }
 
+
         // If the settings don't change, we don't log the event.
         if (mLastSettings.get(phoneId) != null &&
                 Arrays.equals(TelephonySettings.toByteArray(mLastSettings.get(phoneId)),
@@ -952,15 +959,27 @@
      * @param phoneId Phone id
      * @param capabilities IMS capabilities array
      */
-    public synchronized void writeOnImsCapabilities(int phoneId, boolean[] capabilities) {
+    public synchronized void writeOnImsCapabilities(int phoneId,
+            @ImsRegistrationImplBase.ImsRegistrationTech int radioTech,
+            MmTelFeature.MmTelCapabilities capabilities) {
         ImsCapabilities cap = new ImsCapabilities();
 
-        cap.voiceOverLte = capabilities[0];
-        cap.videoOverLte = capabilities[1];
-        cap.voiceOverWifi = capabilities[2];
-        cap.videoOverWifi = capabilities[3];
-        cap.utOverLte = capabilities[4];
-        cap.utOverWifi = capabilities[5];
+        if (radioTech == ImsRegistrationImplBase.REGISTRATION_TECH_LTE) {
+            cap.voiceOverLte = capabilities.isCapable(
+                    MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
+            cap.videoOverLte = capabilities.isCapable(
+                    MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO);
+            cap.utOverLte = capabilities.isCapable(
+                    MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT);
+
+        } else if (radioTech == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN) {
+            cap.voiceOverWifi = capabilities.isCapable(
+                    MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
+            cap.videoOverWifi = capabilities.isCapable(
+                    MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO);
+            cap.utOverWifi = capabilities.isCapable(
+                    MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT);
+        }
 
         TelephonyEvent event = new TelephonyEventBuilder(phoneId).setImsCapabilities(cap).build();
 
@@ -1312,26 +1331,26 @@
      * @param rilSerial RIL request serial number
      * @param rilError RIL error
      * @param rilRequest RIL request
-     * @param response Data call response
+     * @param result Data call result
      */
     private void writeOnSetupDataCallResponse(int phoneId, int rilSerial, int rilError,
-                                              int rilRequest, DataCallResponse response) {
+                                              int rilRequest, SetupDataCallResult result) {
 
         RilSetupDataCallResponse setupDataCallResponse = new RilSetupDataCallResponse();
         RilDataCall dataCall = new RilDataCall();
 
-        if (response != null) {
-            setupDataCallResponse.status = (response.getStatus() == 0
-                    ? RilDataCallFailCause.PDP_FAIL_NONE : response.getStatus());
-            setupDataCallResponse.suggestedRetryTimeMillis = response.getSuggestedRetryTime();
+        if (result != null) {
+            setupDataCallResponse.status =
+                    (result.status == 0 ? RilDataCallFailCause.PDP_FAIL_NONE : result.status);
+            setupDataCallResponse.suggestedRetryTimeMillis = result.suggestedRetryTime;
 
-            dataCall.cid = response.getCallId();
-            if (!TextUtils.isEmpty(response.getType())) {
-                dataCall.type = toPdpType(response.getType());
+            dataCall.cid = result.cid;
+            if (!TextUtils.isEmpty(result.type)) {
+                dataCall.type = toPdpType(result.type);
             }
 
-            if (!TextUtils.isEmpty(response.getIfname())) {
-                dataCall.iframe = response.getIfname();
+            if (!TextUtils.isEmpty(result.ifname)) {
+                dataCall.iframe = result.ifname;
             }
         }
         setupDataCallResponse.call = dataCall;
@@ -1419,8 +1438,8 @@
                                             int rilRequest, Object ret) {
         switch (rilRequest) {
             case RIL_REQUEST_SETUP_DATA_CALL:
-                DataCallResponse dataCall = (DataCallResponse) ret;
-                writeOnSetupDataCallResponse(phoneId, rilSerial, rilError, rilRequest, dataCall);
+                SetupDataCallResult result = (SetupDataCallResult) ret;
+                writeOnSetupDataCallResponse(phoneId, rilSerial, rilError, rilRequest, result);
                 break;
             case RIL_REQUEST_DEACTIVATE_DATA_CALL:
                 writeOnDeactivateDataCallResponse(phoneId, rilError);
diff --git a/src/java/com/android/internal/telephony/sip/SipPhoneBase.java b/src/java/com/android/internal/telephony/sip/SipPhoneBase.java
index ade427b..0943b78 100755
--- a/src/java/com/android/internal/telephony/sip/SipPhoneBase.java
+++ b/src/java/com/android/internal/telephony/sip/SipPhoneBase.java
@@ -44,7 +44,6 @@
 import com.android.internal.telephony.PhoneNotifier;
 import com.android.internal.telephony.TelephonyProperties;
 import com.android.internal.telephony.UUSInfo;
-import com.android.internal.telephony.dataconnection.DataConnection;
 import com.android.internal.telephony.uicc.IccFileHandler;
 
 import java.util.ArrayList;
@@ -416,14 +415,6 @@
     }
 
     @Override
-    public void getDataCallList(Message response) {
-    }
-
-    public List<DataConnection> getCurrentDataConnectionList () {
-        return null;
-    }
-
-    @Override
     public void updateServiceLocation() {
     }
 
diff --git a/src/java/com/android/internal/telephony/test/SimulatedCommands.java b/src/java/com/android/internal/telephony/test/SimulatedCommands.java
index ff8c0a8..cc9886f 100644
--- a/src/java/com/android/internal/telephony/test/SimulatedCommands.java
+++ b/src/java/com/android/internal/telephony/test/SimulatedCommands.java
@@ -17,11 +17,10 @@
 package com.android.internal.telephony.test;
 
 import android.hardware.radio.V1_0.DataRegStateResult;
+import android.hardware.radio.V1_0.SetupDataCallResult;
 import android.hardware.radio.V1_0.VoiceRegStateResult;
 import android.net.KeepalivePacketData;
-import android.net.LinkAddress;
 import android.net.LinkProperties;
-import android.net.NetworkUtils;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -35,6 +34,7 @@
 import android.telephony.CellInfoGsm;
 import android.telephony.IccOpenLogicalChannelResponse;
 import android.telephony.ImsiEncryptionInfo;
+import android.telephony.NetworkRegistrationState;
 import android.telephony.NetworkScanRequest;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
@@ -61,7 +61,6 @@
 import com.android.internal.telephony.uicc.IccSlotStatus;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -120,10 +119,17 @@
     int mNetworkType;
     String mPin2Code;
     boolean mSsnNotifyOn = false;
-    private int mVoiceRegState = ServiceState.RIL_REG_STATE_HOME;
+    private int mVoiceRegState = NetworkRegistrationState.REG_STATE_HOME;
     private int mVoiceRadioTech = ServiceState.RIL_RADIO_TECHNOLOGY_UMTS;
-    private int mDataRegState = ServiceState.RIL_REG_STATE_HOME;
+    private int mDataRegState = NetworkRegistrationState.REG_STATE_HOME;
     private int mDataRadioTech = ServiceState.RIL_RADIO_TECHNOLOGY_UMTS;
+    public boolean mCssSupported;
+    public int mRoamingIndicator;
+    public int mSystemIsInPrl;
+    public int mDefaultRoamingIndicator;
+    public int mReasonForDenial;
+    public int mMaxDataCalls;
+
     private SignalStrength mSignalStrength;
     private List<CellInfo> mCellInfoList;
     private int[] mImsRegState;
@@ -138,7 +144,7 @@
     int mNextCallFailCause = CallFailCause.NORMAL_CLEARING;
 
     private boolean mDcSuccess = true;
-    private DataCallResponse mDcResponse;
+    private SetupDataCallResult mSetupDataCallResult;
     private boolean mIsRadioPowerFailResponse = false;
 
     //***** Constructor
@@ -958,6 +964,11 @@
         VoiceRegStateResult ret = new VoiceRegStateResult();
         ret.regState = mVoiceRegState;
         ret.rat = mVoiceRadioTech;
+        ret.cssSupported = mCssSupported;
+        ret.roamingIndicator = mRoamingIndicator;
+        ret.systemIsInPrl = mSystemIsInPrl;
+        ret.defaultRoamingIndicator = mDefaultRoamingIndicator;
+        ret.reasonForDenial = mReasonForDenial;
 
         resultSuccess(result, ret);
     }
@@ -984,6 +995,8 @@
         DataRegStateResult ret = new DataRegStateResult();
         ret.regState = mDataRegState;
         ret.rat = mDataRadioTech;
+        ret.maxDataCalls = mMaxDataCalls;
+        ret.reasonDataDenied = mReasonForDenial;
 
         resultSuccess(result, ret);
     }
@@ -1111,8 +1124,8 @@
         unimplemented(response);
     }
 
-    public void setDataCallResponse(final boolean success, final DataCallResponse dcResponse) {
-        mDcResponse = dcResponse;
+    public void setDataCallResult(final boolean success, final SetupDataCallResult dcResult) {
+        mSetupDataCallResult = dcResult;
         mDcSuccess = success;
     }
 
@@ -1131,22 +1144,30 @@
         SimulatedCommandsVerifier.getInstance().setupDataCall(accessNetworkType, dataProfile,
                 isRoaming, allowRoaming, reason, linkProperties, result);
 
-        if (mDcResponse == null) {
+        if (mSetupDataCallResult == null) {
             try {
-                mDcResponse = new DataCallResponse(0, -1, 1, 2, "IP", "rmnet_data7",
-                        Arrays.asList(new LinkAddress("12.34.56.78/32")),
-                        Arrays.asList(NetworkUtils.numericToInetAddress("98.76.54.32")),
-                        Arrays.asList(NetworkUtils.numericToInetAddress("11.22.33.44")),
-                        null, 1440);
+                mSetupDataCallResult = new SetupDataCallResult();
+                mSetupDataCallResult.status = 0;
+                mSetupDataCallResult.suggestedRetryTime = -1;
+                mSetupDataCallResult.cid = 1;
+                mSetupDataCallResult.active = 2;
+                mSetupDataCallResult.type = "IP";
+                mSetupDataCallResult.ifname = "rmnet_data7";
+                mSetupDataCallResult.addresses = "12.34.56.78";
+                mSetupDataCallResult.dnses = "98.76.54.32";
+                mSetupDataCallResult.gateways = "11.22.33.44";
+                mSetupDataCallResult.pcscf = "";
+                mSetupDataCallResult.mtu = 1440;
             } catch (Exception e) {
 
             }
         }
 
         if (mDcSuccess) {
-            resultSuccess(result, mDcResponse);
+            resultSuccess(result, mSetupDataCallResult);
         } else {
-            resultFail(result, mDcResponse, new RuntimeException("Setup data call failed!"));
+            resultFail(result, mSetupDataCallResult,
+                    new RuntimeException("Setup data call failed!"));
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/test/TestConferenceEventPackageParser.java b/src/java/com/android/internal/telephony/test/TestConferenceEventPackageParser.java
index 735ed17..62c2a77 100644
--- a/src/java/com/android/internal/telephony/test/TestConferenceEventPackageParser.java
+++ b/src/java/com/android/internal/telephony/test/TestConferenceEventPackageParser.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.telephony.test;
 
-import com.android.ims.ImsConferenceState;
+import android.telephony.ims.ImsConferenceState;
 import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -26,7 +26,6 @@
 import android.util.Log;
 import android.util.Xml;
 
-import java.io.BufferedInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 
@@ -47,9 +46,9 @@
  * </pre>
  * <p>
  * Note: This XML format is similar to the information stored in the
- * {@link com.android.ims.ImsConferenceState} parcelable.  The {@code status} values expected in the
+ * {@link ImsConferenceState} parcelable.  The {@code status} values expected in the
  * XML are those found in the {@code ImsConferenceState} class (e.g.
- * {@link com.android.ims.ImsConferenceState#STATUS_CONNECTED}).
+ * {@link ImsConferenceState#STATUS_CONNECTED}).
  * <p>
  * Place a file formatted similar to above in /data/data/com.android.phone/files/ and invoke the
  * following command while you have an ongoing IMS call:
@@ -79,10 +78,10 @@
 
     /**
      * Parses the conference event package XML file and returns an
-     * {@link com.android.ims.ImsConferenceState} instance containing the participants described in
+     * {@link ImsConferenceState} instance containing the participants described in
      * the XML file.
      *
-     * @return The {@link com.android.ims.ImsConferenceState} instance.
+     * @return The {@link ImsConferenceState} instance.
      */
     public ImsConferenceState parse() {
         ImsConferenceState conferenceState = new ImsConferenceState();
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java b/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
index 56b299a..fa41922 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
@@ -302,7 +302,7 @@
                             | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS);
             return getCarrierPrivilegeStatus(pInfo);
         } catch (PackageManager.NameNotFoundException ex) {
-            Rlog.e(LOG_TAG, "NameNotFoundException", ex);
+            log("Package " + packageName + " not found for carrier privilege status check");
         }
         return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
     }
@@ -690,4 +690,4 @@
                 return "UNKNOWN";
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/telephonytests/AndroidManifest.xml b/tests/telephonytests/AndroidManifest.xml
index 028b1e0..35ed2fb 100644
--- a/tests/telephonytests/AndroidManifest.xml
+++ b/tests/telephonytests/AndroidManifest.xml
@@ -38,5 +38,6 @@
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
-
+    <uses-permission android:name="android.permission.BIND_TELEPHONY_NETWORK_SERVICE"/>
+    <uses-permission android:name="android.permission.BIND_TELEPHONY_DATA_SERVICE" />
 </manifest>
diff --git a/tests/telephonytests/src/android/telephony/ims/ImsCompatTests.java b/tests/telephonytests/src/android/telephony/ims/ImsCompatTests.java
new file mode 100644
index 0000000..caa245f
--- /dev/null
+++ b/tests/telephonytests/src/android/telephony/ims/ImsCompatTests.java
@@ -0,0 +1,79 @@
+/*
+ * 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 android.telephony.ims;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.aidl.IImsCallSessionListener;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.ims.internal.IImsMMTelFeature;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class ImsCompatTests {
+
+    TestMMTelFeatureCompat mCompatMmTel;
+    IImsMMTelFeature mCompatMmTelBinder;
+
+    TestImsCallSessionCompat mCompatCallSession;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mCompatMmTel = new TestMMTelFeatureCompat();
+        mCompatMmTelBinder = mCompatMmTel.getBinder();
+
+        mCompatCallSession = new TestImsCallSessionCompat();
+    }
+
+    @After
+    public void tearDown() {
+        mCompatMmTel = null;
+        mCompatMmTelBinder = null;
+
+        mCompatCallSession = null;
+    }
+
+    @SmallTest
+    @Test
+    public void testCreateCallSessionCompat() throws Exception {
+        mCompatMmTelBinder.createCallSession(0, new ImsCallProfile());
+        assertTrue(mCompatMmTel.isCreateCallSessionCalled);
+    }
+
+    @SmallTest
+    @Test
+    public void testListenerCompatLayer() throws Exception {
+        IImsCallSessionListener listener = mock(IImsCallSessionListener.class);
+        ImsReasonInfo info = new ImsReasonInfo();
+        mCompatCallSession.setListener(listener);
+
+        mCompatCallSession.mListener.callSessionTerminated(null, info);
+
+        verify(listener).callSessionTerminated(eq(info));
+    }
+}
diff --git a/tests/telephonytests/src/android/telephony/ims/ImsFeatureTest.java b/tests/telephonytests/src/android/telephony/ims/ImsFeatureTest.java
index 9bd7d6e..07d3223 100644
--- a/tests/telephonytests/src/android/telephony/ims/ImsFeatureTest.java
+++ b/tests/telephonytests/src/android/telephony/ims/ImsFeatureTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -17,13 +17,17 @@
 package android.telephony.ims;
 
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
 
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.verify;
 
-import android.os.IInterface;
+import android.os.Parcel;
 import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.feature.CapabilityChangeRequest;
 import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.ims.internal.IImsFeatureStatusCallback;
@@ -33,71 +37,198 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 @RunWith(AndroidJUnit4.class)
 public class ImsFeatureTest {
 
-    private TestImsFeature mTestImsService;
+    private TestImsFeature mTestImsFeature;
+    private ImsFeature.CapabilityCallback mCapabilityCallback;
 
     @Mock
     private IImsFeatureStatusCallback mTestStatusCallback;
     @Mock
     private IImsFeatureStatusCallback mTestStatusCallback2;
 
-    private class TestImsFeature extends ImsFeature {
-
-        public void testSetFeatureState(int featureState) {
-            setFeatureState(featureState);
-        }
-
-        @Override
-        public void onFeatureReady() {
-
-        }
-
-        @Override
-        public void onFeatureRemoved() {
-
-        }
-
-        @Override
-        public IInterface getBinder() {
-            return null;
-        }
-    }
-
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mTestImsService = new TestImsFeature();
+        mTestImsFeature = new TestImsFeature();
+        mCapabilityCallback = Mockito.spy(new ImsFeature.CapabilityCallback());
+        mTestImsFeature.addCapabilityCallback(mCapabilityCallback);
     }
 
     @After
     public void tearDown() {
-        mTestImsService = null;
+        mTestImsFeature = null;
+        mCapabilityCallback = null;
     }
 
     @Test
     @SmallTest
     public void testSetCallbackAndNotify() throws Exception {
-        mTestImsService.addImsFeatureStatusCallback(mTestStatusCallback);
-        mTestImsService.addImsFeatureStatusCallback(mTestStatusCallback2);
+        mTestImsFeature.addImsFeatureStatusCallback(mTestStatusCallback);
+        mTestImsFeature.addImsFeatureStatusCallback(mTestStatusCallback2);
 
-        verify(mTestStatusCallback).notifyImsFeatureStatus(eq(ImsFeature.STATE_NOT_AVAILABLE));
-        verify(mTestStatusCallback2).notifyImsFeatureStatus(eq(ImsFeature.STATE_NOT_AVAILABLE));
+        verify(mTestStatusCallback).notifyImsFeatureStatus(eq(ImsFeature.STATE_UNAVAILABLE));
+        verify(mTestStatusCallback2).notifyImsFeatureStatus(eq(ImsFeature.STATE_UNAVAILABLE));
     }
 
     @Test
     @SmallTest
     public void testSetFeatureAndCheckCallback() throws Exception {
-        mTestImsService.addImsFeatureStatusCallback(mTestStatusCallback);
-        mTestImsService.addImsFeatureStatusCallback(mTestStatusCallback2);
+        mTestImsFeature.addImsFeatureStatusCallback(mTestStatusCallback);
+        mTestImsFeature.addImsFeatureStatusCallback(mTestStatusCallback2);
 
-        mTestImsService.testSetFeatureState(ImsFeature.STATE_READY);
+        mTestImsFeature.testSetFeatureState(ImsFeature.STATE_READY);
 
         verify(mTestStatusCallback).notifyImsFeatureStatus(eq(ImsFeature.STATE_READY));
         verify(mTestStatusCallback2).notifyImsFeatureStatus(eq(ImsFeature.STATE_READY));
-        assertEquals(ImsFeature.STATE_READY, mTestImsService.getFeatureState());
+        assertEquals(ImsFeature.STATE_READY, mTestImsFeature.getFeatureState());
+    }
+
+    @SmallTest
+    @Test
+    public void testCapabilityConfigAdd() throws Exception {
+        ImsFeature.Capabilities c = new ImsFeature.Capabilities();
+        c.addCapabilities(TestImsFeature.CAPABILITY_TEST_1);
+
+        assertTrue(c.isCapable(TestImsFeature.CAPABILITY_TEST_1));
+    }
+
+    @SmallTest
+    @Test
+    public void testCapabilityConfigAddMultiple() throws Exception {
+        ImsFeature.Capabilities c = new ImsFeature.Capabilities();
+        c.addCapabilities(TestImsFeature.CAPABILITY_TEST_1);
+        c.addCapabilities(TestImsFeature.CAPABILITY_TEST_2);
+
+        assertTrue(c.isCapable(TestImsFeature.CAPABILITY_TEST_2));
+    }
+
+    @SmallTest
+    @Test
+    public void testCapabilityConfigHasMultiple() throws Exception {
+        ImsFeature.Capabilities c = new ImsFeature.Capabilities();
+        c.addCapabilities(TestImsFeature.CAPABILITY_TEST_1);
+        c.addCapabilities(TestImsFeature.CAPABILITY_TEST_2);
+
+        assertTrue(c.isCapable(
+                TestImsFeature.CAPABILITY_TEST_1 | TestImsFeature.CAPABILITY_TEST_2));
+    }
+
+    @SmallTest
+    @Test
+    public void testCapabilityConfigRemove() throws Exception {
+        ImsFeature.Capabilities c = new ImsFeature.Capabilities();
+        c.addCapabilities(TestImsFeature.CAPABILITY_TEST_1);
+        c.addCapabilities(TestImsFeature.CAPABILITY_TEST_2);
+        c.removeCapabilities(TestImsFeature.CAPABILITY_TEST_1);
+
+        assertTrue(c.isCapable(TestImsFeature.CAPABILITY_TEST_2));
+    }
+
+    @SmallTest
+    @Test
+    public void testSetCapabilityConfig() throws Exception {
+        CapabilityChangeRequest request = new CapabilityChangeRequest();
+        request.addCapabilitiesToEnableForTech(TestImsFeature.CAPABILITY_TEST_1,
+                ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
+
+        mTestImsFeature.requestChangeEnabledCapabilities(request, null);
+
+        assertEquals(request, mTestImsFeature.lastRequest);
+    }
+
+
+    @SmallTest
+    @Test
+    public void testSetCapabilityConfigError() throws Exception {
+        CapabilityChangeRequest request = new CapabilityChangeRequest();
+        request.addCapabilitiesToEnableForTech(TestImsFeature.CAPABILITY_TEST_1,
+                ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
+
+        mTestImsFeature.setCapabilitiesResult = ImsFeature.CAPABILITY_ERROR_GENERIC;
+        mTestImsFeature.requestChangeEnabledCapabilities(request, mCapabilityCallback);
+
+        verify(mCapabilityCallback).onChangeCapabilityConfigurationError(
+                eq(TestImsFeature.CAPABILITY_TEST_1),
+                eq(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN),
+                eq(ImsFeature.CAPABILITY_ERROR_GENERIC));
+        assertEquals(request, mTestImsFeature.lastRequest);
+    }
+
+    @SmallTest
+    @Test
+    public void testNotifyCapabilityStatusChanged() throws Exception {
+        ImsFeature.Capabilities status =
+                new ImsFeature.Capabilities();
+        status.addCapabilities(TestImsFeature.CAPABILITY_TEST_1);
+        status.addCapabilities(TestImsFeature.CAPABILITY_TEST_2);
+
+        mTestImsFeature.capabilitiesStatusChanged(status);
+
+        assertEquals(status.getMask(), mTestImsFeature.queryCapabilityStatus().getMask());
+    }
+
+    @SmallTest
+    @Test
+    public void testNotifyCapabilityStatusChangedCallback() throws Exception {
+        ImsFeature.Capabilities status =
+                new ImsFeature.Capabilities();
+        status.addCapabilities(TestImsFeature.CAPABILITY_TEST_1);
+        status.addCapabilities(TestImsFeature.CAPABILITY_TEST_2);
+
+        mTestImsFeature.capabilitiesStatusChanged(status);
+
+        assertEquals(status.getMask(), mTestImsFeature.queryCapabilityStatus().getMask());
+        verify(mCapabilityCallback).onCapabilitiesStatusChanged(eq(status));
+    }
+
+    @SmallTest
+    @Test
+    public void testCapabilityChangeContainsFullSets() throws Exception {
+        CapabilityChangeRequest request = new CapabilityChangeRequest();
+        request.addCapabilitiesToEnableForTech(TestImsFeature.CAPABILITY_TEST_1
+                        | TestImsFeature.CAPABILITY_TEST_2,
+                ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
+        request.addCapabilitiesToEnableForTech(TestImsFeature.CAPABILITY_TEST_2,
+                ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+        request.addCapabilitiesToDisableForTech(TestImsFeature.CAPABILITY_TEST_1,
+                ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+
+        mTestImsFeature.changeEnabledCapabilities(request, /*Callback*/null);
+
+        assertTrue(request.getCapabilitiesToDisable().containsAll(
+                mTestImsFeature.lastRequest.getCapabilitiesToDisable()));
+        assertTrue(request.getCapabilitiesToEnable().containsAll(
+                mTestImsFeature.lastRequest.getCapabilitiesToEnable()));
+    }
+
+    @SmallTest
+    @Test
+    public void testCapabilityChangeRequestParcel() throws Exception {
+        CapabilityChangeRequest request = new CapabilityChangeRequest();
+        // add some capabilities
+        request.addCapabilitiesToEnableForTech(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+                ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
+        request.addCapabilitiesToEnableForTech(
+                MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO
+                        | MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+                ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+        request.addCapabilitiesToDisableForTech(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT,
+                ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+        request.addCapabilitiesToDisableForTech(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT,
+                ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
+
+        Parcel p = Parcel.obtain();
+        request.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        CapabilityChangeRequest result =
+                CapabilityChangeRequest.CREATOR.createFromParcel(p);
+        p.recycle();
+
+        assertEquals(request, result);
     }
 }
diff --git a/tests/telephonytests/src/android/telephony/ims/ImsRegistrationTests.java b/tests/telephonytests/src/android/telephony/ims/ImsRegistrationTests.java
index 77c81af..65cb5f7 100644
--- a/tests/telephonytests/src/android/telephony/ims/ImsRegistrationTests.java
+++ b/tests/telephonytests/src/android/telephony/ims/ImsRegistrationTests.java
@@ -28,15 +28,13 @@
 import android.os.RemoteException;
 import android.support.test.runner.AndroidJUnit4;
 import android.telephony.ServiceState;
-import android.telephony.ims.internal.feature.ImsFeature;
-import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
+import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.stub.ImsFeatureConfiguration;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.internal.IImsRegistration;
-import com.android.ims.internal.IImsRegistrationCallback;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
diff --git a/tests/telephonytests/src/android/telephony/ims/ImsServiceTest.java b/tests/telephonytests/src/android/telephony/ims/ImsServiceTest.java
index 6936362..37fe7b7 100644
--- a/tests/telephonytests/src/android/telephony/ims/ImsServiceTest.java
+++ b/tests/telephonytests/src/android/telephony/ims/ImsServiceTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -16,8 +16,6 @@
 
 package android.telephony.ims;
 
-import static com.android.internal.telephony.ims.ImsResolver.SERVICE_INTERFACE;
-
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertNull;
@@ -25,29 +23,29 @@
 import static junit.framework.Assert.fail;
 
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
 import android.content.Context;
 import android.content.Intent;
 import android.os.RemoteException;
 import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.aidl.IImsServiceController;
 import android.telephony.ims.feature.ImsFeature;
-import android.telephony.ims.feature.MMTelFeature;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsFeatureConfiguration;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.SparseArray;
 
 import com.android.ims.ImsManager;
 import com.android.ims.internal.IImsFeatureStatusCallback;
-import com.android.ims.internal.IImsMMTelFeature;
-import com.android.ims.internal.IImsServiceController;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
 
 /**
  * Unit tests for ImsService
@@ -61,21 +59,22 @@
     private TestImsService mTestImsService;
     private IImsServiceController mTestImsServiceBinder;
 
-    @Mock
     private Context mMockContext;
-    @Mock
     private IImsFeatureStatusCallback mTestCallback;
 
     @Before
     public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
+        mMockContext = mock(Context.class);
+        mTestCallback = mock(IImsFeatureStatusCallback.class);
         mTestImsService = new TestImsService(mMockContext);
         mTestImsServiceBinder = (IImsServiceController) mTestImsService.onBind(
-                new Intent(SERVICE_INTERFACE));
+                new Intent(ImsService.SERVICE_INTERFACE));
     }
 
     @After
     public void tearDown() throws Exception {
+        mMockContext = null;
+        mTestCallback = null;
         mTestImsService = null;
         mTestImsServiceBinder = null;
     }
@@ -83,45 +82,46 @@
     @Test
     @SmallTest
     public void testCreateMMTelFeature() throws RemoteException {
-        IImsMMTelFeature f = mTestImsServiceBinder.createMMTelFeature(TEST_SLOT_0, mTestCallback);
-        mTestImsService.mTestMMTelFeature.sendSetFeatureState(ImsFeature.STATE_READY);
+        IImsMmTelFeature f = mTestImsServiceBinder.createMmTelFeature(TEST_SLOT_0, mTestCallback);
+        mTestImsService.mTestMmTelFeature.sendSetFeatureState(ImsFeature.STATE_READY);
 
         SparseArray<ImsFeature> features = mTestImsService.getFeatures(TEST_SLOT_0);
-        ImsFeature featureToVerify = features.get(ImsFeature.MMTEL);
-        MMTelFeature testMMTelFeature = null;
-        if (featureToVerify instanceof MMTelFeature) {
-            testMMTelFeature = (MMTelFeature) featureToVerify;
+        ImsFeature featureToVerify = features.get(ImsFeature.FEATURE_MMTEL);
+        MmTelFeature testMMTelFeature = null;
+        if (featureToVerify instanceof MmTelFeature) {
+            testMMTelFeature = (MmTelFeature) featureToVerify;
         } else {
             fail();
         }
-        assertEquals(mTestImsService.mSpyMMTelFeature, testMMTelFeature);
+        assertEquals(mTestImsService.mSpyMmTelFeature, testMMTelFeature);
         // Verify that upon creating a feature, we assign the callback and get the set feature state
         // when querying it.
-        verify(mTestImsService.mSpyMMTelFeature).addImsFeatureStatusCallback(eq(mTestCallback));
-        assertEquals(ImsFeature.STATE_READY, f.getFeatureStatus());
+        verify(mTestImsService.mSpyMmTelFeature).addImsFeatureStatusCallback(eq(mTestCallback));
+        assertEquals(ImsFeature.STATE_READY, f.getFeatureState());
     }
 
     @Test
     @SmallTest
     public void testRemoveMMTelFeature() throws RemoteException {
-        mTestImsServiceBinder.createMMTelFeature(TEST_SLOT_0, mTestCallback);
+        mTestImsServiceBinder.createMmTelFeature(TEST_SLOT_0, mTestCallback);
 
-        mTestImsServiceBinder.removeImsFeature(TEST_SLOT_0, ImsFeature.MMTEL, mTestCallback);
+        mTestImsServiceBinder.removeImsFeature(TEST_SLOT_0, ImsFeature.FEATURE_MMTEL,
+                mTestCallback);
 
-        verify(mTestImsService.mSpyMMTelFeature).onFeatureRemoved();
-        verify(mTestImsService.mSpyMMTelFeature).removeImsFeatureStatusCallback(mTestCallback);
+        verify(mTestImsService.mSpyMmTelFeature).onFeatureRemoved();
+        verify(mTestImsService.mSpyMmTelFeature).removeImsFeatureStatusCallback(mTestCallback);
         SparseArray<ImsFeature> features = mTestImsService.getFeatures(TEST_SLOT_0);
-        assertNull(features.get(ImsFeature.MMTEL));
+        assertNull(features.get(ImsFeature.FEATURE_MMTEL));
     }
 
     @Test
     @SmallTest
     public void testCallMethodOnCreatedFeature() throws RemoteException {
-        IImsMMTelFeature f = mTestImsServiceBinder.createMMTelFeature(TEST_SLOT_0, mTestCallback);
+        IImsMmTelFeature f = mTestImsServiceBinder.createMmTelFeature(TEST_SLOT_0, mTestCallback);
 
-        f.isConnected(0/*callSessionType*/, 0 /*callType*/);
+        f.getUtInterface();
 
-        assertTrue(mTestImsService.mTestMMTelFeature.isConnectedCalled);
+        assertTrue(mTestImsService.mTestMmTelFeature.isUtInterfaceCalled);
     }
 
     /**
@@ -131,9 +131,9 @@
     @Test
     @SmallTest
     public void testImsServiceUpSentCompat() throws RemoteException {
-        mTestImsServiceBinder.createMMTelFeature(TEST_SLOT_0, mTestCallback);
+        mTestImsServiceBinder.createMmTelFeature(TEST_SLOT_0, mTestCallback);
 
-        mTestImsService.mSpyMMTelFeature.sendSetFeatureState(ImsFeature.STATE_READY);
+        mTestImsService.mSpyMmTelFeature.sendSetFeatureState(ImsFeature.STATE_READY);
 
         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
         verify(mMockContext).sendBroadcast(intentCaptor.capture());
@@ -153,9 +153,9 @@
     @Test
     @SmallTest
     public void testImsServiceDownSentCompatInitializing() throws RemoteException {
-        mTestImsServiceBinder.createMMTelFeature(TEST_SLOT_0, mTestCallback);
+        mTestImsServiceBinder.createMmTelFeature(TEST_SLOT_0, mTestCallback);
 
-        mTestImsService.mSpyMMTelFeature.sendSetFeatureState(ImsFeature.STATE_INITIALIZING);
+        mTestImsService.mSpyMmTelFeature.sendSetFeatureState(ImsFeature.STATE_INITIALIZING);
 
         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
         verify(mMockContext).sendBroadcast(intentCaptor.capture());
@@ -168,6 +168,23 @@
         }
     }
 
+    /**
+     * Tests that the ImsService will return the correct ImsFeatureConfiguration when queried.
+     */
+    @Test
+    @SmallTest
+    public void testQuerySupportedImsFeatures() throws RemoteException {
+        ImsFeatureConfiguration config = new ImsFeatureConfiguration.Builder()
+                .addFeature(ImsFeature.FEATURE_MMTEL)
+                .addFeature(ImsFeature.FEATURE_RCS)
+                .build();
+        mTestImsService.testFeatureConfig = config;
+
+        ImsFeatureConfiguration result = mTestImsServiceBinder.querySupportedImsFeatures();
+
+        assertEquals(config, result);
+    }
+
     private void verifyServiceDownSent(Intent testIntent) {
         assertEquals(ImsManager.ACTION_IMS_SERVICE_DOWN, testIntent.getAction());
         assertEquals(TEST_SLOT_0, testIntent.getIntExtra(ImsManager.EXTRA_PHONE_ID, -1));
diff --git a/tests/telephonytests/src/android/telephony/ims/internal/MmTelFeatureTests.java b/tests/telephonytests/src/android/telephony/ims/MmTelFeatureTests.java
similarity index 76%
rename from tests/telephonytests/src/android/telephony/ims/internal/MmTelFeatureTests.java
rename to tests/telephonytests/src/android/telephony/ims/MmTelFeatureTests.java
index c2cdd75..ccbc251 100644
--- a/tests/telephonytests/src/android/telephony/ims/internal/MmTelFeatureTests.java
+++ b/tests/telephonytests/src/android/telephony/ims/MmTelFeatureTests.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -14,23 +14,24 @@
  * limitations under the License.
  */
 
-package android.telephony.ims.internal;
+package android.telephony.ims;
 
 import static junit.framework.Assert.assertEquals;
 
+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.support.test.runner.AndroidJUnit4;
-import android.telephony.ims.internal.aidl.IImsMmTelFeature;
-import android.telephony.ims.internal.feature.ImsFeature;
-import android.telephony.ims.internal.feature.MmTelFeature;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsCallSessionImplBase;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.ImsCallSession;
 
 import org.junit.After;
 import org.junit.Before;
@@ -45,7 +46,7 @@
     private static final int TEST_CAPABILITY = 1;
     private static final int TEST_RADIO_TECH = 0;
 
-    private TestMmTelFeature mFeature;
+    private android.telephony.ims.TestMmTelFeature mFeature;
     private IImsMmTelFeature mFeatureBinder;
     private ImsFeature.CapabilityCallback mCapabilityCallback;
     private MmTelFeature.Listener mListener;
@@ -81,12 +82,13 @@
     @Test
     public void testNewIncomingCall() throws Exception {
         IImsCallSession sessionBinder = Mockito.mock(IImsCallSession.class);
-        ImsCallSession session = new ImsCallSession(sessionBinder);
+        ImsCallSessionImplBase session = new ImsCallSessionImplBase();
+        session.setServiceImpl(sessionBinder);
 
         mFeature.incomingCall(session);
-        ArgumentCaptor<ImsCallSession> captor = ArgumentCaptor.forClass(ImsCallSession.class);
-        verify(mListener).onIncomingCall(captor.capture());
+        ArgumentCaptor<IImsCallSession> captor = ArgumentCaptor.forClass(IImsCallSession.class);
+        verify(mListener).onIncomingCall(captor.capture(), any());
 
-        assertEquals(sessionBinder, captor.getValue().getSession());
+        assertEquals(sessionBinder, captor.getValue());
     }
 }
diff --git a/tests/telephonytests/src/android/telephony/ims/TestImsCallSessionCompat.java b/tests/telephonytests/src/android/telephony/ims/TestImsCallSessionCompat.java
new file mode 100644
index 0000000..b3c5e00
--- /dev/null
+++ b/tests/telephonytests/src/android/telephony/ims/TestImsCallSessionCompat.java
@@ -0,0 +1,32 @@
+/*
+ * 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 android.telephony.ims;
+
+import android.telephony.ims.compat.stub.ImsCallSessionImplBase;
+
+import com.android.ims.internal.IImsCallSessionListener;
+
+public class TestImsCallSessionCompat extends ImsCallSessionImplBase {
+
+
+    IImsCallSessionListener mListener;
+
+    @Override
+    public void setListener(IImsCallSessionListener listener) {
+        mListener = listener;
+    }
+}
diff --git a/tests/telephonytests/src/android/telephony/ims/internal/TestImsFeature.java b/tests/telephonytests/src/android/telephony/ims/TestImsFeature.java
similarity index 89%
rename from tests/telephonytests/src/android/telephony/ims/internal/TestImsFeature.java
rename to tests/telephonytests/src/android/telephony/ims/TestImsFeature.java
index efbf95e..ec09b4a 100644
--- a/tests/telephonytests/src/android/telephony/ims/internal/TestImsFeature.java
+++ b/tests/telephonytests/src/android/telephony/ims/TestImsFeature.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-package android.telephony.ims.internal;
+package android.telephony.ims;
 
 import android.os.IInterface;
-import android.telephony.ims.internal.feature.CapabilityChangeRequest;
-import android.telephony.ims.internal.feature.ImsFeature;
+import android.telephony.ims.feature.CapabilityChangeRequest;
+import android.telephony.ims.feature.ImsFeature;
 
 public class TestImsFeature extends ImsFeature {
 
diff --git a/tests/telephonytests/src/android/telephony/ims/TestImsService.java b/tests/telephonytests/src/android/telephony/ims/TestImsService.java
index 380d70a..944514a 100644
--- a/tests/telephonytests/src/android/telephony/ims/TestImsService.java
+++ b/tests/telephonytests/src/android/telephony/ims/TestImsService.java
@@ -16,43 +16,46 @@
 
 package android.telephony.ims;
 
+import static org.mockito.Mockito.spy;
+
 import android.content.Context;
-import android.telephony.ims.feature.MMTelFeature;
+import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.feature.RcsFeature;
+import android.telephony.ims.stub.ImsFeatureConfiguration;
 
 import org.mockito.MockitoAnnotations;
 
-import static org.mockito.Mockito.spy;
-
 /**
  * Test ImsService used by mockito to verify functionality.
  */
 
-public class TestImsService extends ImsService {
+public class TestImsService extends android.telephony.ims.ImsService {
 
-    public TestMMTelFeature mSpyMMTelFeature;
-    public TestMMTelFeature mTestMMTelFeature;
+    public TestMmTelFeature mSpyMmTelFeature;
+    public TestMmTelFeature mTestMmTelFeature;
+
+    public ImsFeatureConfiguration testFeatureConfig;
 
     public TestImsService(Context context) {
         attachBaseContext(context);
         MockitoAnnotations.initMocks(this);
         // Must create real MMTelFeature to initialize ImsFeature objects.
-        mTestMMTelFeature = new TestMMTelFeature();
-        mSpyMMTelFeature = spy(mTestMMTelFeature);
+        mTestMmTelFeature = new TestMmTelFeature();
+        mSpyMmTelFeature = spy(mTestMmTelFeature);
     }
 
     @Override
-    public MMTelFeature onCreateEmergencyMMTelImsFeature(int slotId) {
+    public MmTelFeature createMmTelFeature(int slotId) {
+        return mSpyMmTelFeature;
+    }
+
+    @Override
+    public RcsFeature createRcsFeature(int slotId) {
         return null;
     }
 
     @Override
-    public MMTelFeature onCreateMMTelImsFeature(int slotId) {
-        return mSpyMMTelFeature;
-    }
-
-    @Override
-    public RcsFeature onCreateRcsFeature(int slotId) {
-        return null;
+    public ImsFeatureConfiguration querySupportedImsFeatures() {
+        return testFeatureConfig;
     }
 }
diff --git a/tests/telephonytests/src/android/telephony/ims/TestMMTelFeature.java b/tests/telephonytests/src/android/telephony/ims/TestMMTelFeatureCompat.java
similarity index 60%
rename from tests/telephonytests/src/android/telephony/ims/TestMMTelFeature.java
rename to tests/telephonytests/src/android/telephony/ims/TestMMTelFeatureCompat.java
index 9e944bf..ee9ac82 100644
--- a/tests/telephonytests/src/android/telephony/ims/TestMMTelFeature.java
+++ b/tests/telephonytests/src/android/telephony/ims/TestMMTelFeatureCompat.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -16,18 +16,22 @@
 
 package android.telephony.ims;
 
-import android.telephony.ims.feature.MMTelFeature;
+import android.telephony.ims.compat.feature.MMTelFeature;
+
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsCallSessionListener;
 
 /**
  * MMTelFeature implementation used by mockito to test functionality.
  */
 
-public class TestMMTelFeature extends MMTelFeature {
+public class TestMMTelFeatureCompat extends MMTelFeature {
 
     public boolean isConnectedCalled = false;
+    public boolean isCreateCallSessionCalled = false;
 
     @Override
-    public boolean isConnected(int callSessionType, int callType) {
+    public boolean isConnected(int callSessinType, int callType) {
         isConnectedCalled = true;
         return true;
     }
@@ -43,4 +47,12 @@
     public void sendSetFeatureState(int state) {
         setFeatureState(state);
     }
+
+    // Compatibility Method
+    @Override
+    public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
+            IImsCallSessionListener listener) {
+        isCreateCallSessionCalled = true;
+        return null;
+    }
 }
diff --git a/tests/telephonytests/src/android/telephony/ims/internal/TestMmTelFeature.java b/tests/telephonytests/src/android/telephony/ims/TestMmTelFeature.java
similarity index 80%
rename from tests/telephonytests/src/android/telephony/ims/internal/TestMmTelFeature.java
rename to tests/telephonytests/src/android/telephony/ims/TestMmTelFeature.java
index e960957..795d1c0 100644
--- a/tests/telephonytests/src/android/telephony/ims/internal/TestMmTelFeature.java
+++ b/tests/telephonytests/src/android/telephony/ims/TestMmTelFeature.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -14,20 +14,18 @@
  * limitations under the License.
  */
 
-package android.telephony.ims.internal;
+package android.telephony.ims;
 
 import android.os.RemoteException;
-import android.telephony.ims.internal.feature.CapabilityChangeRequest;
-import android.telephony.ims.internal.feature.ImsFeature;
-import android.telephony.ims.internal.feature.MmTelFeature;
+import android.telephony.ims.feature.CapabilityChangeRequest;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsCallSessionImplBase;
 import android.telephony.ims.stub.ImsEcbmImplBase;
 import android.telephony.ims.stub.ImsMultiEndpointImplBase;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.telephony.ims.stub.ImsUtImplBase;
 
-import com.android.ims.ImsCallProfile;
-import com.android.ims.internal.ImsCallSession;
-
 public class TestMmTelFeature extends MmTelFeature {
 
     public boolean queryConfigurationResult = false;
@@ -35,8 +33,8 @@
     public CapabilityChangeRequest lastRequest;
     public boolean isUtInterfaceCalled = false;
 
-    public void incomingCall(ImsCallSession c) throws RemoteException {
-        notifyIncomingCall(c);
+    public void incomingCall(ImsCallSessionImplBase c) throws RemoteException {
+        notifyIncomingCall(c, null);
     }
 
     @Override
@@ -45,9 +43,8 @@
     }
 
     @Override
-    public ImsCallSession createCallSession(ImsCallProfile profile,
-            ImsCallSessionListener listener) {
-        return super.createCallSession(profile, listener);
+    public ImsCallSessionImplBase createCallSession(ImsCallProfile profile) {
+        return super.createCallSession(profile);
     }
 
     @Override
diff --git a/tests/telephonytests/src/android/telephony/ims/internal/ImsFeatureTest.java b/tests/telephonytests/src/android/telephony/ims/internal/ImsFeatureTest.java
deleted file mode 100644
index 703eede..0000000
--- a/tests/telephonytests/src/android/telephony/ims/internal/ImsFeatureTest.java
+++ /dev/null
@@ -1,234 +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 android.telephony.ims.internal;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertTrue;
-
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.verify;
-
-import android.os.Parcel;
-import android.support.test.runner.AndroidJUnit4;
-import android.telephony.ims.internal.feature.CapabilityChangeRequest;
-import android.telephony.ims.internal.feature.ImsFeature;
-import android.telephony.ims.internal.feature.MmTelFeature;
-import android.telephony.ims.stub.ImsRegistrationImplBase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.ims.internal.IImsFeatureStatusCallback;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-@RunWith(AndroidJUnit4.class)
-public class ImsFeatureTest {
-
-    private TestImsFeature mTestImsFeature;
-    private ImsFeature.CapabilityCallback mCapabilityCallback;
-
-    @Mock
-    private IImsFeatureStatusCallback mTestStatusCallback;
-    @Mock
-    private IImsFeatureStatusCallback mTestStatusCallback2;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mTestImsFeature = new TestImsFeature();
-        mCapabilityCallback = Mockito.spy(new ImsFeature.CapabilityCallback());
-        mTestImsFeature.addCapabilityCallback(mCapabilityCallback);
-    }
-
-    @After
-    public void tearDown() {
-        mTestImsFeature = null;
-        mCapabilityCallback = null;
-    }
-
-    @Test
-    @SmallTest
-    public void testSetCallbackAndNotify() throws Exception {
-        mTestImsFeature.addImsFeatureStatusCallback(mTestStatusCallback);
-        mTestImsFeature.addImsFeatureStatusCallback(mTestStatusCallback2);
-
-        verify(mTestStatusCallback).notifyImsFeatureStatus(eq(ImsFeature.STATE_UNAVAILABLE));
-        verify(mTestStatusCallback2).notifyImsFeatureStatus(eq(ImsFeature.STATE_UNAVAILABLE));
-    }
-
-    @Test
-    @SmallTest
-    public void testSetFeatureAndCheckCallback() throws Exception {
-        mTestImsFeature.addImsFeatureStatusCallback(mTestStatusCallback);
-        mTestImsFeature.addImsFeatureStatusCallback(mTestStatusCallback2);
-
-        mTestImsFeature.testSetFeatureState(ImsFeature.STATE_READY);
-
-        verify(mTestStatusCallback).notifyImsFeatureStatus(eq(ImsFeature.STATE_READY));
-        verify(mTestStatusCallback2).notifyImsFeatureStatus(eq(ImsFeature.STATE_READY));
-        assertEquals(ImsFeature.STATE_READY, mTestImsFeature.getFeatureState());
-    }
-
-    @SmallTest
-    @Test
-    public void testCapabilityConfigAdd() throws Exception {
-        ImsFeature.Capabilities c = new ImsFeature.Capabilities();
-        c.addCapabilities(TestImsFeature.CAPABILITY_TEST_1);
-
-        assertTrue(c.isCapable(TestImsFeature.CAPABILITY_TEST_1));
-    }
-
-    @SmallTest
-    @Test
-    public void testCapabilityConfigAddMultiple() throws Exception {
-        ImsFeature.Capabilities c = new ImsFeature.Capabilities();
-        c.addCapabilities(TestImsFeature.CAPABILITY_TEST_1);
-        c.addCapabilities(TestImsFeature.CAPABILITY_TEST_2);
-
-        assertTrue(c.isCapable(TestImsFeature.CAPABILITY_TEST_2));
-    }
-
-    @SmallTest
-    @Test
-    public void testCapabilityConfigHasMultiple() throws Exception {
-        ImsFeature.Capabilities c = new ImsFeature.Capabilities();
-        c.addCapabilities(TestImsFeature.CAPABILITY_TEST_1);
-        c.addCapabilities(TestImsFeature.CAPABILITY_TEST_2);
-
-        assertTrue(c.isCapable(
-                TestImsFeature.CAPABILITY_TEST_1 | TestImsFeature.CAPABILITY_TEST_2));
-    }
-
-    @SmallTest
-    @Test
-    public void testCapabilityConfigRemove() throws Exception {
-        ImsFeature.Capabilities c = new ImsFeature.Capabilities();
-        c.addCapabilities(TestImsFeature.CAPABILITY_TEST_1);
-        c.addCapabilities(TestImsFeature.CAPABILITY_TEST_2);
-        c.removeCapabilities(TestImsFeature.CAPABILITY_TEST_1);
-
-        assertTrue(c.isCapable(TestImsFeature.CAPABILITY_TEST_2));
-    }
-
-    @SmallTest
-    @Test
-    public void testSetCapabilityConfig() throws Exception {
-        CapabilityChangeRequest request = new CapabilityChangeRequest();
-        request.addCapabilitiesToEnableForTech(TestImsFeature.CAPABILITY_TEST_1,
-                ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
-
-        mTestImsFeature.requestChangeEnabledCapabilities(request, null);
-
-        assertEquals(request, mTestImsFeature.lastRequest);
-    }
-
-
-    @SmallTest
-    @Test
-    public void testSetCapabilityConfigError() throws Exception {
-        CapabilityChangeRequest request = new CapabilityChangeRequest();
-        request.addCapabilitiesToEnableForTech(TestImsFeature.CAPABILITY_TEST_1,
-                ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
-
-        mTestImsFeature.setCapabilitiesResult = ImsFeature.CAPABILITY_ERROR_GENERIC;
-        mTestImsFeature.requestChangeEnabledCapabilities(request, mCapabilityCallback);
-
-        verify(mCapabilityCallback).onChangeCapabilityConfigurationError(
-                eq(TestImsFeature.CAPABILITY_TEST_1),
-                eq(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN),
-                eq(ImsFeature.CAPABILITY_ERROR_GENERIC));
-        assertEquals(request, mTestImsFeature.lastRequest);
-    }
-
-    @SmallTest
-    @Test
-    public void testNotifyCapabilityStatusChanged() throws Exception {
-        ImsFeature.Capabilities status =
-                new ImsFeature.Capabilities();
-        status.addCapabilities(TestImsFeature.CAPABILITY_TEST_1);
-        status.addCapabilities(TestImsFeature.CAPABILITY_TEST_2);
-
-        mTestImsFeature.capabilitiesStatusChanged(status);
-
-        assertEquals(status.getMask(), mTestImsFeature.queryCapabilityStatus().getMask());
-    }
-
-    @SmallTest
-    @Test
-    public void testNotifyCapabilityStatusChangedCallback() throws Exception {
-        ImsFeature.Capabilities status =
-                new ImsFeature.Capabilities();
-        status.addCapabilities(TestImsFeature.CAPABILITY_TEST_1);
-        status.addCapabilities(TestImsFeature.CAPABILITY_TEST_2);
-
-        mTestImsFeature.capabilitiesStatusChanged(status);
-
-        assertEquals(status.getMask(), mTestImsFeature.queryCapabilityStatus().getMask());
-        verify(mCapabilityCallback).onCapabilitiesStatusChanged(eq(status));
-    }
-
-    @SmallTest
-    @Test
-    public void testCapabilityChangeContainsFullSets() throws Exception {
-        CapabilityChangeRequest request = new CapabilityChangeRequest();
-        request.addCapabilitiesToEnableForTech(TestImsFeature.CAPABILITY_TEST_1
-                        | TestImsFeature.CAPABILITY_TEST_2,
-                ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
-        request.addCapabilitiesToEnableForTech(TestImsFeature.CAPABILITY_TEST_2,
-                ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
-        request.addCapabilitiesToDisableForTech(TestImsFeature.CAPABILITY_TEST_1,
-                ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
-
-        mTestImsFeature.changeEnabledCapabilities(request, /*Callback*/null);
-
-        assertTrue(request.getCapabilitiesToDisable().containsAll(
-                mTestImsFeature.lastRequest.getCapabilitiesToDisable()));
-        assertTrue(request.getCapabilitiesToEnable().containsAll(
-                mTestImsFeature.lastRequest.getCapabilitiesToEnable()));
-    }
-
-    @SmallTest
-    @Test
-    public void testCapabilityChangeRequestParcel() throws Exception {
-        CapabilityChangeRequest request = new CapabilityChangeRequest();
-        // add some capabilities
-        request.addCapabilitiesToEnableForTech(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
-                ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
-        request.addCapabilitiesToEnableForTech(
-                MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO
-                        | MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
-                ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
-        request.addCapabilitiesToDisableForTech(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT,
-                ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
-        request.addCapabilitiesToDisableForTech(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT,
-                ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
-
-        Parcel p = Parcel.obtain();
-        request.writeToParcel(p, 0);
-        p.setDataPosition(0);
-        CapabilityChangeRequest result =
-                CapabilityChangeRequest.CREATOR.createFromParcel(p);
-        p.recycle();
-
-        assertEquals(request, result);
-    }
-}
diff --git a/tests/telephonytests/src/android/telephony/ims/internal/ImsServiceTest.java b/tests/telephonytests/src/android/telephony/ims/internal/ImsServiceTest.java
deleted file mode 100644
index f4c1149..0000000
--- a/tests/telephonytests/src/android/telephony/ims/internal/ImsServiceTest.java
+++ /dev/null
@@ -1,199 +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 android.telephony.ims.internal;
-
-import static com.android.internal.telephony.ims.ImsResolver.SERVICE_INTERFACE;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.fail;
-
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.RemoteException;
-import android.support.test.runner.AndroidJUnit4;
-import android.telephony.ims.internal.aidl.IImsMmTelFeature;
-import android.telephony.ims.internal.aidl.IImsServiceController;
-import android.telephony.ims.internal.feature.ImsFeature;
-import android.telephony.ims.internal.feature.MmTelFeature;
-import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.SparseArray;
-
-import com.android.ims.ImsManager;
-import com.android.ims.internal.IImsFeatureStatusCallback;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-
-/**
- * Unit tests for ImsService
- */
-@RunWith(AndroidJUnit4.class)
-public class ImsServiceTest {
-
-    private static final int TEST_SLOT_0 = 0;
-    private static final int TEST_SLOT_1 = 1;
-
-    private TestImsService mTestImsService;
-    private IImsServiceController mTestImsServiceBinder;
-
-    private Context mMockContext;
-    private IImsFeatureStatusCallback mTestCallback;
-
-    @Before
-    public void setUp() throws Exception {
-        mMockContext = mock(Context.class);
-        mTestCallback = mock(IImsFeatureStatusCallback.class);
-        mTestImsService = new TestImsService(mMockContext);
-        mTestImsServiceBinder = (IImsServiceController) mTestImsService.onBind(
-                new Intent(SERVICE_INTERFACE));
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        mMockContext = null;
-        mTestCallback = null;
-        mTestImsService = null;
-        mTestImsServiceBinder = null;
-    }
-
-    @Test
-    @SmallTest
-    public void testCreateMMTelFeature() throws RemoteException {
-        IImsMmTelFeature f = mTestImsServiceBinder.createMmTelFeature(TEST_SLOT_0, mTestCallback);
-        mTestImsService.mTestMmTelFeature.sendSetFeatureState(ImsFeature.STATE_READY);
-
-        SparseArray<ImsFeature> features = mTestImsService.getFeatures(TEST_SLOT_0);
-        ImsFeature featureToVerify = features.get(ImsFeature.FEATURE_MMTEL);
-        MmTelFeature testMMTelFeature = null;
-        if (featureToVerify instanceof MmTelFeature) {
-            testMMTelFeature = (MmTelFeature) featureToVerify;
-        } else {
-            fail();
-        }
-        assertEquals(mTestImsService.mSpyMmTelFeature, testMMTelFeature);
-        // Verify that upon creating a feature, we assign the callback and get the set feature state
-        // when querying it.
-        verify(mTestImsService.mSpyMmTelFeature).addImsFeatureStatusCallback(eq(mTestCallback));
-        assertEquals(ImsFeature.STATE_READY, f.getFeatureState());
-    }
-
-    @Test
-    @SmallTest
-    public void testRemoveMMTelFeature() throws RemoteException {
-        mTestImsServiceBinder.createMmTelFeature(TEST_SLOT_0, mTestCallback);
-
-        mTestImsServiceBinder.removeImsFeature(TEST_SLOT_0, ImsFeature.FEATURE_MMTEL,
-                mTestCallback);
-
-        verify(mTestImsService.mSpyMmTelFeature).onFeatureRemoved();
-        verify(mTestImsService.mSpyMmTelFeature).removeImsFeatureStatusCallback(mTestCallback);
-        SparseArray<ImsFeature> features = mTestImsService.getFeatures(TEST_SLOT_0);
-        assertNull(features.get(ImsFeature.FEATURE_MMTEL));
-    }
-
-    @Test
-    @SmallTest
-    public void testCallMethodOnCreatedFeature() throws RemoteException {
-        IImsMmTelFeature f = mTestImsServiceBinder.createMmTelFeature(TEST_SLOT_0, mTestCallback);
-
-        f.getUtInterface();
-
-        assertTrue(mTestImsService.mTestMmTelFeature.isUtInterfaceCalled);
-    }
-
-    /**
-     * Tests that the new ImsService still sends the IMS_SERVICE_UP broadcast when the feature is
-     * set to ready.
-     */
-    @Test
-    @SmallTest
-    public void testImsServiceUpSentCompat() throws RemoteException {
-        mTestImsServiceBinder.createMmTelFeature(TEST_SLOT_0, mTestCallback);
-
-        mTestImsService.mSpyMmTelFeature.sendSetFeatureState(ImsFeature.STATE_READY);
-
-        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
-        verify(mMockContext).sendBroadcast(intentCaptor.capture());
-        try {
-            // Verify IMS_SERVICE_UP is sent
-            assertNotNull(intentCaptor.getValue());
-            verifyServiceUpSent(intentCaptor.getValue());
-        } catch (IndexOutOfBoundsException e) {
-            fail("Did not receive all intents");
-        }
-    }
-
-    /**
-     * Tests that the new ImsService still sends the IMS_SERVICE_DOWN broadcast when the feature is
-     * set to initializing.
-     */
-    @Test
-    @SmallTest
-    public void testImsServiceDownSentCompatInitializing() throws RemoteException {
-        mTestImsServiceBinder.createMmTelFeature(TEST_SLOT_0, mTestCallback);
-
-        mTestImsService.mSpyMmTelFeature.sendSetFeatureState(ImsFeature.STATE_INITIALIZING);
-
-        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
-        verify(mMockContext).sendBroadcast(intentCaptor.capture());
-        try {
-            // IMS_SERVICE_DOWN is sent when the service is STATE_INITIALIZING.
-            assertNotNull(intentCaptor.getValue());
-            verifyServiceDownSent(intentCaptor.getValue());
-        } catch (IndexOutOfBoundsException e) {
-            fail("Did not receive all intents");
-        }
-    }
-
-    /**
-     * Tests that the ImsService will return the correct ImsFeatureConfiguration when queried.
-     */
-    @Test
-    @SmallTest
-    public void testQuerySupportedImsFeatures() throws RemoteException {
-        ImsFeatureConfiguration config = new ImsFeatureConfiguration.Builder()
-                .addFeature(ImsFeature.FEATURE_MMTEL)
-                .addFeature(ImsFeature.FEATURE_RCS)
-                .build();
-        mTestImsService.testFeatureConfig = config;
-
-        ImsFeatureConfiguration result = mTestImsServiceBinder.querySupportedImsFeatures();
-
-        assertEquals(config, result);
-    }
-
-    private void verifyServiceDownSent(Intent testIntent) {
-        assertEquals(ImsManager.ACTION_IMS_SERVICE_DOWN, testIntent.getAction());
-        assertEquals(TEST_SLOT_0, testIntent.getIntExtra(ImsManager.EXTRA_PHONE_ID, -1));
-    }
-
-    private void verifyServiceUpSent(Intent testIntent) {
-        assertEquals(ImsManager.ACTION_IMS_SERVICE_UP, testIntent.getAction());
-        assertEquals(TEST_SLOT_0, testIntent.getIntExtra(ImsManager.EXTRA_PHONE_ID, -1));
-    }
-}
diff --git a/tests/telephonytests/src/android/telephony/ims/internal/TestImsService.java b/tests/telephonytests/src/android/telephony/ims/internal/TestImsService.java
deleted file mode 100644
index 97dfff7..0000000
--- a/tests/telephonytests/src/android/telephony/ims/internal/TestImsService.java
+++ /dev/null
@@ -1,58 +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 android.telephony.ims.internal;
-
-import static org.mockito.Mockito.spy;
-
-import android.content.Context;
-import android.telephony.ims.internal.feature.MmTelFeature;
-import android.telephony.ims.internal.feature.RcsFeature;
-import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
-
-/**
- * Test ImsService used by mockito to verify functionality.
- */
-
-public class TestImsService extends ImsService {
-
-    public TestMmTelFeature mSpyMmTelFeature;
-    public TestMmTelFeature mTestMmTelFeature;
-
-    public ImsFeatureConfiguration testFeatureConfig;
-
-    public TestImsService(Context context) {
-        attachBaseContext(context);
-        // Must create real MMTelFeature to initialize ImsFeature objects.
-        mTestMmTelFeature = new TestMmTelFeature();
-        mSpyMmTelFeature = spy(mTestMmTelFeature);
-    }
-
-    @Override
-    public ImsFeatureConfiguration querySupportedImsFeatures() {
-        return testFeatureConfig;
-    }
-
-    @Override
-    public MmTelFeature createMmTelFeature(int slotId) {
-        return mSpyMmTelFeature;
-    }
-
-    @Override
-    public RcsFeature createRcsFeature(int slotId) {
-        return null;
-    }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/AnswerToResetTest.java b/tests/telephonytests/src/com/android/internal/telephony/AnswerToResetTest.java
new file mode 100644
index 0000000..d5dc47b
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/AnswerToResetTest.java
@@ -0,0 +1,321 @@
+/*
+ * 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+
+public class AnswerToResetTest {
+
+    @Test
+    @SmallTest
+    public void tesAnswerToRestOddLength() {
+        String str = "3B02145";
+        AnswerToReset atr = AnswerToReset.parseAtr(str);
+        assertNull(atr);
+    }
+
+    @Test
+    @SmallTest
+    public void tesAnswerToRestTooShortLength() {
+        String str = "3B";
+        AnswerToReset atr = AnswerToReset.parseAtr(str);
+        assertNull(atr);
+    }
+
+    @Test
+    @SmallTest
+    public void tesAnswerToRestNoInterfaceByteNoHistoricalByte() {
+        String str = "3B00";
+        AnswerToReset atr = AnswerToReset.parseAtr(str);
+        assertNotNull(atr);
+        assertEquals(atr.getConventionByte(), (byte) 0x3B);
+        assertEquals(atr.getFormatByte(), (byte) 0x00);
+        assertTrue(atr.getInterfaceBytes().isEmpty());
+        assertEquals(atr.getHistoricalBytes().length, 0);
+        assertNull(atr.getCheckByte());
+    }
+
+    @Test
+    @SmallTest
+    public void tesAnswerToRestNoHistoricalByte() {
+        String str = "3F909580B1FE001F4297";
+        AnswerToReset atr = AnswerToReset.parseAtr(str);
+        assertNotNull(atr);
+        assertEquals(atr.getConventionByte(), (byte) 0x3F);
+        assertEquals(atr.getFormatByte(), (byte) 0x90);
+
+        assertEquals(atr.getInterfaceBytes().size(), 4);
+        AnswerToReset.InterfaceByte expect_t1 =
+                new AnswerToReset.InterfaceByte((byte) 0x95, null, null, (byte) 0x80);
+        AnswerToReset.InterfaceByte expect_t2 =
+                new AnswerToReset.InterfaceByte(null, null, null, (byte) 0xB1);
+        AnswerToReset.InterfaceByte expect_t3 =
+                new AnswerToReset.InterfaceByte((byte) 0xFE, (byte) 0x00, null, (byte) 0x1F);
+        AnswerToReset.InterfaceByte expect_t4 =
+                new AnswerToReset.InterfaceByte((byte) 0x42, null, null, null);
+        ArrayList<AnswerToReset.InterfaceByte> expect = new ArrayList<>(
+                Arrays.asList(expect_t1, expect_t2, expect_t3, expect_t4)
+        );
+        assertEquals(expect, atr.getInterfaceBytes());
+
+        assertEquals(atr.getHistoricalBytes().length, 0);
+        assertEquals(atr.getCheckByte(), Byte.valueOf((byte) 0x97));
+    }
+
+    @Test
+    @SmallTest
+    public void tesAnswerToRestNoInterfaceByte() {
+        String str = "3F078031A073BE211797";
+        AnswerToReset atr = AnswerToReset.parseAtr(str);
+        assertNotNull(atr);
+        assertEquals(atr.getConventionByte(), (byte) 0x3F);
+        assertEquals(atr.getFormatByte(), (byte) 0x07);
+        assertTrue(atr.getInterfaceBytes().isEmpty());
+        assertEquals(atr.getHistoricalBytes().length, 7);
+        byte[] expect = new byte[]{
+                (byte) 0x80, (byte) 0x31, (byte) 0xA0, (byte) 0x73,
+                (byte) 0xBE, (byte) 0x21, (byte) 0x17};
+        assertTrue(Arrays.equals(atr.getHistoricalBytes(), expect));
+        assertEquals(atr.getCheckByte(), Byte.valueOf((byte) 0x97));
+    }
+
+    @Test
+    @SmallTest
+    public void tesAnswerToRestSuccess() {
+        String str = "3F979580B1FE001F428031A073BE211797";
+        AnswerToReset atr = AnswerToReset.parseAtr(str);
+        assertNotNull(atr);
+        assertEquals(atr.getConventionByte(), (byte) 0x3F);
+        assertEquals(atr.getFormatByte(), (byte) 0x97);
+
+        assertEquals(atr.getInterfaceBytes().size(), 4);
+        AnswerToReset.InterfaceByte expect_t1 =
+                new AnswerToReset.InterfaceByte((byte) 0x95, null, null, (byte) 0x80);
+        AnswerToReset.InterfaceByte expect_t2 =
+                new AnswerToReset.InterfaceByte(null, null, null, (byte) 0xB1);
+        AnswerToReset.InterfaceByte expect_t3 =
+                new AnswerToReset.InterfaceByte((byte) 0xFE, (byte) 0x00, null, (byte) 0x1F);
+        AnswerToReset.InterfaceByte expect_t4 =
+                new AnswerToReset.InterfaceByte((byte) 0x42, null, null, null);
+        ArrayList<AnswerToReset.InterfaceByte> expect_ib = new ArrayList<>(
+                Arrays.asList(expect_t1, expect_t2, expect_t3, expect_t4)
+        );
+        assertEquals(expect_ib, atr.getInterfaceBytes());
+        assertEquals(atr.getHistoricalBytes().length, 7);
+        byte[] expect_hb = new byte[]{
+                (byte) 0x80, (byte) 0x31, (byte) 0xA0, (byte) 0x73,
+                (byte) 0xBE, (byte) 0x21, (byte) 0x17};
+        assertTrue(Arrays.equals(atr.getHistoricalBytes(), expect_hb));
+        assertEquals(atr.getCheckByte(), Byte.valueOf((byte) 0x97));
+    }
+
+    @Test
+    @SmallTest
+    public void tesAnswerToRestSuccessWithoutCheckByte() {
+        String str = "3F979580B0FE0010428031A073BE2117";
+        AnswerToReset atr = AnswerToReset.parseAtr(str);
+        assertNotNull(atr);
+        assertEquals(atr.getConventionByte(), (byte) 0x3F);
+        assertEquals(atr.getFormatByte(), (byte) 0x97);
+
+        assertEquals(atr.getInterfaceBytes().size(), 4);
+        AnswerToReset.InterfaceByte expect_t1 =
+                new AnswerToReset.InterfaceByte((byte) 0x95, null, null, (byte) 0x80);
+        AnswerToReset.InterfaceByte expect_t2 =
+                new AnswerToReset.InterfaceByte(null, null, null, (byte) 0xB0);
+        AnswerToReset.InterfaceByte expect_t3 =
+                new AnswerToReset.InterfaceByte((byte) 0xFE, (byte) 0x00, null, (byte) 0x10);
+        AnswerToReset.InterfaceByte expect_t4 =
+                new AnswerToReset.InterfaceByte((byte) 0x42, null, null, null);
+        ArrayList<AnswerToReset.InterfaceByte> expect_ib = new ArrayList<>(
+                Arrays.asList(expect_t1, expect_t2, expect_t3, expect_t4)
+        );
+        assertEquals(expect_ib, atr.getInterfaceBytes());
+
+        assertEquals(atr.getHistoricalBytes().length, 7);
+        byte[] expect_hb = new byte[]{
+                (byte) 0x80, (byte) 0x31, (byte) 0xA0, (byte) 0x73,
+                (byte) 0xBE, (byte) 0x21, (byte) 0x17};
+        assertTrue(Arrays.equals(atr.getHistoricalBytes(), expect_hb));
+
+        assertEquals(atr.getCheckByte(), null);
+        assertFalse(atr.isEuiccSupported());
+    }
+
+    @Test
+    @SmallTest
+    public void tesAnswerToRestFailWithoutCheckByte() {
+        String str = "3F979581B0FE0010428031A073BE2117";
+        AnswerToReset atr = AnswerToReset.parseAtr(str);
+        assertNull(atr);
+    }
+
+    @Test
+    @SmallTest
+    public void tesAnswerToRestFailWithExtraByte() {
+        String str = "3F979580B1FE001F428031A073BE21179718";
+        AnswerToReset atr = AnswerToReset.parseAtr(str);
+        assertNull(atr);
+    }
+
+    @Test
+    @SmallTest
+    public void tesAnswerToRestEuiccSupported() {
+        String str = "3F979580BFFE8210428031A073BE211797";
+        AnswerToReset atr = AnswerToReset.parseAtr(str);
+        assertNotNull(atr);
+        assertEquals(atr.getConventionByte(), (byte) 0x3F);
+        assertEquals(atr.getFormatByte(), (byte) 0x97);
+
+        assertEquals(atr.getInterfaceBytes().size(), 4);
+        AnswerToReset.InterfaceByte expect_t1 =
+                new AnswerToReset.InterfaceByte((byte) 0x95, null, null, (byte) 0x80);
+        AnswerToReset.InterfaceByte expect_t2 =
+                new AnswerToReset.InterfaceByte(null, null, null, (byte) 0xBF);
+        AnswerToReset.InterfaceByte expect_t3 =
+                new AnswerToReset.InterfaceByte((byte) 0xFE, (byte) 0x82, null, (byte) 0x10);
+        AnswerToReset.InterfaceByte expect_t4 =
+                new AnswerToReset.InterfaceByte((byte) 0x42, null, null, null);
+        ArrayList<AnswerToReset.InterfaceByte> expect_ib = new ArrayList<>(
+                Arrays.asList(expect_t1, expect_t2, expect_t3, expect_t4)
+        );
+        assertEquals(expect_ib, atr.getInterfaceBytes());
+
+        assertEquals(atr.getHistoricalBytes().length, 7);
+        byte[] expect_hb = new byte[]{
+                (byte) 0x80, (byte) 0x31, (byte) 0xA0, (byte) 0x73,
+                (byte) 0xBE, (byte) 0x21, (byte) 0x17};
+        assertTrue(Arrays.equals(atr.getHistoricalBytes(), expect_hb));
+
+        assertEquals(atr.getCheckByte(), Byte.valueOf((byte) 0x97));
+
+        assertTrue(atr.isEuiccSupported());
+    }
+
+    @Test
+    @SmallTest
+    public void tesAnswerToRestEuiccSupportedWithLowerCaseString() {
+        String str = "3f979580bffe8210428031a073be211797";
+        AnswerToReset atr = AnswerToReset.parseAtr(str);
+        assertNotNull(atr);
+        assertEquals(atr.getConventionByte(), (byte) 0x3F);
+        assertEquals(atr.getFormatByte(), (byte) 0x97);
+
+        assertEquals(atr.getInterfaceBytes().size(), 4);
+        AnswerToReset.InterfaceByte expect_t1 =
+                new AnswerToReset.InterfaceByte((byte) 0x95, null, null, (byte) 0x80);
+        AnswerToReset.InterfaceByte expect_t2 =
+                new AnswerToReset.InterfaceByte(null, null, null, (byte) 0xBF);
+        AnswerToReset.InterfaceByte expect_t3 =
+                new AnswerToReset.InterfaceByte((byte) 0xFE, (byte) 0x82, null, (byte) 0x10);
+        AnswerToReset.InterfaceByte expect_t4 =
+                new AnswerToReset.InterfaceByte((byte) 0x42, null, null, null);
+        ArrayList<AnswerToReset.InterfaceByte> expect_ib = new ArrayList<>(
+                Arrays.asList(expect_t1, expect_t2, expect_t3, expect_t4)
+        );
+        assertEquals(expect_ib, atr.getInterfaceBytes());
+
+        assertEquals(atr.getHistoricalBytes().length, 7);
+        byte[] expect_hb = new byte[]{
+            (byte) 0x80, (byte) 0x31, (byte) 0xA0, (byte) 0x73,
+            (byte) 0xBE, (byte) 0x21, (byte) 0x17};
+        assertTrue(Arrays.equals(atr.getHistoricalBytes(), expect_hb));
+
+        assertEquals(atr.getCheckByte(), Byte.valueOf((byte) 0x97));
+
+        assertTrue(atr.isEuiccSupported());
+    }
+
+    @Test
+    @SmallTest
+    public void tesAnswerToRestEuiccNotSupportedDueToIncorrectT() {
+        String str = "3F979580BEFE8210428031A073BE211797";
+        AnswerToReset atr = AnswerToReset.parseAtr(str);
+        assertNotNull(atr);
+        assertEquals(atr.getConventionByte(), (byte) 0x3F);
+        assertEquals(atr.getFormatByte(), (byte) 0x97);
+
+        assertEquals(atr.getInterfaceBytes().size(), 4);
+        AnswerToReset.InterfaceByte expect_t1 =
+                new AnswerToReset.InterfaceByte((byte) 0x95, null, null, (byte) 0x80);
+        AnswerToReset.InterfaceByte expect_t2 =
+                new AnswerToReset.InterfaceByte(null, null, null, (byte) 0xBE);
+        AnswerToReset.InterfaceByte expect_t3 =
+                new AnswerToReset.InterfaceByte((byte) 0xFE, (byte) 0x82, null, (byte) 0x10);
+        AnswerToReset.InterfaceByte expect_t4 =
+                new AnswerToReset.InterfaceByte((byte) 0x42, null, null, null);
+        ArrayList<AnswerToReset.InterfaceByte> expect_ib = new ArrayList<>(
+                Arrays.asList(expect_t1, expect_t2, expect_t3, expect_t4)
+        );
+        assertEquals(expect_ib, atr.getInterfaceBytes());
+
+        assertEquals(atr.getHistoricalBytes().length, 7);
+        byte[] expect_hb = new byte[]{
+                (byte) 0x80, (byte) 0x31, (byte) 0xA0, (byte) 0x73,
+                (byte) 0xBE, (byte) 0x21, (byte) 0x17};
+        assertTrue(Arrays.equals(atr.getHistoricalBytes(), expect_hb));
+
+        assertEquals(atr.getCheckByte(), Byte.valueOf((byte) 0x97));
+
+        assertFalse(atr.isEuiccSupported());
+    }
+
+    @Test
+    @SmallTest
+    public void tesAnswerToRestEuiccNotSupportedDueToIncorrectTB() {
+        String str = "3F979580BFFE8110428031A073BE211797";
+        AnswerToReset atr = AnswerToReset.parseAtr(str);
+        assertNotNull(atr);
+        assertEquals(atr.getConventionByte(), (byte) 0x3F);
+        assertEquals(atr.getFormatByte(), (byte) 0x97);
+
+        assertEquals(atr.getInterfaceBytes().size(), 4);
+        AnswerToReset.InterfaceByte expect_t1 =
+                new AnswerToReset.InterfaceByte((byte) 0x95, null, null, (byte) 0x80);
+        AnswerToReset.InterfaceByte expect_t2 =
+                new AnswerToReset.InterfaceByte(null, null, null, (byte) 0xBF);
+        AnswerToReset.InterfaceByte expect_t3 =
+                new AnswerToReset.InterfaceByte((byte) 0xFE, (byte) 0x81, null, (byte) 0x10);
+        AnswerToReset.InterfaceByte expect_t4 =
+                new AnswerToReset.InterfaceByte((byte) 0x42, null, null, null);
+        ArrayList<AnswerToReset.InterfaceByte> expect_ib = new ArrayList<>(
+                Arrays.asList(expect_t1, expect_t2, expect_t3, expect_t4)
+        );
+        assertEquals(expect_ib, atr.getInterfaceBytes());
+
+        assertEquals(atr.getHistoricalBytes().length, 7);
+        byte[] expect_hb = new byte[]{
+                (byte) 0x80, (byte) 0x31, (byte) 0xA0, (byte) 0x73,
+                (byte) 0xBE, (byte) 0x21, (byte) 0x17};
+        assertTrue(Arrays.equals(atr.getHistoricalBytes(), expect_hb));
+
+        assertEquals(atr.getCheckByte(), Byte.valueOf((byte) 0x97));
+
+        assertFalse(atr.isEuiccSupported());
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkServiceTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkServiceTest.java
new file mode 100644
index 0000000..16bc535
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkServiceTest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2016 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.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.IntentFilter;
+import android.content.pm.ServiceInfo;
+import android.os.RemoteException;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.INetworkService;
+import android.telephony.INetworkServiceCallback;
+import android.telephony.NetworkRegistrationState;
+import android.telephony.NetworkService;
+import android.telephony.NetworkServiceCallback;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import com.android.internal.R;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+public class CellularNetworkServiceTest extends TelephonyTest {
+    CellularNetworkService mCellularNetworkService;
+
+    @Mock
+    private INetworkServiceCallback mCallback;
+
+    private void addNetworkService() {
+        mCellularNetworkService = new CellularNetworkService();
+        ServiceInfo serviceInfo =  new ServiceInfo();
+        serviceInfo.packageName = "com.android.phone";
+        serviceInfo.permission = "android.permission.BIND_NETWORK_SERVICE";
+        IntentFilter filter = new IntentFilter();
+        mContextFixture.addService(
+                NetworkService.NETWORK_SERVICE_INTERFACE,
+                null,
+                "com.android.phone",
+                mCellularNetworkService.mBinder,
+                serviceInfo,
+                filter);
+    }
+    INetworkService.Stub mBinder;
+
+    @Before
+    public void setUp() throws Exception {
+
+        logd("CellularNetworkServiceTest +Setup!");
+        super.setUp("CellularNetworkServiceTest");
+
+        mContextFixture.putResource(R.string.config_wwan_network_service_package,
+                "com.android.phone");
+        addNetworkService();
+        mBinder = mCellularNetworkService.mBinder;
+        mBinder.createNetworkServiceProvider(0);
+
+        int dds = SubscriptionManager.getDefaultDataSubscriptionId();
+        doReturn(dds).when(mPhone).getSubId();
+
+        logd("CellularNetworkServiceTest -Setup!");
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    @Test
+    @MediumTest
+    public void testGetNetworkRegistrationState() {
+        int voiceRegState = NetworkRegistrationState.REG_STATE_HOME;
+        int dataRegState = NetworkRegistrationState.REG_STATE_HOME;
+        int voiceRadioTech = ServiceState.RIL_RADIO_TECHNOLOGY_HSPA;
+        int dataRadioTech = ServiceState.RIL_RADIO_TECHNOLOGY_HSPA;
+        int domain = NetworkRegistrationState.DOMAIN_CS;
+
+        boolean cssSupported = true;
+        int roamingIndicator = 1;
+        int systemIsInPrl = 2;
+        int defaultRoamingIndicator = 3;
+        int reasonForDenial = 0;
+        int maxDataCalls = 4;
+        int[] availableServices = new int[] {
+                NetworkRegistrationState.SERVICE_TYPE_VOICE,
+                NetworkRegistrationState.SERVICE_TYPE_SMS,
+                NetworkRegistrationState.SERVICE_TYPE_VIDEO
+        };
+
+        mSimulatedCommands.setVoiceRegState(voiceRegState);
+        mSimulatedCommands.setVoiceRadioTech(voiceRadioTech);
+        mSimulatedCommands.setDataRegState(dataRegState);
+        mSimulatedCommands.setDataRadioTech(dataRadioTech);
+        mSimulatedCommands.mCssSupported = cssSupported;
+        mSimulatedCommands.mRoamingIndicator = roamingIndicator;
+        mSimulatedCommands.mSystemIsInPrl = systemIsInPrl;
+        mSimulatedCommands.mDefaultRoamingIndicator = defaultRoamingIndicator;
+        mSimulatedCommands.mReasonForDenial = reasonForDenial;
+        mSimulatedCommands.mMaxDataCalls = maxDataCalls;
+
+        mSimulatedCommands.notifyNetworkStateChanged();
+
+        try {
+            mBinder.getNetworkRegistrationState(0, domain, mCallback);
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+
+        waitForMs(1000);
+
+        NetworkRegistrationState expectedState = new NetworkRegistrationState(
+                AccessNetworkConstants.TransportType.WWAN, domain, voiceRegState,
+                ServiceState.rilRadioTechnologyToNetworkType(voiceRadioTech), reasonForDenial,
+                false, availableServices, null, cssSupported,
+                roamingIndicator, systemIsInPrl, defaultRoamingIndicator);
+
+        try {
+            verify(mCallback, times(1)).onGetNetworkRegistrationStateComplete(
+                    eq(NetworkServiceCallback.RESULT_SUCCESS), eq(expectedState));
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+
+        domain = NetworkRegistrationState.DOMAIN_PS;
+        availableServices = new int[] {NetworkRegistrationState.SERVICE_TYPE_DATA};
+        try {
+            mBinder.getNetworkRegistrationState(0, domain, mCallback);
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+
+        waitForMs(1000);
+
+        expectedState = new NetworkRegistrationState(
+                AccessNetworkConstants.TransportType.WWAN, domain, voiceRegState,
+                ServiceState.rilRadioTechnologyToNetworkType(voiceRadioTech), reasonForDenial,
+                false, availableServices, null, maxDataCalls);
+
+        try {
+            verify(mCallback, times(1)).onGetNetworkRegistrationStateComplete(
+                    eq(NetworkServiceCallback.RESULT_SUCCESS), eq(expectedState));
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ExponentialBackoffTest.java b/tests/telephonytests/src/com/android/internal/telephony/ExponentialBackoffTest.java
index 87347e5..f9a883a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ExponentialBackoffTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ExponentialBackoffTest.java
@@ -17,8 +17,10 @@
 package com.android.internal.telephony;
 
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.os.Handler;
@@ -31,7 +33,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-
+import org.mockito.Mock;
 @RunWith(AndroidJUnit4.class)
 public class ExponentialBackoffTest extends ImsTestBase {
 
@@ -40,20 +42,23 @@
     private static final int MULTIPLIER = 2;
 
     private ExponentialBackoff mBackoffUnderTest;
-    private Handler mHandler = spy(new Handler(Looper.getMainLooper()));
-    private Runnable mRunnable = spy(new MyRunnable());
-
-    public class MyRunnable implements Runnable {
-        @Override
-        public void run() {
-            // do nothing
-        }
-    }
+    private Handler mHandler = new Handler(Looper.getMainLooper());
+    @Mock private Runnable mRunnable;
+    @Mock private ExponentialBackoff.HandlerAdapter mHandlerAdapter;
 
     @Before
-    public void setUp() {
+    public void setUp() throws Exception {
+        super.setUp();
         mBackoffUnderTest = new ExponentialBackoff(
                 START_DELAY_MS, MAXIMUM_DELAY_MS, MULTIPLIER, mHandler, mRunnable);
+        mBackoffUnderTest.setHandlerAdapter(mHandlerAdapter);
+        doAnswer(invocation -> mHandler.postDelayed((Runnable) invocation.getArguments()[0],
+                        (long) invocation.getArguments()[1])
+        ).when(mHandlerAdapter).postDelayed(any(Runnable.class), anyLong());
+        doAnswer(invocation -> {
+            mHandler.removeCallbacks((Runnable) invocation.getArguments()[0]);
+            return null;
+        }).when(mHandlerAdapter).removeCallbacks(any(Runnable.class));
     }
 
     @After
@@ -74,10 +79,10 @@
     @Test
     public void testStopBackoff() {
         mBackoffUnderTest.start();
-        reset(mHandler);
 
         mBackoffUnderTest.stop();
-        verify(mHandler).removeCallbacks(mRunnable);
+        // removeCallbacks is called during start() and stop()
+        verify(mHandlerAdapter, times(2)).removeCallbacks(mRunnable);
     }
 
     @Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineTest.java b/tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineTest.java
index 55add03..a8bf3bd 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineTest.java
@@ -28,6 +28,7 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
+import com.android.internal.telephony.TimeZoneLookupHelper.OffsetResult;
 import com.android.internal.telephony.util.TimeStampedValue;
 
 import org.junit.After;
@@ -51,6 +52,9 @@
     @Mock
     private TimeZoneLookupHelper mTimeZoneLookupHelper;
 
+    @Mock
+    private TimeServiceHelper mTimeServiceHelper;
+
     private NitzStateMachine mNitzStateMachine;
 
     @Before
@@ -62,8 +66,8 @@
         when(mDeviceState.getIgnoreNitz()).thenReturn(false);
         when(mDeviceState.getNitzUpdateDiffMillis()).thenReturn(2000);
         when(mDeviceState.getNitzUpdateSpacingMillis()).thenReturn(1000 * 60 * 10);
-        when(mDeviceState.elapsedRealtime()).thenReturn(123456789L);
-        when(mDeviceState.currentTimeMillis()).thenReturn(987654321L);
+        when(mTimeServiceHelper.elapsedRealtime()).thenReturn(123456789L);
+        when(mTimeServiceHelper.currentTimeMillis()).thenReturn(987654321L);
 
         mNitzStateMachine = new NitzStateMachine(
                 mPhone, mTimeServiceHelper, mDeviceState, mTimeZoneLookupHelper);
@@ -78,8 +82,6 @@
         verify(mDeviceState, atLeast(0)).getIgnoreNitz();
         verify(mDeviceState, atLeast(0)).getNitzUpdateDiffMillis();
         verify(mDeviceState, atLeast(0)).getNitzUpdateSpacingMillis();
-        verify(mDeviceState, atLeast(0)).elapsedRealtime();
-        verify(mDeviceState, atLeast(0)).currentTimeMillis();
         verify(mDeviceState, atLeast(0)).getNetworkCountryIsoForPhone();
         verifyNoMoreInteractions(mDeviceState);
 
@@ -90,6 +92,8 @@
         verify(mTimeServiceHelper, atLeast(0)).isTimeDetectionEnabled();
         verify(mTimeServiceHelper, atLeast(0)).isTimeZoneDetectionEnabled();
         verify(mTimeServiceHelper, atLeast(0)).isTimeZoneSettingInitialized();
+        verify(mTimeServiceHelper, atLeast(0)).elapsedRealtime();
+        verify(mTimeServiceHelper, atLeast(0)).currentTimeMillis();
         verifyNoMoreInteractions(mTimeServiceHelper);
 
         super.tearDown();
@@ -110,9 +114,9 @@
         TestNitzSignal testNitzSignal = createTestNitzSignal();
 
         // Configure expected time zone lookup and the result.
-        String testTimeZoneId = US_TIME_ZONE_ID;
-        when(mTimeZoneLookupHelper.guessZoneIdByNitzCountry(
-                testNitzSignal.getNitzData(), US_ISO_CODE)).thenReturn(testTimeZoneId);
+        OffsetResult testLookupResult = new OffsetResult(US_TIME_ZONE_ID, true /* oneMatch */);
+        when(mTimeZoneLookupHelper.lookupByNitzCountry(
+                testNitzSignal.getNitzData(), US_ISO_CODE)).thenReturn(testLookupResult);
 
         // Simulate the elapsedRealtime() value incrementing with the passage of time.
         incrementSimulatedDeviceClock(1000);
@@ -122,15 +126,15 @@
 
         // Check resulting state and side effects.
         long expectedAdjustedCurrentTimeMillis =
-                testNitzSignal.getAdjustedCurrentTimeMillis(mDeviceState.elapsedRealtime());
+                testNitzSignal.getAdjustedCurrentTimeMillis(mTimeServiceHelper.elapsedRealtime());
 
-        verifyTimeServiceTimeZoneWasSet(testTimeZoneId);
+        verifyTimeServiceTimeZoneWasSet(testLookupResult.zoneId);
         verifyTimeServiceTimeWasSet(expectedAdjustedCurrentTimeMillis);
 
 
         assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
         assertEquals(testNitzSignal.getNitzData(), mNitzStateMachine.getCachedNitzData());
-        assertEquals(testTimeZoneId, mNitzStateMachine.getSavedTimeZoneId());
+        assertEquals(testLookupResult.zoneId, mNitzStateMachine.getSavedTimeZoneId());
     }
 
     @Test
@@ -149,9 +153,9 @@
         TestNitzSignal testNitzSignal = createTestNitzSignal();
 
         // Configure expected time zone lookup and the result.
-        String testTimeZoneId = US_TIME_ZONE_ID;
-        when(mTimeZoneLookupHelper.guessZoneIdByNitzCountry(
-                testNitzSignal.getNitzData(), US_ISO_CODE)).thenReturn(testTimeZoneId);
+        OffsetResult testLookupResult = new OffsetResult(US_TIME_ZONE_ID, true /* oneMatch */);
+        when(mTimeZoneLookupHelper.lookupByNitzCountry(
+                testNitzSignal.getNitzData(), US_ISO_CODE)).thenReturn(testLookupResult);
 
         // Simulate the elapsedRealtime() value incrementing with the passage of time.
         incrementSimulatedDeviceClock(1000);
@@ -161,14 +165,14 @@
 
         // Check resulting state and side effects.
         long expectedAdjustedCurrentTimeMillis =
-                testNitzSignal.getAdjustedCurrentTimeMillis(mDeviceState.elapsedRealtime());
+                testNitzSignal.getAdjustedCurrentTimeMillis(mTimeServiceHelper.elapsedRealtime());
 
         verifyTimeServiceTimeZoneWasNotSet();
         verifyTimeServiceTimeWasSet(expectedAdjustedCurrentTimeMillis);
 
         assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
         assertEquals(testNitzSignal.getNitzData(), mNitzStateMachine.getCachedNitzData());
-        assertEquals(testTimeZoneId, mNitzStateMachine.getSavedTimeZoneId());
+        assertEquals(testLookupResult.zoneId, mNitzStateMachine.getSavedTimeZoneId());
     }
 
     @Test
@@ -186,9 +190,9 @@
         TestNitzSignal testNitzSignal = createTestNitzSignal();
 
         // Configure expected time zone lookup and the result.
-        String testTimeZoneId = US_TIME_ZONE_ID;
-        when(mTimeZoneLookupHelper.guessZoneIdByNitzCountry(
-                testNitzSignal.getNitzData(), US_ISO_CODE)).thenReturn(testTimeZoneId);
+        OffsetResult testLookupResult = new OffsetResult(US_TIME_ZONE_ID, true /* oneMatch */);
+        when(mTimeZoneLookupHelper.lookupByNitzCountry(
+                testNitzSignal.getNitzData(), US_ISO_CODE)).thenReturn(testLookupResult);
 
         // Simulate the elapsedRealtime() value incrementing with the passage of time.
         incrementSimulatedDeviceClock(1000);
@@ -197,12 +201,12 @@
         mNitzStateMachine.handleNitzReceived(testNitzSignal.asTimeStampedValue());
 
         // Check resulting state and side effects.
-        verifyTimeServiceTimeZoneWasSet(testTimeZoneId);
+        verifyTimeServiceTimeZoneWasSet(testLookupResult.zoneId);
         verifyTimeServiceTimeWasNotSet();
 
         assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
         assertEquals(testNitzSignal.getNitzData(), mNitzStateMachine.getCachedNitzData());
-        assertEquals(testTimeZoneId, mNitzStateMachine.getSavedTimeZoneId());
+        assertEquals(testLookupResult.zoneId, mNitzStateMachine.getSavedTimeZoneId());
     }
 
     @Test
@@ -221,9 +225,9 @@
         TestNitzSignal testNitzSignal = createTestNitzSignal();
 
         // Configure expected time zone lookup and the result.
-        String testTimeZoneId = US_TIME_ZONE_ID;
-        when(mTimeZoneLookupHelper.guessZoneIdByNitzCountry(
-                testNitzSignal.getNitzData(), US_ISO_CODE)).thenReturn(testTimeZoneId);
+        OffsetResult testLookupResult = new OffsetResult(US_TIME_ZONE_ID, true /* oneMatch */);
+        when(mTimeZoneLookupHelper.lookupByNitzCountry(
+                testNitzSignal.getNitzData(), US_ISO_CODE)).thenReturn(testLookupResult);
 
         // Simulate the elapsedRealtime() value incrementing with the passage of time.
         incrementSimulatedDeviceClock(1000);
@@ -237,7 +241,7 @@
 
         assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
         assertEquals(testNitzSignal.getNitzData(), mNitzStateMachine.getCachedNitzData());
-        assertEquals(testTimeZoneId, mNitzStateMachine.getSavedTimeZoneId());
+        assertEquals(testLookupResult.zoneId, mNitzStateMachine.getSavedTimeZoneId());
     }
 
     @Test
@@ -271,22 +275,22 @@
         //
 
         // Configure expected time zone lookup and the result.
-        String testTimeZoneId = US_TIME_ZONE_ID;
-        when(mTimeZoneLookupHelper.guessZoneIdByNitzCountry(
-                testNitzSignal.getNitzData(), US_ISO_CODE)).thenReturn(testTimeZoneId);
+        OffsetResult testLookupResult = new OffsetResult(US_TIME_ZONE_ID, true /* oneMatch */);
+        when(mTimeZoneLookupHelper.lookupByNitzCountry(
+                testNitzSignal.getNitzData(), US_ISO_CODE)).thenReturn(testLookupResult);
 
         // Simulate the country being known.
         when(mDeviceState.getNetworkCountryIsoForPhone()).thenReturn(US_ISO_CODE);
         mNitzStateMachine.handleNetworkCountryCodeSet(true);
 
         // Check resulting state and side effects.
-        verifyTimeServiceTimeZoneWasSet(testTimeZoneId);
+        verifyTimeServiceTimeZoneWasSet(testLookupResult.zoneId);
 
         // TODO(nfuller): The following line should probably be assertTrue but the logic under test
         // is probably buggy. Fix.
         assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
         assertEquals(testNitzSignal.getNitzData(), mNitzStateMachine.getCachedNitzData());
-        assertEquals(testTimeZoneId, mNitzStateMachine.getSavedTimeZoneId());
+        assertEquals(testLookupResult.zoneId, mNitzStateMachine.getSavedTimeZoneId());
     }
 
     private void verifyTimeServiceTimeZoneWasNotSet() {
@@ -308,10 +312,10 @@
     }
 
     private void incrementSimulatedDeviceClock(int incMillis) {
-        long currentElapsedRealtime = mDeviceState.elapsedRealtime();
-        when(mDeviceState.elapsedRealtime()).thenReturn(currentElapsedRealtime + incMillis);
-        long currentTimeMillis = mDeviceState.currentTimeMillis();
-        when(mDeviceState.elapsedRealtime()).thenReturn(currentTimeMillis + incMillis);
+        long currentElapsedRealtime = mTimeServiceHelper.elapsedRealtime();
+        when(mTimeServiceHelper.elapsedRealtime()).thenReturn(currentElapsedRealtime + incMillis);
+        long currentTimeMillis = mTimeServiceHelper.currentTimeMillis();
+        when(mTimeServiceHelper.elapsedRealtime()).thenReturn(currentTimeMillis + incMillis);
     }
 
     private static long createTime(TimeZone timeZone, int year, int monthOfYear, int dayOfMonth,
@@ -327,7 +331,7 @@
      * receivedRealtimeMillis from the current {@code mDeviceState.elapsedRealtime()}.
      */
     private TestNitzSignal createTestNitzSignal() {
-        long receivedRealtimeMillis = mDeviceState.elapsedRealtime();
+        long receivedRealtimeMillis = mTimeServiceHelper.elapsedRealtime();
         // Create an arbitrary time.
         long timeMillis = createTime(TimeZone.getTimeZone("UTC"), 2017, 1, 2, 12, 45, 25);
         // Create arbitrary NITZ data.
diff --git a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
index d877ac4..6118b62 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
@@ -81,7 +81,6 @@
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
 
-import static org.junit.Assert.fail;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doReturn;
@@ -101,11 +100,8 @@
 import android.hardware.radio.V1_0.RadioError;
 import android.hardware.radio.V1_0.RadioResponseInfo;
 import android.hardware.radio.V1_0.RadioResponseType;
-import android.hardware.radio.V1_0.SetupDataCallResult;
 import android.hardware.radio.V1_0.SmsWriteArgs;
 import android.net.ConnectivityManager;
-import android.net.LinkAddress;
-import android.net.NetworkUtils;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IPowerManager;
@@ -128,7 +124,6 @@
 import android.telephony.CellSignalStrengthWcdma;
 import android.telephony.SmsManager;
 import android.telephony.TelephonyManager;
-import android.telephony.data.DataCallResponse;
 import android.telephony.data.DataProfile;
 
 import com.android.internal.telephony.RIL.RilHandler;
@@ -142,10 +137,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
 import java.util.ArrayList;
-import java.util.Arrays;
 
 public class RILTest extends TelephonyTest {
 
@@ -1023,18 +1015,6 @@
         assertFalse(ril.getWakeLock(RIL.FOR_WAKELOCK).isHeld());
     }
 
-    private static Object invokeMethod(
-            Object instance, String methodName, Class<?>[] parameterClasses, Object[] parameters) {
-        try {
-            Method method = instance.getClass().getDeclaredMethod(methodName, parameterClasses);
-            method.setAccessible(true);
-            return method.invoke(instance, parameters);
-        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
-            fail(instance.getClass() + " " + methodName + " " + e.getClass().getName());
-        }
-        return null;
-    }
-
     private static RadioResponseInfo createFakeRadioResponseInfo(int serial, int error, int type) {
         RadioResponseInfo respInfo = new RadioResponseInfo();
         respInfo.serial = serial;
@@ -1557,62 +1537,4 @@
 
         return RIL.convertHalCellInfoList_1_2(records);
     }
-
-    @Test
-    public void testConvertDataCallResult() throws Exception {
-
-        SetupDataCallResult result = new SetupDataCallResult();
-        result.status = 0;
-        result.suggestedRetryTime = -1;
-        result.cid = 1;
-        result.active = 1;
-        result.type = "IP";
-        result.ifname = "eth0";
-        result.addresses = "10.0.2.15";
-        result.dnses = "10.0.2.3";
-        result.gateways = "10.0.2.15 fe80::2";
-        result.pcscf = "";
-        result.mtu = 1500;
-
-        DataCallResponse response = new DataCallResponse(0, -1, 1, 1, "IP",
-                "eth0",
-                Arrays.asList(new LinkAddress(NetworkUtils.numericToInetAddress("10.0.2.15"), 32)),
-                Arrays.asList(NetworkUtils.numericToInetAddress("10.0.2.3")),
-                Arrays.asList(NetworkUtils.numericToInetAddress("10.0.2.15"),
-                        NetworkUtils.numericToInetAddress("fe80::2")),
-                Arrays.asList(""),
-                1500);
-
-        assertEquals(response, invokeMethod(mRILInstance, "convertDataCallResult",
-                new Class<?>[] {SetupDataCallResult.class},
-                new Object[] {result}));
-
-
-        result.status = 0;
-        result.suggestedRetryTime = -1;
-        result.cid = 0;
-        result.active = 2;
-        result.type = "IPV4V6";
-        result.ifname = "ifname";
-        result.addresses = "2607:fb90:a620:651d:eabe:f8da:c107:44be/64";
-        result.dnses = "fd00:976a::9      fd00:976a::10";
-        result.gateways = "fe80::4c61:1832:7b28:d36c    1.2.3.4";
-        result.pcscf = "fd00:976a:c206:20::6   fd00:976a:c206:20::9    fd00:976a:c202:1d::9";
-        result.mtu = 1500;
-
-        response = new DataCallResponse(0, -1, 0, 2, "IPV4V6",
-                "ifname",
-                Arrays.asList(new LinkAddress("2607:fb90:a620:651d:eabe:f8da:c107:44be/64")),
-                Arrays.asList(NetworkUtils.numericToInetAddress("fd00:976a::9"),
-                        NetworkUtils.numericToInetAddress("fd00:976a::10")),
-                Arrays.asList(NetworkUtils.numericToInetAddress("fe80::4c61:1832:7b28:d36c"),
-                        NetworkUtils.numericToInetAddress("1.2.3.4")),
-                Arrays.asList("fd00:976a:c206:20::6", "fd00:976a:c206:20::9",
-                        "fd00:976a:c202:1d::9"),
-                1500);
-
-        assertEquals(response, invokeMethod(mRILInstance, "convertDataCallResult",
-                new Class<?>[] {SetupDataCallResult.class},
-                new Object[] {result}));
-    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index 5c1c900..d14e1a9 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -34,9 +34,8 @@
 
 import android.app.IAlarmManager;
 import android.content.Intent;
-import android.hardware.radio.V1_0.CellIdentityGsm;
-import android.hardware.radio.V1_0.CellInfoType;
-import android.hardware.radio.V1_0.VoiceRegStateResult;
+import android.content.IntentFilter;
+import android.content.pm.ServiceInfo;
 import android.os.AsyncResult;
 import android.os.Bundle;
 import android.os.Handler;
@@ -50,8 +49,11 @@
 import android.os.WorkSource;
 import android.support.test.filters.FlakyTest;
 import android.telephony.CarrierConfigManager;
+import android.telephony.CellIdentityGsm;
 import android.telephony.CellInfo;
 import android.telephony.CellInfoGsm;
+import android.telephony.NetworkRegistrationState;
+import android.telephony.NetworkService;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionManager;
@@ -60,6 +62,7 @@
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Pair;
 
+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;
@@ -88,6 +91,8 @@
     @Mock
     protected IAlarmManager mAlarmManager;
 
+    CellularNetworkService mCellularNetworkService;
+
     private ServiceStateTracker sst;
     private ServiceStateTrackerTestHandler mSSTTestHandler;
     private PersistableBundle mBundle;
@@ -117,12 +122,31 @@
         }
     }
 
+    private void addNetworkService() {
+        mCellularNetworkService = new CellularNetworkService();
+        ServiceInfo serviceInfo =  new ServiceInfo();
+        serviceInfo.packageName = "com.android.phone";
+        serviceInfo.permission = "android.permission.BIND_NETWORK_SERVICE";
+        IntentFilter filter = new IntentFilter();
+        mContextFixture.addService(
+                NetworkService.NETWORK_SERVICE_INTERFACE,
+                null,
+                "com.android.phone",
+                mCellularNetworkService.mBinder,
+                serviceInfo,
+                filter);
+    }
+
     @Before
     public void setUp() throws Exception {
 
         logd("ServiceStateTrackerTest +Setup!");
         super.setUp("ServiceStateTrackerTest");
 
+        mContextFixture.putResource(R.string.config_wwan_network_service_package,
+                "com.android.phone");
+        addNetworkService();
+
         doReturn(true).when(mDct).isDisconnected();
         mPhone.mDcTracker = mDct;
 
@@ -134,9 +158,9 @@
         mBundle.putStringArray(
                 CarrierConfigManager.KEY_NON_ROAMING_OPERATOR_STRING_ARRAY, new String[]{"123456"});
 
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_HOME);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_HOME);
         mSimulatedCommands.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_HSPA);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_HOME);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_HOME);
         mSimulatedCommands.setDataRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_HSPA);
 
         int dds = SubscriptionManager.getDefaultDataSubscriptionId();
@@ -406,12 +430,9 @@
     @Test
     @MediumTest
     public void testGsmCellLocation() {
-
-        VoiceRegStateResult result = new VoiceRegStateResult();
-        result.cellIdentity.cellInfoType = CellInfoType.GSM;
-        result.cellIdentity.cellIdentityGsm.add(new CellIdentityGsm());
-        result.cellIdentity.cellIdentityGsm.get(0).lac = 2;
-        result.cellIdentity.cellIdentityGsm.get(0).cid = 3;
+        CellIdentityGsm cellIdentityGsm = new CellIdentityGsm(0, 0, 2, 3);
+        NetworkRegistrationState result = new NetworkRegistrationState(
+                0, 0, 0, 0, 0, false, null, cellIdentityGsm);
 
         sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_GET_LOC_DONE,
                 new AsyncResult(null, result, null)));
@@ -474,8 +495,8 @@
 
         // Enable roaming and trigger events to notify handler registered
         doReturn(true).when(mPhone).isPhoneTypeGsm();
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(200);
@@ -486,8 +507,8 @@
         assertEquals(EVENT_DATA_ROAMING_ON, messageArgumentCaptor.getValue().what);
 
         // Disable roaming
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_HOME);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_HOME);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_HOME);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_HOME);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
@@ -496,8 +517,8 @@
         sst.unregisterForVoiceRoamingOn(mTestHandler);
 
         // Enable roaming
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(200);
@@ -511,8 +532,8 @@
     public void testRegAndUnregForVoiceRoamingOff() throws Exception {
         // Enable roaming
         doReturn(true).when(mPhone).isPhoneTypeGsm();
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
@@ -521,8 +542,8 @@
 
         // Disable roaming
         doReturn(true).when(mPhone).isPhoneTypeGsm();
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_HOME);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_HOME);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_HOME);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_HOME);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(200);
@@ -533,8 +554,8 @@
         assertEquals(EVENT_DATA_ROAMING_OFF, messageArgumentCaptor.getValue().what);
 
         // Enable roaming
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
@@ -543,8 +564,8 @@
         sst.unregisterForVoiceRoamingOff(mTestHandler);
 
         // Disable roaming
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_HOME);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_HOME);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_HOME);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_HOME);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
@@ -560,8 +581,8 @@
 
         // Enable roaming and trigger events to notify handler registered
         doReturn(true).when(mPhone).isPhoneTypeGsm();
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(200);
@@ -572,8 +593,8 @@
         assertEquals(EVENT_DATA_ROAMING_ON, messageArgumentCaptor.getValue().what);
 
         // Disable roaming
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_HOME);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_HOME);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_HOME);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_HOME);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
@@ -582,8 +603,8 @@
         sst.unregisterForDataRoamingOn(mTestHandler);
 
         // Enable roaming
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(200);
@@ -597,8 +618,8 @@
     public void testRegAndUnregForDataRoamingOff() throws Exception {
         // Enable roaming
         doReturn(true).when(mPhone).isPhoneTypeGsm();
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
@@ -607,8 +628,8 @@
 
         // Disable roaming
         doReturn(true).when(mPhone).isPhoneTypeGsm();
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_HOME);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_HOME);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_HOME);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_HOME);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
@@ -619,8 +640,8 @@
         assertEquals(EVENT_DATA_ROAMING_OFF, messageArgumentCaptor.getValue().what);
 
         // Enable roaming
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
@@ -629,8 +650,8 @@
         sst.unregisterForDataRoamingOff(mTestHandler);
 
         // Disable roaming
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_HOME);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_HOME);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_HOME);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_HOME);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
@@ -653,8 +674,8 @@
         sst.registerForDataConnectionAttached(mTestHandler, EVENT_DATA_CONNECTION_ATTACHED, null);
 
         // set service state in service and trigger events to post message on handler
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(200);
@@ -675,8 +696,8 @@
         sst.unregisterForDataConnectionAttached(mTestHandler);
 
         // set service state in service
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
@@ -690,8 +711,8 @@
     public void testRegAndUnregForDataConnAttach() throws Exception {
         // Initially set service state out of service
         doReturn(true).when(mPhone).isPhoneTypeGsm();
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_UNKNOWN);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_UNKNOWN);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
@@ -699,8 +720,8 @@
         sst.registerForDataConnectionAttached(mTestHandler, EVENT_DATA_CONNECTION_ATTACHED, null);
 
         // set service state in service and trigger events to post message on handler
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(200);
@@ -711,8 +732,8 @@
         assertEquals(EVENT_DATA_CONNECTION_ATTACHED, messageArgumentCaptor.getValue().what);
 
         // set service state out of service
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_UNKNOWN);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_UNKNOWN);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
@@ -721,8 +742,8 @@
         sst.unregisterForDataConnectionAttached(mTestHandler);
 
         // set service state in service
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
@@ -736,15 +757,15 @@
     public void testRegAndUnregForDataConnDetach() throws Exception {
         // Initially set service state in service
         doReturn(true).when(mPhone).isPhoneTypeGsm();
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         sst.registerForDataConnectionDetached(mTestHandler, EVENT_DATA_CONNECTION_DETACHED, null);
 
         // set service state out of service and trigger events to post message on handler
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_UNKNOWN);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_UNKNOWN);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(200);
@@ -755,8 +776,8 @@
         assertEquals(EVENT_DATA_CONNECTION_DETACHED, messageArgumentCaptor.getValue().what);
 
         // set service state in service
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
@@ -765,8 +786,8 @@
         sst.unregisterForDataConnectionDetached(mTestHandler);
 
         // set service state out of service
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_UNKNOWN);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_UNKNOWN);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
@@ -778,7 +799,7 @@
     @Test
     @MediumTest
     public void testRegisterForDataRegStateOrRatChange() {
-        int drs = sst.mSS.RIL_REG_STATE_HOME;
+        int drs = NetworkRegistrationState.REG_STATE_HOME;
         int rat = sst.mSS.RIL_RADIO_TECHNOLOGY_LTE;
         sst.mSS.setRilDataRadioTechnology(rat);
         sst.mSS.setDataRegState(drs);
@@ -799,8 +820,8 @@
     public void testRegAndUnregForNetworkAttached() throws Exception {
         // Initially set service state out of service
         doReturn(true).when(mPhone).isPhoneTypeGsm();
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_UNKNOWN);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_UNKNOWN);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
@@ -808,8 +829,8 @@
         sst.registerForNetworkAttached(mTestHandler, EVENT_REGISTERED_TO_NETWORK, null);
 
         // set service state in service and trigger events to post message on handler
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
@@ -820,8 +841,8 @@
         assertEquals(EVENT_REGISTERED_TO_NETWORK, messageArgumentCaptor.getValue().what);
 
         // set service state out of service
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_UNKNOWN);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_UNKNOWN);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
@@ -830,8 +851,8 @@
         sst.unregisterForNetworkAttached(mTestHandler);
 
         // set service state in service
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
@@ -854,8 +875,8 @@
         sst.registerForNetworkAttached(mTestHandler, EVENT_REGISTERED_TO_NETWORK, null);
 
         // set service state in service and trigger events to post message on handler
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
@@ -881,8 +902,8 @@
         sst.registerForNetworkAttached(mTestHandler, EVENT_REGISTERED_TO_NETWORK, null);
 
         // set service state in service
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
@@ -1021,8 +1042,8 @@
         // Enable roaming
         doReturn(true).when(mPhone).isPhoneTypeGsm();
 
-        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+        mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
         mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(200);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index 443e427..0f75a94 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -47,13 +47,13 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.euicc.EuiccManager;
+import android.telephony.ims.ImsCallProfile;
 import android.test.mock.MockContentProvider;
 import android.test.mock.MockContentResolver;
 import android.util.Log;
 import android.util.Singleton;
 
 import com.android.ims.ImsCall;
-import com.android.ims.ImsCallProfile;
 import com.android.ims.ImsEcbm;
 import com.android.ims.ImsManager;
 import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
@@ -81,6 +81,8 @@
 import org.mockito.stubbing.Answer;
 
 import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -131,8 +133,6 @@
     @Mock
     protected ImsCall mImsCall;
     @Mock
-    protected ImsCallProfile mImsCallProfile;
-    @Mock
     protected ImsEcbm mImsEcbm;
     @Mock
     protected SubscriptionController mSubscriptionController;
@@ -195,10 +195,9 @@
     @Mock
     protected NitzStateMachine mNitzStateMachine;
     @Mock
-    protected TimeServiceHelper mTimeServiceHelper;
-    @Mock
     protected RadioConfig mMockRadioConfig;
 
+    protected ImsCallProfile mImsCallProfile;
     protected TelephonyManager mTelephonyManager;
     protected SubscriptionManager mSubscriptionManager;
     protected EuiccManager mEuiccManager;
@@ -310,6 +309,7 @@
         MockitoAnnotations.initMocks(this);
 
         mPhones = new Phone[] {mPhone};
+        mImsCallProfile = new ImsCallProfile();
         mSimulatedCommands = new SimulatedCommands();
         mContextFixture = new ContextFixture();
         mContext = mContextFixture.getTestDouble();
@@ -372,8 +372,6 @@
                 .makeDeviceStateMonitor(nullable(Phone.class));
         doReturn(mNitzStateMachine).when(mTelephonyComponentFactory)
                 .makeNitzStateMachine(nullable(GsmCdmaPhone.class));
-        doReturn(mTimeServiceHelper).when(mTelephonyComponentFactory)
-                .makeTimeServiceHelper(nullable(Context.class));
 
         //mPhone
         doReturn(mContext).when(mPhone).getContext();
@@ -449,7 +447,7 @@
                 getRilDataRadioTechnology();
         doReturn(mPhone).when(mCT).getPhone();
         mImsManagerInstances.put(mPhone.getPhoneId(), mImsManager);
-        doReturn(mImsEcbm).when(mImsManager).getEcbmInterface(anyInt());
+        doReturn(mImsEcbm).when(mImsManager).getEcbmInterface();
         doReturn(mPhone).when(mInboundSmsHandler).getPhone();
         doReturn(mImsCallProfile).when(mImsCall).getCallProfile();
         doReturn(mIBinder).when(mIIntentSender).asBinder();
@@ -567,4 +565,16 @@
             }
         }
     }
+
+    public static Object invokeMethod(
+            Object instance, String methodName, Class<?>[] parameterClasses, Object[] parameters) {
+        try {
+            Method method = instance.getClass().getDeclaredMethod(methodName, parameterClasses);
+            method.setAccessible(true);
+            return method.invoke(instance, parameters);
+        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+            fail(instance.getClass() + " " + methodName + " " + e.getClass().getName());
+        }
+        return null;
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TimeZoneLookupHelperTest.java b/tests/telephonytests/src/com/android/internal/telephony/TimeZoneLookupHelperTest.java
index ae24756..d0eeb43 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TimeZoneLookupHelperTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TimeZoneLookupHelperTest.java
@@ -16,6 +16,9 @@
 
 package com.android.internal.telephony;
 
+import com.android.internal.telephony.TimeZoneLookupHelper.CountryResult;
+import com.android.internal.telephony.TimeZoneLookupHelper.OffsetResult;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
@@ -34,6 +37,11 @@
 import java.util.concurrent.TimeUnit;
 
 public class TimeZoneLookupHelperTest {
+    // Note: Historical dates are used to avoid the test breaking due to data changes.
+    /* Arbitrary summer date in the Northern hemisphere. */
+    private static final long NH_SUMMER_TIME_MILLIS = createUtcTime(2015, 6, 20, 1, 2, 3);
+    /* Arbitrary winter date in the Northern hemisphere. */
+    private static final long NH_WINTER_TIME_MILLIS = createUtcTime(2015, 1, 20, 1, 2, 3);
 
     private TimeZoneLookupHelper mTimeZoneLookupHelper;
 
@@ -43,7 +51,7 @@
     }
 
     @Test
-    public void testGuessZoneIdByNitz() {
+    public void testLookupByNitzdByNitz() {
         // Historical dates are used to avoid the test breaking due to data changes.
         // However, algorithm updates may change the exact time zone returned, though it shouldn't
         // ever be a less exact match.
@@ -63,25 +71,35 @@
             int lonWinterOffsetMillis = 0;
             int lonWinterDstOffsetMillis = 0;
 
+            OffsetResult lookupResult;
+
             // Summer, known DST state (DST == true).
             NitzData lonSummerNitzDataWithOffset = NitzData.parse(lonSummerTimeString + ",4");
-            assertTimeZoneId(nhSummerTimeMillis, lonSummerOffsetMillis, lonSummerDstOffsetMillis,
-                    mTimeZoneLookupHelper.guessZoneIdByNitz(lonSummerNitzDataWithOffset));
+            lookupResult = mTimeZoneLookupHelper.lookupByNitz(lonSummerNitzDataWithOffset);
+            assertOffsetResultZoneOffsets(nhSummerTimeMillis, lonSummerOffsetMillis,
+                    lonSummerDstOffsetMillis, lookupResult);
+            assertOffsetResultMetadata(false, lookupResult);
 
             // Winter, known DST state (DST == false).
             NitzData lonWinterNitzDataWithOffset = NitzData.parse(lonWinterTimeString + ",0");
-            assertTimeZoneId(nhWinterTimeMillis, lonWinterOffsetMillis, lonWinterDstOffsetMillis,
-                    mTimeZoneLookupHelper.guessZoneIdByNitz(lonWinterNitzDataWithOffset));
+            lookupResult = mTimeZoneLookupHelper.lookupByNitz(lonWinterNitzDataWithOffset);
+            assertOffsetResultZoneOffsets(nhWinterTimeMillis, lonWinterOffsetMillis,
+                    lonWinterDstOffsetMillis, lookupResult);
+            assertOffsetResultMetadata(false, lookupResult);
 
             // Summer, unknown DST state
             NitzData lonSummerNitzDataWithoutOffset = NitzData.parse(lonSummerTimeString);
-            assertTimeZoneId(nhSummerTimeMillis, lonSummerOffsetMillis, null,
-                    mTimeZoneLookupHelper.guessZoneIdByNitz(lonSummerNitzDataWithoutOffset));
+            lookupResult = mTimeZoneLookupHelper.lookupByNitz(lonSummerNitzDataWithoutOffset);
+            assertOffsetResultZoneOffsets(nhSummerTimeMillis, lonSummerOffsetMillis, null,
+                    lookupResult);
+            assertOffsetResultMetadata(false, lookupResult);
 
             // Winter, unknown DST state
             NitzData lonWinterNitzDataWithoutOffset = NitzData.parse(lonWinterTimeString);
-            assertTimeZoneId(nhWinterTimeMillis, lonWinterOffsetMillis, null,
-                    mTimeZoneLookupHelper.guessZoneIdByNitz(lonWinterNitzDataWithoutOffset));
+            lookupResult = mTimeZoneLookupHelper.lookupByNitz(lonWinterNitzDataWithoutOffset);
+            assertOffsetResultZoneOffsets(nhWinterTimeMillis, lonWinterOffsetMillis, null,
+                    lookupResult);
+            assertOffsetResultMetadata(false, lookupResult);
         }
 
         // Tests for Mountain View, CA, US.
@@ -94,30 +112,40 @@
             int mtvWinterOffsetMillis = (int) TimeUnit.HOURS.toMillis(-7);
             int mtvWinterDstOffsetMillis = 0;
 
+            OffsetResult lookupResult;
+
             // Summer, known DST state (DST == true).
             NitzData mtvSummerNitzDataWithOffset = NitzData.parse(mtvSummerTimeString + ",4");
-            assertTimeZoneId(nhSummerTimeMillis, mtvSummerOffsetMillis, mtvSummerDstOffsetMillis,
-                    mTimeZoneLookupHelper.guessZoneIdByNitz(mtvSummerNitzDataWithOffset));
+            lookupResult = mTimeZoneLookupHelper.lookupByNitz(mtvSummerNitzDataWithOffset);
+            assertOffsetResultZoneOffsets(nhSummerTimeMillis, mtvSummerOffsetMillis,
+                    mtvSummerDstOffsetMillis, lookupResult);
+            assertOffsetResultMetadata(false, lookupResult);
 
             // Winter, known DST state (DST == false).
             NitzData mtvWinterNitzDataWithOffset = NitzData.parse(mtvWinterTimeString + ",0");
-            assertTimeZoneId(nhWinterTimeMillis, mtvWinterOffsetMillis, mtvWinterDstOffsetMillis,
-                    mTimeZoneLookupHelper.guessZoneIdByNitz(mtvWinterNitzDataWithOffset));
+            lookupResult = mTimeZoneLookupHelper.lookupByNitz(mtvWinterNitzDataWithOffset);
+            assertOffsetResultZoneOffsets(nhWinterTimeMillis, mtvWinterOffsetMillis,
+                    mtvWinterDstOffsetMillis, lookupResult);
+            assertOffsetResultMetadata(false, lookupResult);
 
             // Summer, unknown DST state
             NitzData mtvSummerNitzDataWithoutOffset = NitzData.parse(mtvSummerTimeString);
-            assertTimeZoneId(nhSummerTimeMillis, mtvSummerOffsetMillis, null,
-                    mTimeZoneLookupHelper.guessZoneIdByNitz(mtvSummerNitzDataWithoutOffset));
+            lookupResult = mTimeZoneLookupHelper.lookupByNitz(mtvSummerNitzDataWithoutOffset);
+            assertOffsetResultZoneOffsets(nhSummerTimeMillis, mtvSummerOffsetMillis, null,
+                    lookupResult);
+            assertOffsetResultMetadata(false, lookupResult);
 
             // Winter, unknown DST state
             NitzData mtvWinterNitzDataWithoutOffset = NitzData.parse(mtvWinterTimeString);
-            assertTimeZoneId(nhWinterTimeMillis, mtvWinterOffsetMillis, null,
-                    mTimeZoneLookupHelper.guessZoneIdByNitz(mtvWinterNitzDataWithoutOffset));
+            lookupResult = mTimeZoneLookupHelper.lookupByNitz(mtvWinterNitzDataWithoutOffset);
+            assertOffsetResultZoneOffsets(nhWinterTimeMillis, mtvWinterOffsetMillis, null,
+                    lookupResult);
+            assertOffsetResultMetadata(false, lookupResult);
         }
     }
 
     @Test
-    public void testGuessZoneIdByNitzCountry() {
+    public void testLookupByNitzCountry() {
         // Historical dates are used to avoid the test breaking due to data changes.
         // However, algorithm updates may change the exact time zone returned, though it shouldn't
         // ever be a less exact match.
@@ -126,8 +154,8 @@
 
         // Two countries in the northern hemisphere that share the same Winter and Summer DST
         // offsets at the dates being used.
-        String isoCountry1 = "DE"; // Germany
-        String isoCountry2 = "ES"; // Spain
+        String deIso = "DE"; // Germany
+        String adIso = "AD"; // Andora
         String summerTimeNitzString = "15/06/20,01:02:03+8";
         String winterTimeNitzString = "15/01/20,01:02:03+4";
 
@@ -140,19 +168,22 @@
             assertEquals(expectedUtcOffset, nitzData.getLocalOffsetMillis());
             assertEquals(expectedDstOffset, nitzData.getDstAdjustmentMillis());
 
-            String country1SummerWithDstResult =
-                    mTimeZoneLookupHelper.guessZoneIdByNitzCountry(nitzData, isoCountry1);
-            assertTimeZoneId(nhSummerTimeMillis, expectedUtcOffset, expectedDstOffset,
-                    country1SummerWithDstResult);
-            assertTimeZoneIdUsedInCountry(isoCountry1, country1SummerWithDstResult);
+            OffsetResult expectedResult;
 
-            String country2SummerWithDstResult =
-                    mTimeZoneLookupHelper.guessZoneIdByNitzCountry(nitzData, isoCountry2);
-            assertTimeZoneId(nhSummerTimeMillis, expectedUtcOffset, expectedDstOffset,
-                    country2SummerWithDstResult);
-            assertTimeZoneIdUsedInCountry(isoCountry2, country2SummerWithDstResult);
+            OffsetResult deSummerWithDstResult =
+                    mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, deIso);
+            expectedResult = new OffsetResult("Europe/Berlin", false /* isOnlyMatch */);
+            assertEquals(expectedResult, deSummerWithDstResult);
+            assertOffsetResultZoneOffsets(nhSummerTimeMillis, expectedUtcOffset, expectedDstOffset,
+                    deSummerWithDstResult);
 
-            assertDifferentZoneIds(country1SummerWithDstResult, country2SummerWithDstResult);
+            OffsetResult adSummerWithDstResult =
+                    mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, adIso);
+            expectedResult = new OffsetResult("Europe/Andorra", true /* isOnlyMatch */);
+            assertEquals(expectedResult, adSummerWithDstResult);
+            assertOffsetResultZoneOffsets(nhSummerTimeMillis, expectedUtcOffset, expectedDstOffset,
+                    adSummerWithDstResult);
+            assertOffsetResultZoneCountry(adIso, adSummerWithDstResult);
         }
 
         // Winter, known DST state (DST == false)
@@ -164,19 +195,21 @@
             assertEquals(expectedUtcOffset, nitzData.getLocalOffsetMillis());
             assertEquals(expectedDstOffset, nitzData.getDstAdjustmentMillis());
 
-            String country1WinterWithDstResult =
-                    mTimeZoneLookupHelper.guessZoneIdByNitzCountry(nitzData, isoCountry1);
-            assertTimeZoneId(nhWinterTimeMillis, expectedUtcOffset, expectedDstOffset,
-                    country1WinterWithDstResult);
-            assertTimeZoneIdUsedInCountry(isoCountry1, country1WinterWithDstResult);
+            OffsetResult expectedResult;
 
-            String country2WinterWithDstResult =
-                    mTimeZoneLookupHelper.guessZoneIdByNitzCountry(nitzData, isoCountry2);
-            assertTimeZoneId(nhWinterTimeMillis, expectedUtcOffset, expectedDstOffset,
-                    country2WinterWithDstResult);
-            assertTimeZoneIdUsedInCountry(isoCountry2, country2WinterWithDstResult);
+            OffsetResult deWinterWithDstResult =
+                    mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, deIso);
+            expectedResult = new OffsetResult("Europe/Berlin", false /* isOnlyMatch */);
+            assertEquals(expectedResult, deWinterWithDstResult);
+            assertOffsetResultZoneOffsets(nhWinterTimeMillis, expectedUtcOffset, expectedDstOffset,
+                    deWinterWithDstResult);
 
-            assertDifferentZoneIds(country1WinterWithDstResult, country2WinterWithDstResult);
+            OffsetResult adWinterWithDstResult =
+                    mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, adIso);
+            expectedResult = new OffsetResult("Europe/Andorra", true /* isOnlyMatch */);
+            assertEquals(expectedResult, adWinterWithDstResult);
+            assertOffsetResultZoneOffsets(nhWinterTimeMillis, expectedUtcOffset, expectedDstOffset,
+                    adWinterWithDstResult);
         }
 
         // Summer, unknown DST state
@@ -190,13 +223,13 @@
             assertEquals(expectedUtcOffset, nitzData.getLocalOffsetMillis());
             assertEquals(expectedDstOffset, nitzData.getDstAdjustmentMillis());
 
-            String country1SummerUnknownDstResult =
-                    mTimeZoneLookupHelper.guessZoneIdByNitzCountry(nitzData, isoCountry1);
-            assertNull(country1SummerUnknownDstResult);
+            OffsetResult deSummerUnknownDstResult =
+                    mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, deIso);
+            assertNull(deSummerUnknownDstResult);
 
-            String country2SummerUnknownDstResult =
-                    mTimeZoneLookupHelper.guessZoneIdByNitzCountry(nitzData, isoCountry2);
-            assertNull(country2SummerUnknownDstResult);
+            OffsetResult adSummerUnknownDstResult =
+                    mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, adIso);
+            assertNull(adSummerUnknownDstResult);
         }
 
         // Winter, unknown DST state
@@ -207,56 +240,74 @@
             assertEquals(expectedUtcOffset, nitzData.getLocalOffsetMillis());
             assertEquals(expectedDstOffset, nitzData.getDstAdjustmentMillis());
 
-            String country1WinterUnknownDstResult =
-                    mTimeZoneLookupHelper.guessZoneIdByNitzCountry(nitzData, isoCountry1);
-            assertTimeZoneId(nhWinterTimeMillis, expectedUtcOffset, expectedDstOffset,
-                    country1WinterUnknownDstResult);
-            assertTimeZoneIdUsedInCountry(isoCountry1, country1WinterUnknownDstResult);
+            OffsetResult expectedResult;
 
-            String country2WinterUnknownDstResult =
-                    mTimeZoneLookupHelper.guessZoneIdByNitzCountry(nitzData, isoCountry2);
-            assertTimeZoneId(nhWinterTimeMillis, expectedUtcOffset, expectedDstOffset,
-                    country2WinterUnknownDstResult);
-            assertTimeZoneIdUsedInCountry(isoCountry2, country2WinterUnknownDstResult);
+            OffsetResult deWinterUnknownDstResult =
+                    mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, deIso);
+            expectedResult = new OffsetResult("Europe/Berlin", false /* isOnlyMatch */);
+            assertEquals(expectedResult, deWinterUnknownDstResult);
+            assertOffsetResultZoneOffsets(nhWinterTimeMillis, expectedUtcOffset, expectedDstOffset,
+                    deWinterUnknownDstResult);
 
-            assertDifferentZoneIds(country1WinterUnknownDstResult, country2WinterUnknownDstResult);
+            OffsetResult adWinterUnknownDstResult =
+                    mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, adIso);
+            expectedResult = new OffsetResult("Europe/Andorra", true /* isOnlyMatch */);
+            assertEquals(expectedResult, adWinterUnknownDstResult);
+            assertOffsetResultZoneOffsets(nhWinterTimeMillis, expectedUtcOffset, expectedDstOffset,
+                    adWinterUnknownDstResult);
         }
     }
 
     @Test
-    public void testGuessZoneIdByCountry() {
+    public void testLookupByCountry() {
         // Historical dates are used to avoid the test breaking due to data changes.
         long nhSummerTimeMillis = createUtcTime(2015, 6, 20, 1, 2, 3);
         long nhWinterTimeMillis = createUtcTime(2015, 1, 20, 1, 2, 3);
 
+        CountryResult expectedResult;
+
         // GB has one time zone.
-        assertEquals("Europe/London",
-                mTimeZoneLookupHelper.guessZoneIdByCountry("gb", nhSummerTimeMillis));
-        assertEquals("Europe/London",
-                mTimeZoneLookupHelper.guessZoneIdByCountry("gb", nhWinterTimeMillis));
+        expectedResult = new CountryResult("Europe/London", true /* allZonesHaveSameOffset */,
+                nhSummerTimeMillis);
+        assertEquals(expectedResult,
+                mTimeZoneLookupHelper.lookupByCountry("gb", nhSummerTimeMillis));
+        expectedResult = new CountryResult("Europe/London", true /* allZonesHaveSameOffset */,
+                nhWinterTimeMillis);
+        assertEquals(expectedResult,
+                mTimeZoneLookupHelper.lookupByCountry("gb", nhWinterTimeMillis));
 
         // DE has two time zones according to data, but they agree on offset.
-        assertEquals("Europe/Berlin",
-                mTimeZoneLookupHelper.guessZoneIdByCountry("de", nhSummerTimeMillis));
-        assertEquals("Europe/Berlin",
-                mTimeZoneLookupHelper.guessZoneIdByCountry("de", nhWinterTimeMillis));
+        expectedResult = new CountryResult("Europe/Berlin", true /* allZonesHaveSameOffset */,
+                nhSummerTimeMillis);
+        assertEquals(expectedResult,
+                mTimeZoneLookupHelper.lookupByCountry("de", nhSummerTimeMillis));
+        expectedResult = new CountryResult("Europe/Berlin", true /* allZonesHaveSameOffset */,
+                nhWinterTimeMillis);
+        assertEquals(expectedResult,
+                mTimeZoneLookupHelper.lookupByCountry("de", nhWinterTimeMillis));
 
         // US has many time zones that have different offsets.
-        assertNull(mTimeZoneLookupHelper.guessZoneIdByCountry("us", nhSummerTimeMillis));
-        assertNull(mTimeZoneLookupHelper.guessZoneIdByCountry("us", nhWinterTimeMillis));
+        expectedResult = new CountryResult("America/New_York", false /* allZonesHaveSameOffset */,
+                nhSummerTimeMillis);
+        assertEquals(expectedResult,
+                mTimeZoneLookupHelper.lookupByCountry("us", nhSummerTimeMillis));
+        expectedResult = new CountryResult("America/New_York", false /* allZonesHaveSameOffset */,
+                nhWinterTimeMillis);
+        assertEquals(expectedResult,
+                mTimeZoneLookupHelper.lookupByCountry("us", nhWinterTimeMillis));
     }
 
     @Test
     public void testCountryUsesUtc() {
-        assertFalse(mTimeZoneLookupHelper.countryUsesUtc("us"));
-        assertTrue(mTimeZoneLookupHelper.countryUsesUtc("gb"));
+        assertFalse(mTimeZoneLookupHelper.countryUsesUtc("us", NH_SUMMER_TIME_MILLIS));
+        assertFalse(mTimeZoneLookupHelper.countryUsesUtc("us", NH_WINTER_TIME_MILLIS));
+        assertFalse(mTimeZoneLookupHelper.countryUsesUtc("gb", NH_SUMMER_TIME_MILLIS));
+        assertTrue(mTimeZoneLookupHelper.countryUsesUtc("gb", NH_WINTER_TIME_MILLIS));
     }
 
-    private static void assertDifferentZoneIds(String zone1, String zone2) {
-        assertFalse("Zone IDs not different, both=" + zone1, zone1.equals(zone2));
-    }
-
-    private static void assertTimeZoneIdUsedInCountry(String isoCountryCode, String timeZoneId) {
+    private static void assertOffsetResultZoneCountry(
+            String isoCountryCode, OffsetResult lookupResult) {
+        String timeZoneId = lookupResult.zoneId;
         List<String> zoneIdsByCountry =
                 TimeZoneFinder.getInstance().lookupTimeZoneIdsByCountry(isoCountryCode);
         assertTrue(timeZoneId + " must be used in " + isoCountryCode,
@@ -264,11 +315,12 @@
     }
 
     /**
-     * Assert the timeZone has the expected properties at the specified time.
+     * Assert the time zone in the OffsetResult has the expected properties at the specified time.
      */
-    private static void assertTimeZoneId(
-            long time, int expectedOffsetAtTime, Integer expectedDstAtTime, String timeZoneId) {
-        TimeZone timeZone = TimeZone.getTimeZone(timeZoneId);
+    private static void assertOffsetResultZoneOffsets(long time, int expectedOffsetAtTime,
+            Integer expectedDstAtTime, OffsetResult lookupResult) {
+
+        TimeZone timeZone = TimeZone.getTimeZone(lookupResult.zoneId);
         GregorianCalendar calendar = new GregorianCalendar(timeZone);
         calendar.setTimeInMillis(time);
         int actualOffsetAtTime =
@@ -285,6 +337,10 @@
         }
     }
 
+    private static void assertOffsetResultMetadata(boolean isOnlyMatch, OffsetResult lookupResult) {
+        assertEquals(isOnlyMatch, lookupResult.isOnlyMatch);
+    }
+
     private static long createUtcTime(
             int year, int monthOfYear, int dayOfMonth, int hourOfDay, int minute, int second) {
         GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
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 975c9a1..2cbfefb 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
@@ -20,12 +20,14 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkPolicyManager.OVERRIDE_CONGESTED;
 import static android.net.NetworkPolicyManager.OVERRIDE_UNMETERED;
+
 import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
 import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_ADDRESS;
 import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_DNS;
 import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_GATEWAY;
 import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_IFNAME;
 import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_PCSCF_ADDRESS;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -35,16 +37,18 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.content.IntentFilter;
+import android.content.pm.ServiceInfo;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkUtils;
-import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Message;
 import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.AccessNetworkConstants.TransportType;
 import android.telephony.CarrierConfigManager;
 import android.telephony.ServiceState;
 import android.telephony.data.DataCallResponse;
@@ -52,6 +56,7 @@
 import android.telephony.data.DataService;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import com.android.internal.R;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.RetryManager;
 import com.android.internal.telephony.TelephonyTest;
@@ -126,12 +131,30 @@
         public void onLooperPrepared() {
             Handler h = new Handler();
 
-            mDcc = DcController.makeDcc(mPhone, mDcTracker, h);
-            mDc = DataConnection.makeDataConnection(mPhone, 0, mDcTracker, mDcTesterFailBringUpAll,
-                    mDcc);
+            DataServiceManager manager = new DataServiceManager(mPhone, TransportType.WWAN);
+            mDcc = DcController.makeDcc(mPhone, mDcTracker, manager, h);
+            mDcc.start();
+            mDc = DataConnection.makeDataConnection(mPhone, 0, mDcTracker, manager,
+                    mDcTesterFailBringUpAll, mDcc);
         }
     }
 
+    private void addDataService() {
+        CellularDataService cellularDataService = new CellularDataService();
+        ServiceInfo serviceInfo = new ServiceInfo();
+        serviceInfo.packageName = "com.android.phone";
+        serviceInfo.permission = "android.permission.BIND_DATA_SERVICE";
+        IntentFilter filter = new IntentFilter();
+        mContextFixture.addService(
+                DataService.DATA_SERVICE_INTERFACE,
+                null,
+                "com.android.phone",
+                cellularDataService.mBinder,
+                serviceInfo,
+                filter);
+    }
+
+
     @Before
     public void setUp() throws Exception {
         logd("+Setup!");
@@ -160,9 +183,13 @@
                 "evdo:131072,262144,1048576,4096,16384,524288",
                 "lte:524288,1048576,8388608,262144,524288,4194304"});
 
+        mContextFixture.putResource(R.string.config_wwan_data_service_package,
+                "com.android.phone");
 
         mDcp.mApnContext = mApnContext;
 
+        addDataService();
+
         mDataConnectionTestHandler = new DataConnectionTestHandler(getClass().getSimpleName());
         mDataConnectionTestHandler.start();
 
@@ -185,12 +212,12 @@
         return (IState) method.invoke(mDc);
     }
 
-    private long getSuggestedRetryDelay(AsyncResult ar) throws Exception {
+    private long getSuggestedRetryDelay(DataCallResponse response) throws Exception {
         Class[] cArgs = new Class[1];
-        cArgs[0] = AsyncResult.class;
+        cArgs[0] = DataCallResponse.class;
         Method method = DataConnection.class.getDeclaredMethod("getSuggestedRetryDelay", cArgs);
         method.setAccessible(true);
-        return (long) method.invoke(mDc, ar);
+        return (long) method.invoke(mDc, response);
     }
 
     private SetupResult setLinkProperties(DataCallResponse response,
@@ -257,8 +284,7 @@
                 Arrays.asList(FAKE_PCSCF_ADDRESS),
                 1440);
 
-        AsyncResult ar = new AsyncResult(null, response, null);
-        assertEquals(response.getSuggestedRetryTime(), getSuggestedRetryDelay(ar));
+        assertEquals(response.getSuggestedRetryTime(), getSuggestedRetryDelay(response));
 
         response = new DataCallResponse(0, 1000, 1, 2, "IP", FAKE_IFNAME,
                 Arrays.asList(new LinkAddress(NetworkUtils.numericToInetAddress(FAKE_ADDRESS), 0)),
@@ -266,8 +292,7 @@
                 Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_GATEWAY)),
                 Arrays.asList(FAKE_PCSCF_ADDRESS),
                 1440);
-        ar = new AsyncResult(null, response, null);
-        assertEquals(response.getSuggestedRetryTime(), getSuggestedRetryDelay(ar));
+        assertEquals(response.getSuggestedRetryTime(), getSuggestedRetryDelay(response));
 
         response = new DataCallResponse(0, 9999, 1, 2, "IP", FAKE_IFNAME,
                 Arrays.asList(new LinkAddress(NetworkUtils.numericToInetAddress(FAKE_ADDRESS), 0)),
@@ -275,9 +300,7 @@
                 Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_GATEWAY)),
                 Arrays.asList(FAKE_PCSCF_ADDRESS),
                 1440);
-        ar = new AsyncResult(null, response, null);
-
-        assertEquals(response.getSuggestedRetryTime(), getSuggestedRetryDelay(ar));
+        assertEquals(response.getSuggestedRetryTime(), getSuggestedRetryDelay(response));
     }
 
     @Test
@@ -289,8 +312,8 @@
                 Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_GATEWAY)),
                 Arrays.asList(FAKE_PCSCF_ADDRESS),
                 1440);
-        AsyncResult ar = new AsyncResult(null, response, null);
-        assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY, getSuggestedRetryDelay(ar));
+
+        assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY, getSuggestedRetryDelay(response));
 
         response = new DataCallResponse(0, -5, 1, 2, "IP", FAKE_IFNAME,
                 Arrays.asList(new LinkAddress(NetworkUtils.numericToInetAddress(FAKE_ADDRESS), 0)),
@@ -298,8 +321,7 @@
                 Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_GATEWAY)),
                 Arrays.asList(FAKE_PCSCF_ADDRESS),
                 1440);
-        ar = new AsyncResult(null, response, null);
-        assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY, getSuggestedRetryDelay(ar));
+        assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY, getSuggestedRetryDelay(response));
 
         response = new DataCallResponse(0, Integer.MIN_VALUE, 1, 2, "IP", FAKE_IFNAME,
                 Arrays.asList(new LinkAddress(NetworkUtils.numericToInetAddress(FAKE_ADDRESS), 0)),
@@ -307,8 +329,7 @@
                 Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_GATEWAY)),
                 Arrays.asList(FAKE_PCSCF_ADDRESS),
                 1440);
-        ar = new AsyncResult(null, response, null);
-        assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY, getSuggestedRetryDelay(ar));
+        assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY, getSuggestedRetryDelay(response));
     }
 
     @Test
@@ -321,8 +342,7 @@
                 Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_GATEWAY)),
                 Arrays.asList(FAKE_PCSCF_ADDRESS),
                 1440);
-        AsyncResult ar = new AsyncResult(null, response, null);
-        assertEquals(RetryManager.NO_RETRY, getSuggestedRetryDelay(ar));
+        assertEquals(RetryManager.NO_RETRY, getSuggestedRetryDelay(response));
     }
 
     private NetworkInfo getNetworkInfo() throws Exception {
@@ -470,7 +490,7 @@
                 1440);
 
         LinkProperties linkProperties = new LinkProperties();
-        assertEquals(SetupResult.ERR_UnacceptableParameter,
+        assertEquals(SetupResult.ERROR_INVALID_ARG,
                 setLinkProperties(response, linkProperties));
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java
index 0683c48..c86c911 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java
@@ -66,6 +66,8 @@
     DataConnection mDc;
     @Mock
     HashMap<ApnContext, ConnectionParams> mApnContexts;
+    @Mock
+    DataServiceManager mDataServiceManager;
 
     UpdateLinkPropertyResult mResult;
 
@@ -83,7 +85,8 @@
         @Override
         public void onLooperPrepared() {
             mHandler = new Handler();
-            mDcc = DcController.makeDcc(mPhone, mDcTracker, mHandler);
+            mDcc = DcController.makeDcc(mPhone, mDcTracker, mDataServiceManager, mHandler);
+            mDcc.start();
             setReady(true);
         }
     }
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 c6a17dd..f7eed07 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
 import static com.android.internal.telephony.dataconnection.ApnSettingTest.createApnSetting;
+
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -39,13 +40,14 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ServiceInfo;
 import android.database.Cursor;
 import android.database.MatrixCursor;
-import android.net.LinkAddress;
+import android.hardware.radio.V1_0.SetupDataCallResult;
 import android.net.LinkProperties;
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
-import android.net.NetworkUtils;
 import android.net.Uri;
 import android.os.AsyncResult;
 import android.os.HandlerThread;
@@ -55,12 +57,12 @@
 import android.provider.Settings;
 import android.provider.Telephony;
 import android.support.test.filters.FlakyTest;
+import android.telephony.AccessNetworkConstants.TransportType;
 import android.telephony.CarrierConfigManager;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
-import android.telephony.data.DataCallResponse;
 import android.telephony.data.DataProfile;
 import android.telephony.data.DataService;
 import android.test.mock.MockContentProvider;
@@ -69,6 +71,7 @@
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.LocalLog;
 
+import com.android.internal.R;
 import com.android.internal.telephony.DctConstants;
 import com.android.internal.telephony.ISub;
 import com.android.internal.telephony.Phone;
@@ -146,6 +149,21 @@
     private final ApnSettingContentProvider mApnSettingContentProvider =
             new ApnSettingContentProvider();
 
+    private void addDataService() {
+        CellularDataService cellularDataService = new CellularDataService();
+        ServiceInfo serviceInfo = new ServiceInfo();
+        serviceInfo.packageName = "com.android.phone";
+        serviceInfo.permission = "android.permission.BIND_DATA_SERVICE";
+        IntentFilter filter = new IntentFilter();
+        mContextFixture.addService(
+                DataService.DATA_SERVICE_INTERFACE,
+                null,
+                "com.android.phone",
+                cellularDataService.mBinder,
+                serviceInfo,
+                filter);
+    }
+
     private class DcTrackerTestHandler extends HandlerThread {
 
         private DcTrackerTestHandler(String name) {
@@ -154,7 +172,7 @@
 
         @Override
         public void onLooperPrepared() {
-            mDct = new DcTracker(mPhone);
+            mDct = new DcTracker(mPhone, TransportType.WWAN);
             setReady(true);
         }
     }
@@ -389,6 +407,9 @@
                 "evdo:131072,262144,1048576,4096,16384,524288",
                 "lte:524288,1048576,8388608,262144,524288,4194304"});
 
+        mContextFixture.putResource(R.string.config_wwan_data_service_package,
+                "com.android.phone");
+
         ((MockContentResolver) mContext.getContentResolver()).addProvider(
                 Telephony.Carriers.CONTENT_URI.getAuthority(), mApnSettingContentProvider);
 
@@ -420,7 +441,8 @@
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
         mBundle = mContextFixture.getCarrierConfigBundle();
 
-        mSimulatedCommands.setDataCallResponse(true, createDataCallResponse());
+        mSimulatedCommands.setDataCallResult(true, createSetupDataCallResult());
+        addDataService();
 
         mDcTrackerTestHandler = new DcTrackerTestHandler(getClass().getSimpleName());
         mDcTrackerTestHandler.start();
@@ -439,14 +461,20 @@
     }
 
     // Create a successful data response
-    private static DataCallResponse createDataCallResponse() throws Exception {
-
-        return new DataCallResponse(0, -1, 1, 2, "IP", FAKE_IFNAME,
-                Arrays.asList(new LinkAddress(NetworkUtils.numericToInetAddress(FAKE_ADDRESS), 0)),
-                Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_DNS)),
-                Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_GATEWAY)),
-                Arrays.asList(FAKE_PCSCF_ADDRESS),
-                1440);
+    private static SetupDataCallResult createSetupDataCallResult() throws Exception {
+        SetupDataCallResult result = new SetupDataCallResult();
+        result.status = 0;
+        result.suggestedRetryTime = -1;
+        result.cid = 1;
+        result.active = 2;
+        result.type = "IP";
+        result.ifname = FAKE_IFNAME;
+        result.addresses = FAKE_ADDRESS;
+        result.dnses = FAKE_DNS;
+        result.gateways = FAKE_GATEWAY;
+        result.pcscf = FAKE_PCSCF_ADDRESS;
+        result.mtu = 1440;
+        return result;
     }
 
     private void verifyDataProfile(DataProfile dp, String apn, int profileId,
@@ -512,7 +540,7 @@
 
         mDct.setUserDataEnabled(true);
 
-        mSimulatedCommands.setDataCallResponse(true, createDataCallResponse());
+        mSimulatedCommands.setDataCallResult(true, createSetupDataCallResult());
 
         DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
         boolean allowed = isDataAllowed(dataConnectionReasons);
@@ -576,15 +604,17 @@
         mDct.setUserDataEnabled(true);
 
         // LOST_CONNECTION(0x10004) is a non-permanent failure, so we'll retry data setup later.
-        DataCallResponse dcResponse = new DataCallResponse(0x10004, -1, 1, 2, "IP", FAKE_IFNAME,
+        /*DataCallResponse dcResponse = new DataCallResponse(0x10004, -1, 1, 2, "IP", FAKE_IFNAME,
                 Arrays.asList(new LinkAddress(NetworkUtils.numericToInetAddress(FAKE_ADDRESS), 0)),
                 Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_DNS)),
                 Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_GATEWAY)),
                 Arrays.asList(FAKE_PCSCF_ADDRESS),
-                1440);
+                1440);*/
+        SetupDataCallResult result = createSetupDataCallResult();
+        result.status = 0x10004;
 
         // Simulate RIL fails the data call setup
-        mSimulatedCommands.setDataCallResponse(false, dcResponse);
+        mSimulatedCommands.setDataCallResult(false, result);
 
         DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
         boolean allowed = isDataAllowed(dataConnectionReasons);
@@ -647,7 +677,7 @@
                 anyLong(), any(PendingIntent.class));
 
         // This time we'll let RIL command succeed.
-        mSimulatedCommands.setDataCallResponse(true, createDataCallResponse());
+        mSimulatedCommands.setDataCallResult(true, createSetupDataCallResult());
 
         // Simulate the timer expires.
         Intent intent = new Intent("com.android.internal.telephony.data-reconnect.default");
@@ -841,7 +871,7 @@
         mContextFixture.putBooleanResource(
                 com.android.internal.R.bool.config_auto_attach_data_on_creation, true);
 
-        mSimulatedCommands.setDataCallResponse(true, createDataCallResponse());
+        mSimulatedCommands.setDataCallResult(true, createSetupDataCallResult());
 
         DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
         boolean allowed = isDataAllowed(dataConnectionReasons);
diff --git a/tests/telephonytests/src/android/telephony/ims/ImsCallProfileTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsCallProfileTest.java
similarity index 84%
rename from tests/telephonytests/src/android/telephony/ims/ImsCallProfileTest.java
rename to tests/telephonytests/src/com/android/internal/telephony/ims/ImsCallProfileTest.java
index bc8d4e1..2ea1f28 100644
--- a/tests/telephonytests/src/android/telephony/ims/ImsCallProfileTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsCallProfileTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -11,12 +11,12 @@
  * 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.
+ * limitations under the License
  */
 
 // Note: Package name is intentionally wrong for this test; the internal junk class is used to test
 // that parcelables of types other than android.* are stripped out.
-package com.android.telephony.ims;
+package com.android.internal.telephony.ims;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
@@ -28,13 +28,15 @@
 import android.telecom.DisconnectCause;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import com.android.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallProfile;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 /**
- * Tests for the {@link com.android.ims.ImsCallProfile} class.
+ * Tests for the {@link ImsCallProfile} class.
+ *
+ * Test must NOT be in the "android." namespace.
  */
 @RunWith(AndroidJUnit4.class)
 public class ImsCallProfileTest {
@@ -49,15 +51,15 @@
             mTest = in.readInt();
         }
 
-        public static final Creator<JunkParcelable> CREATOR = new Creator<JunkParcelable>() {
+        public static final Creator<JunkParcelable> CREATOR = new Creator<ImsCallProfileTest.JunkParcelable>() {
             @Override
-            public JunkParcelable createFromParcel(Parcel in) {
-                return new JunkParcelable(in);
+            public ImsCallProfileTest.JunkParcelable createFromParcel(Parcel in) {
+                return new ImsCallProfileTest.JunkParcelable(in);
             }
 
             @Override
-            public JunkParcelable[] newArray(int size) {
-                return new JunkParcelable[size];
+            public ImsCallProfileTest.JunkParcelable[] newArray(int size) {
+                return new ImsCallProfileTest.JunkParcelable[size];
             }
         };
 
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 17ac579..f217c7d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsManagerTest.java
@@ -17,17 +17,20 @@
 package com.android.internal.telephony.ims;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.os.IBinder;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
+import android.telephony.ims.stub.ImsConfigImplBase;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.ims.ImsConfig;
@@ -39,11 +42,32 @@
 import org.junit.Test;
 import org.mockito.Mock;
 
+import java.util.Hashtable;
+
 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;
 
     PersistableBundle mBundle;
+
     @Mock
     IBinder mBinder;
+    @Mock
+    ImsConfigImplBase mImsConfigImplBaseMock;
+    Hashtable<Integer, Integer> mProvisionedIntVals = new Hashtable<>();
+    Hashtable<Integer, String> mProvisionedStringVals = new Hashtable<>();
+    ImsConfigImplBase.ImsConfigStub mImsConfigStub;
+    ImsConfig mImsConfig;
+
+    private final int[] mSubId = {0};
+    private int mPhoneId;
 
     @Before
     public void setUp() throws Exception {
@@ -57,6 +81,8 @@
         mServiceManagerMockedServices.put("isub", mBinder);
 
         mImsManagerInstances.remove(mPhoneId);
+
+        setDefaultValues();
     }
 
     @After
@@ -75,25 +101,16 @@
                 WFC_IMS_MODE_DEFAULT_VAL);
         mBundle.putInt(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT,
                 WFC_IMS_ROAMING_MODE_DEFAULT_VAL);
+        mBundle.putBoolean(CarrierConfigManager.KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL,
+                ENHANCED_4G_MODE_DEFAULT_VAL);
+        mBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, true);
     }
 
-    private static final boolean ENHANCED_4G_ENABLE_DEFAULT_VAL = 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 final int[] mSubId = {0};
-    private int mPhoneId;
-
     @Test @SmallTest
     public void testGetDefaultValues() {
         doReturn("-1").when(mSubscriptionController)
                 .getSubscriptionProperty(anyInt(), anyString(), anyString());
 
-        setDefaultValues();
-
         ImsManager imsManager = ImsManager.getInstance(mContext, mPhoneId);
 
         assertEquals(WFC_IMS_ENABLE_DEFAULT_VAL, imsManager.isWfcEnabledByUser());
@@ -150,6 +167,9 @@
                 eq(SubscriptionManager.VT_IMS_ENABLED),
                 eq("0"));
 
+        // enhanced 4g mode must be editable to use setEnhanced4gLteModeSetting
+        mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL,
+                ENHANCED_4G_MODE_EDITABLE);
         imsManager.setEnhanced4gLteModeSetting(true);
         verify(mSubscriptionController, times(1)).setSubscriptionProperty(
                 eq(mSubId[0]),
@@ -162,4 +182,110 @@
                 eq(SubscriptionManager.WFC_IMS_ENABLED),
                 eq("1"));
     }
+
+    @Test
+    public void testGetProvisionedValues() throws Exception {
+        ImsManager imsManager = initializeProvisionedValues();
+
+        assertEquals(true, imsManager.isWfcProvisionedOnDevice());
+        verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
+                eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED));
+
+        assertEquals(true, imsManager.isVtProvisionedOnDevice());
+        verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
+                eq(ImsConfig.ConfigConstants.LVC_SETTING_ENABLED));
+
+        assertEquals(true, imsManager.isVolteProvisionedOnDevice());
+        verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
+                eq(ImsConfig.ConfigConstants.VLT_SETTING_ENABLED));
+
+        // If we call get again, times should still be one because the value should be fetched
+        // from cache.
+        assertEquals(true, imsManager.isWfcProvisionedOnDevice());
+        verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
+                eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED));
+
+        assertEquals(true, imsManager.isVtProvisionedOnDevice());
+        verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
+                eq(ImsConfig.ConfigConstants.LVC_SETTING_ENABLED));
+
+        assertEquals(true, imsManager.isVolteProvisionedOnDevice());
+        verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
+                eq(ImsConfig.ConfigConstants.VLT_SETTING_ENABLED));
+    }
+
+    @Test
+    public void testSetProvisionedValues() throws Exception {
+        ImsManager imsManager = initializeProvisionedValues();
+
+        assertEquals(true, imsManager.isWfcProvisionedOnDevice());
+        verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
+                eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED));
+
+        imsManager.getConfigInterface().setProvisionedValue(
+                ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED,
+                ImsConfig.FeatureValueConstants.OFF);
+
+        assertEquals(0, (int) mProvisionedIntVals.get(
+                ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED));
+
+        assertEquals(false, imsManager.isWfcProvisionedOnDevice());
+
+        verify(mImsConfigImplBaseMock, times(1)).setConfig(
+                eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED),
+                eq(0));
+        verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
+                eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED));
+
+    }
+
+    private ImsManager initializeProvisionedValues() {
+        when(mImsConfigImplBaseMock.getConfigInt(anyInt()))
+                .thenAnswer(invocation ->  {
+                    return getProvisionedInt((Integer) (invocation.getArguments()[0]));
+                });
+
+        when(mImsConfigImplBaseMock.setConfig(anyInt(), anyInt()))
+                .thenAnswer(invocation ->  {
+                    mProvisionedIntVals.put((Integer) (invocation.getArguments()[0]),
+                            (Integer) (invocation.getArguments()[1]));
+                    return ImsConfig.OperationStatusConstants.SUCCESS;
+                });
+
+
+        // Configure ImsConfigStub
+        mImsConfigStub = new ImsConfigImplBase.ImsConfigStub(mImsConfigImplBaseMock);
+        doReturn(mImsConfigStub).when(mImsConfigImplBaseMock).getIImsConfig();
+
+        // Configure ImsConfig
+        mImsConfig = new ImsConfig(mImsConfigStub, mContext);
+
+        // Configure ImsManager
+        ImsManager imsManager = ImsManager.getInstance(mContext, mPhoneId);
+        try {
+            replaceInstance(ImsManager.class, "mConfig", imsManager, mImsConfig);
+        } catch (Exception ex) {
+            fail("failed with " + ex);
+        }
+
+        return imsManager;
+    }
+
+    // If the value is ever set, return the set value. If not, return a constant value 1000.
+    private int getProvisionedInt(int item) {
+        if (mProvisionedIntVals.containsKey(item)) {
+            return mProvisionedIntVals.get(item);
+        } else {
+            return ImsConfig.FeatureValueConstants.ON;
+        }
+    }
+
+    // If the value is ever set, return the set value. If not, return a constant value "unset".
+    private String getProvisionedString(int item) {
+        if (mProvisionedStringVals.containsKey(item)) {
+            return mProvisionedStringVals.get(item);
+        } else {
+            return UNSET_PROVISIONED_STRING;
+        }
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
index 4ad0047..e7f701d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
@@ -16,6 +16,20 @@
 
 package com.android.internal.telephony.ims;
 
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 import android.Manifest;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -29,6 +43,7 @@
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.telephony.CarrierConfigManager;
+import android.telephony.ims.ImsService;
 import android.telephony.ims.feature.ImsFeature;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -50,18 +65,6 @@
 import java.util.Set;
 import java.util.stream.Collectors;
 
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
 /**
  * Unit tests for ImsResolver
  */
@@ -108,7 +111,6 @@
         setupResolver(1/*numSlots*/);
         List<ResolveInfo> info = new ArrayList<>();
         Set<String> features = new HashSet<>();
-        features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
         features.add(ImsResolver.METADATA_MMTEL_FEATURE);
         features.add(ImsResolver.METADATA_RCS_FEATURE);
         info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, features, true));
@@ -137,16 +139,25 @@
         setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
         List<ResolveInfo> info = new ArrayList<>();
         Set<String> features = new HashSet<>();
-        features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
         features.add(ImsResolver.METADATA_MMTEL_FEATURE);
         features.add(ImsResolver.METADATA_RCS_FEATURE);
         info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, features, true));
         when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
         ImsServiceController controller = mock(ImsServiceController.class);
-        mTestImsResolver.setImsServiceControllerFactory((context, componentName) -> {
-            when(controller.getComponentName()).thenReturn(componentName);
-            return controller;
-        });
+        mTestImsResolver.setImsServiceControllerFactory(
+                new ImsResolver.ImsServiceControllerFactory() {
+                    @Override
+                    public String getServiceInterface() {
+                        return ImsService.SERVICE_INTERFACE;
+                    }
+
+                    @Override
+                    public ImsServiceController create(Context context, ComponentName componentName,
+                            ImsServiceController.ImsServiceControllerCallbacks callbacks) {
+                        when(controller.getComponentName()).thenReturn(componentName);
+                        return controller;
+                    }
+                });
 
 
         mTestImsResolver.populateCacheAndStartBind();
@@ -166,16 +177,25 @@
         setupResolver(1/*numSlots*/);
         List<ResolveInfo> info = new ArrayList<>();
         Set<String> features = new HashSet<>();
-        features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
         features.add(ImsResolver.METADATA_MMTEL_FEATURE);
         features.add(ImsResolver.METADATA_RCS_FEATURE);
         info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, features, true));
         when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
         ImsServiceController controller = mock(ImsServiceController.class);
-        mTestImsResolver.setImsServiceControllerFactory((context, componentName) -> {
-            when(controller.getComponentName()).thenReturn(componentName);
-            return controller;
-        });
+        mTestImsResolver.setImsServiceControllerFactory(
+                new ImsResolver.ImsServiceControllerFactory() {
+                    @Override
+                    public String getServiceInterface() {
+                        return ImsService.SERVICE_INTERFACE;
+                    }
+
+                    @Override
+                    public ImsServiceController create(Context context, ComponentName componentName,
+                            ImsServiceController.ImsServiceControllerCallbacks callbacks) {
+                        when(controller.getComponentName()).thenReturn(componentName);
+                        return controller;
+                    }
+                });
 
         // Set the CarrierConfig string to null so that ImsResolver will not bind to the available
         // Services
@@ -197,7 +217,6 @@
         setupResolver(1/*numSlots*/);
         List<ResolveInfo> info = new ArrayList<>();
         Set<String> features = new HashSet<>();
-        features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
         features.add(ImsResolver.METADATA_MMTEL_FEATURE);
         features.add(ImsResolver.METADATA_RCS_FEATURE);
         // Use device default package, which will load the ImsService that the device provides
@@ -205,10 +224,20 @@
         info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, features, true));
         when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
         ImsServiceController controller = mock(ImsServiceController.class);
-        mTestImsResolver.setImsServiceControllerFactory((context, componentName) -> {
-            when(controller.getComponentName()).thenReturn(componentName);
-            return controller;
-        });
+        mTestImsResolver.setImsServiceControllerFactory(
+                new ImsResolver.ImsServiceControllerFactory() {
+                    @Override
+                    public String getServiceInterface() {
+                        return ImsService.SERVICE_INTERFACE;
+                    }
+
+                    @Override
+                    public ImsServiceController create(Context context, ComponentName componentName,
+                            ImsServiceController.ImsServiceControllerCallbacks callbacks) {
+                        when(controller.getComponentName()).thenReturn(componentName);
+                        return controller;
+                    }
+                });
 
 
         mTestImsResolver.populateCacheAndStartBind();
@@ -233,14 +262,12 @@
         setupResolver(1/*numSlots*/);
         List<ResolveInfo> info = new ArrayList<>();
         Set<String> deviceFeatures = new HashSet<>();
-        deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
         deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
         deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
         // Set the carrier override package for slot 0
         setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
         Set<String> carrierFeatures = new HashSet<>();
-        // Carrier service doesn't support the emergency voice feature.
-        carrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        // Carrier service doesn't support the voice feature.
         carrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
         // Use device default package, which will load the ImsService that the device provides
         info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
@@ -283,27 +310,20 @@
 
         // Callback from mock ImsServiceControllers
         // All features on slot 1 should be the device default
-        mTestImsResolver.imsServiceFeatureCreated(1, ImsFeature.EMERGENCY_MMTEL, deviceController);
-        mTestImsResolver.imsServiceFeatureCreated(1, ImsFeature.MMTEL, deviceController);
-        mTestImsResolver.imsServiceFeatureCreated(1, ImsFeature.RCS, deviceController);
-        // The carrier override does not support emergency voice
-        mTestImsResolver.imsServiceFeatureCreated(1, ImsFeature.EMERGENCY_MMTEL, deviceController);
-        // The carrier override contains these features
-        mTestImsResolver.imsServiceFeatureCreated(0, ImsFeature.MMTEL, carrierController);
-        mTestImsResolver.imsServiceFeatureCreated(0, ImsFeature.RCS, carrierController);
+        mTestImsResolver.imsServiceFeatureCreated(1, ImsFeature.FEATURE_MMTEL, deviceController);
+        mTestImsResolver.imsServiceFeatureCreated(1, ImsFeature.FEATURE_RCS, deviceController);
+        mTestImsResolver.imsServiceFeatureCreated(0, ImsFeature.FEATURE_MMTEL, deviceController);
+        // The carrier override contains this feature
+        mTestImsResolver.imsServiceFeatureCreated(0, ImsFeature.FEATURE_RCS, carrierController);
         // Get the IImsServiceControllers for each feature on each slot and verify they are correct.
         assertEquals(deviceController, mTestImsResolver.getImsServiceControllerAndListen(
-                1/*Slot id*/, ImsFeature.EMERGENCY_MMTEL, null));
+                1 /*Slot id*/, ImsFeature.FEATURE_MMTEL, null));
         assertEquals(deviceController, mTestImsResolver.getImsServiceControllerAndListen(
-                1 /*Slot id*/, ImsFeature.MMTEL, null));
+                1 /*Slot id*/, ImsFeature.FEATURE_RCS, null));
         assertEquals(deviceController, mTestImsResolver.getImsServiceControllerAndListen(
-                1 /*Slot id*/, ImsFeature.RCS, null));
-        assertEquals(deviceController, mTestImsResolver.getImsServiceControllerAndListen(
-                1 /*Slot id*/, ImsFeature.EMERGENCY_MMTEL, null));
+                0 /*Slot id*/, ImsFeature.FEATURE_MMTEL, null));
         assertEquals(carrierController, mTestImsResolver.getImsServiceControllerAndListen(
-                0 /*Slot id*/, ImsFeature.MMTEL, null));
-        assertEquals(carrierController, mTestImsResolver.getImsServiceControllerAndListen(
-                0 /*Slot id*/, ImsFeature.RCS, null));
+                0 /*Slot id*/, ImsFeature.FEATURE_RCS, null));
     }
 
     /**
@@ -316,16 +336,28 @@
         setupResolver(2/*numSlots*/);
         List<ResolveInfo> info = new ArrayList<>();
         Set<String> features = new HashSet<>();
-        features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
         features.add(ImsResolver.METADATA_MMTEL_FEATURE);
         // Doesn't include RCS feature by default
         info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, features, true));
-        when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+        when(mMockPM.queryIntentServicesAsUser(
+                argThat(argument -> ImsService.SERVICE_INTERFACE.equals(
+                        argument.getAction())), anyInt(), anyInt()))
+                .thenReturn(info);
         ImsServiceController controller = mock(ImsServiceController.class);
-        mTestImsResolver.setImsServiceControllerFactory((context, componentName) -> {
-            when(controller.getComponentName()).thenReturn(componentName);
-            return controller;
-        });
+        mTestImsResolver.setImsServiceControllerFactory(
+                new ImsResolver.ImsServiceControllerFactory() {
+                    @Override
+                    public String getServiceInterface() {
+                        return ImsService.SERVICE_INTERFACE;
+                    }
+
+                    @Override
+                    public ImsServiceController create(Context context, ComponentName componentName,
+                            ImsServiceController.ImsServiceControllerCallbacks callbacks) {
+                        when(controller.getComponentName()).thenReturn(componentName);
+                        return controller;
+                    }
+                });
 
         // Bind using default features
         mTestImsResolver.populateCacheAndStartBind();
@@ -364,7 +396,6 @@
         setupResolver(2/*numSlots*/);
         List<ResolveInfo> info = new ArrayList<>();
         Set<String> deviceFeatures = new HashSet<>();
-        deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
         deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
         // Set the carrier override package for slot 0
         setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
@@ -375,7 +406,10 @@
         // Use device default package, which will load the ImsService that the device provides
         info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
         info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
-        when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+        when(mMockPM.queryIntentServicesAsUser(
+                argThat(argument -> ImsService.SERVICE_INTERFACE.equals(
+                        argument.getAction())), anyInt(), anyInt()))
+                .thenReturn(info);
         ImsServiceController deviceController = mock(ImsServiceController.class);
         ImsServiceController carrierController = mock(ImsServiceController.class);
         setImsServiceControllerFactory(deviceController, carrierController);
@@ -400,7 +434,6 @@
 
         // add RCS to features list
         Set<String> newDeviceFeatures = new HashSet<>();
-        newDeviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
         newDeviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
         newDeviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
         info.clear();
@@ -435,7 +468,6 @@
         setupResolver(2/*numSlots*/);
         List<ResolveInfo> info = new ArrayList<>();
         Set<String> deviceFeatures = new HashSet<>();
-        deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
         deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
         deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
         // Set the carrier override package for slot 0
@@ -446,7 +478,10 @@
         // Use device default package, which will load the ImsService that the device provides
         info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
         info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
-        when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+        when(mMockPM.queryIntentServicesAsUser(
+                argThat(argument -> ImsService.SERVICE_INTERFACE.equals(
+                        argument.getAction())), anyInt(), anyInt()))
+                .thenReturn(info);
         ImsServiceController deviceController = mock(ImsServiceController.class);
         ImsServiceController carrierController = mock(ImsServiceController.class);
         setImsServiceControllerFactory(deviceController, carrierController);
@@ -504,19 +539,20 @@
         setupResolver(2/*numSlots*/);
         List<ResolveInfo> info = new ArrayList<>();
         Set<String> deviceFeatures = new HashSet<>();
-        deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
         deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
         deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
         // Set the carrier override package for slot 0
         setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
         Set<String> carrierFeatures = new HashSet<>();
-        // Carrier service doesn't support the emergency voice feature.
-        carrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        // Carrier service doesn't support the voice feature.
         carrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
         // Use device default package, which will load the ImsService that the device provides
         info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
         info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
-        when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+        when(mMockPM.queryIntentServicesAsUser(
+                argThat(argument -> ImsService.SERVICE_INTERFACE.equals(
+                        argument.getAction())), anyInt(), anyInt()))
+                .thenReturn(info);
         ImsServiceController deviceController = mock(ImsServiceController.class);
         ImsServiceController carrierController = mock(ImsServiceController.class);
         setImsServiceControllerFactory(deviceController, carrierController);
@@ -559,7 +595,6 @@
                 convertToHashSet(newCarrierFeatures, 0);
         verify(carrierController).changeImsServiceFeatures(newCarrierFeatureSet);
         Set<String> newDeviceFeatures = new HashSet<>();
-        newDeviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
         newDeviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
         newDeviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
         HashSet<Pair<Integer, Integer>> newDeviceFeatureSet = convertToHashSet(newDeviceFeatures,
@@ -578,14 +613,16 @@
         setupResolver(2/*numSlots*/);
         List<ResolveInfo> info = new ArrayList<>();
         Set<String> deviceFeatures = new HashSet<>();
-        deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
         deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
         deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
         // Set the carrier override package for slot 0
         setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
         // Use device default package, which will load the ImsService that the device provides
         info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
-        when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+        when(mMockPM.queryIntentServicesAsUser(
+                argThat(argument -> (argument == null || ImsService.SERVICE_INTERFACE
+                        .equals(argument.getAction()))), anyInt(), anyInt()))
+                .thenReturn(info);
         ImsServiceController deviceController = mock(ImsServiceController.class);
         ImsServiceController carrierController = mock(ImsServiceController.class);
         setImsServiceControllerFactory(deviceController, carrierController);
@@ -594,11 +631,13 @@
         waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
 
         Set<String> carrierFeatures = new HashSet<>();
-        // Carrier service doesn't support the emergency voice feature.
-        carrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        // Carrier service doesn't support the voice feature.
         carrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
         info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
-        when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+        when(mMockPM.queryIntentServicesAsUser(
+                argThat(argument -> (argument == null || ImsService.SERVICE_INTERFACE
+                        .equals(argument.getAction()))), anyInt(), anyInt()))
+                .thenReturn(info);
 
         // Tell the package manager that a new carrier app is installed
         Intent addPackageIntent = new Intent();
@@ -629,14 +668,12 @@
         setupResolver(2/*numSlots*/);
         List<ResolveInfo> info = new ArrayList<>();
         Set<String> deviceFeatures = new HashSet<>();
-        deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
         deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
         deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
         // Set the carrier override package for slot 0
         setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
         Set<String> carrierFeatures = new HashSet<>();
-        // Carrier service doesn't support the emergency voice feature.
-        carrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        // Carrier service doesn't support the voice feature.
         carrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
         info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
         // Use device default package, which will load the ImsService that the device provides
@@ -680,14 +717,12 @@
         setupResolver(2/*numSlots*/);
         List<ResolveInfo> info = new ArrayList<>();
         Set<String> deviceFeatures = new HashSet<>();
-        deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
         deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
         deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
         // Set the carrier override package for slot 0
         setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
         Set<String> carrierFeatures = new HashSet<>();
-        // Carrier service doesn't support the emergency voice feature.
-        carrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        // Carrier service doesn't support the voice feature.
         carrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
         info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
         // Use device default package, which will load the ImsService that the device provides
@@ -726,17 +761,16 @@
         setupResolver(2/*numSlots*/);
         List<ResolveInfo> info = new ArrayList<>();
         Set<String> deviceFeatures = new HashSet<>();
-        deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
         deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
         deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
         // Set the carrier override package for slot 0
         setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
         Set<String> carrierFeatures1 = new HashSet<>();
-        // Carrier service doesn't support the emergency voice feature.
+        // Carrier service 1
         carrierFeatures1.add(ImsResolver.METADATA_MMTEL_FEATURE);
         carrierFeatures1.add(ImsResolver.METADATA_RCS_FEATURE);
         Set<String> carrierFeatures2 = new HashSet<>();
-        // Carrier service doesn't support the emergency voice feature.
+        // Carrier service 2 doesn't support the voice feature.
         carrierFeatures2.add(ImsResolver.METADATA_RCS_FEATURE);
         info.add(getResolveInfo(TEST_CARRIER_2_DEFAULT_NAME, carrierFeatures2, true));
         info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures1, true));
@@ -787,7 +821,7 @@
         }
 
         mTestImsResolver = new ImsResolver(mMockContext, TEST_DEVICE_DEFAULT_NAME.getPackageName(),
-                numSlots);
+                numSlots, true);
 
         ArgumentCaptor<BroadcastReceiver> packageBroadcastCaptor =
                 ArgumentCaptor.forClass(BroadcastReceiver.class);
@@ -803,36 +837,58 @@
 
     private void setImsServiceControllerFactory(ImsServiceController deviceController,
             ImsServiceController carrierController) {
-        mTestImsResolver.setImsServiceControllerFactory((context, componentName) -> {
-            if (TEST_DEVICE_DEFAULT_NAME.getPackageName().equals(componentName.getPackageName())) {
-                when(deviceController.getComponentName()).thenReturn(componentName);
-                return deviceController;
-            } else if (TEST_CARRIER_DEFAULT_NAME.getPackageName().equals(
-                    componentName.getPackageName())) {
-                when(carrierController.getComponentName()).thenReturn(componentName);
-                return carrierController;
-            }
-            return null;
-        });
+        mTestImsResolver.setImsServiceControllerFactory(
+                new ImsResolver.ImsServiceControllerFactory() {
+                    @Override
+                    public String getServiceInterface() {
+                        return ImsService.SERVICE_INTERFACE;
+                    }
+
+                    @Override
+                    public ImsServiceController create(Context context, ComponentName componentName,
+                            ImsServiceController.ImsServiceControllerCallbacks callbacks) {
+                        if (TEST_DEVICE_DEFAULT_NAME.getPackageName().equals(
+                                componentName.getPackageName())) {
+                            when(deviceController.getComponentName()).thenReturn(componentName);
+                            return deviceController;
+                        } else if (TEST_CARRIER_DEFAULT_NAME.getPackageName().equals(
+                                componentName.getPackageName())) {
+                            when(carrierController.getComponentName()).thenReturn(componentName);
+                            return carrierController;
+                        }
+                        return null;
+                    }
+                });
     }
 
     private void setImsServiceControllerFactory(ImsServiceController deviceController,
             ImsServiceController carrierController1, ImsServiceController carrierController2) {
-        mTestImsResolver.setImsServiceControllerFactory((context, componentName) -> {
-            if (TEST_DEVICE_DEFAULT_NAME.getPackageName().equals(componentName.getPackageName())) {
-                when(deviceController.getComponentName()).thenReturn(componentName);
-                return deviceController;
-            } else if (TEST_CARRIER_DEFAULT_NAME.getPackageName().equals(
-                    componentName.getPackageName())) {
-                when(carrierController1.getComponentName()).thenReturn(componentName);
-                return carrierController1;
-            } else if (TEST_CARRIER_2_DEFAULT_NAME.getPackageName().equals(
-                    componentName.getPackageName())) {
-                when(carrierController2.getComponentName()).thenReturn(componentName);
-                return carrierController2;
-            }
-            return null;
-        });
+        mTestImsResolver.setImsServiceControllerFactory(
+                new ImsResolver.ImsServiceControllerFactory() {
+                    @Override
+                    public String getServiceInterface() {
+                        return ImsService.SERVICE_INTERFACE;
+                    }
+
+                    @Override
+                    public ImsServiceController create(Context context, ComponentName componentName,
+                            ImsServiceController.ImsServiceControllerCallbacks callbacks) {
+                        if (TEST_DEVICE_DEFAULT_NAME.getPackageName().equals(
+                                componentName.getPackageName())) {
+                            when(deviceController.getComponentName()).thenReturn(componentName);
+                            return deviceController;
+                        } else if (TEST_CARRIER_DEFAULT_NAME.getPackageName().equals(
+                                componentName.getPackageName())) {
+                            when(carrierController1.getComponentName()).thenReturn(componentName);
+                            return carrierController1;
+                        } else if (TEST_CARRIER_2_DEFAULT_NAME.getPackageName().equals(
+                                componentName.getPackageName())) {
+                            when(carrierController2.getComponentName()).thenReturn(componentName);
+                            return carrierController2;
+                        }
+                        return null;
+                    }
+                });
     }
 
 
@@ -850,12 +906,10 @@
 
     private int metadataStringToFeature(String f) {
         switch (f) {
-            case ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE:
-                return ImsFeature.EMERGENCY_MMTEL;
             case ImsResolver.METADATA_MMTEL_FEATURE:
-                return ImsFeature.MMTEL;
+                return ImsFeature.FEATURE_MMTEL;
             case ImsResolver.METADATA_RCS_FEATURE:
-                return ImsFeature.RCS;
+                return ImsFeature.FEATURE_RCS;
         }
         return -1;
     }
@@ -868,17 +922,17 @@
         for (String f : features) {
             switch (f) {
                 case ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE:
-                    if (!sInfo.supportedFeatures.contains(ImsFeature.EMERGENCY_MMTEL)) {
+                    if (!sInfo.supportedFeatures.contains(ImsFeature.FEATURE_EMERGENCY_MMTEL)) {
                         return false;
                     }
                     break;
                 case ImsResolver.METADATA_MMTEL_FEATURE:
-                    if (!sInfo.supportedFeatures.contains(ImsFeature.MMTEL)) {
+                    if (!sInfo.supportedFeatures.contains(ImsFeature.FEATURE_MMTEL)) {
                         return false;
                     }
                     break;
                 case ImsResolver.METADATA_RCS_FEATURE:
-                    if (!sInfo.supportedFeatures.contains(ImsFeature.RCS)) {
+                    if (!sInfo.supportedFeatures.contains(ImsFeature.FEATURE_RCS)) {
                         return false;
                     }
                     break;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
index 1902d1e..9a7f111 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
@@ -38,10 +38,10 @@
 import android.os.RemoteException;
 import android.support.test.filters.FlakyTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.ImsService;
 import android.util.Pair;
 
 import com.android.ims.internal.IImsServiceFeatureCallback;
-import com.android.internal.telephony.ims.ImsServiceController.RebindRetry;
 
 import org.junit.After;
 import org.junit.Before;
@@ -61,7 +61,8 @@
 @Ignore
 public class ImsServiceControllerTest extends ImsTestBase {
 
-    private static final RebindRetry REBIND_RETRY = new RebindRetry() {
+    private static final ImsServiceController.RebindRetry REBIND_RETRY =
+            new ImsServiceController.RebindRetry() {
         @Override
         public long getStartDelay() {
             return 50;
@@ -121,7 +122,7 @@
                 | Context.BIND_IMPORTANT;
         verify(mMockContext).bindService(intentCaptor.capture(), any(), eq(expectedFlags));
         Intent testIntent = intentCaptor.getValue();
-        assertEquals(ImsResolver.SERVICE_INTERFACE, testIntent.getAction());
+        assertEquals(ImsService.SERVICE_INTERFACE, testIntent.getAction());
         assertEquals(mTestComponentName, testIntent.getComponent());
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/TestImsServiceControllerAdapter.java b/tests/telephonytests/src/com/android/internal/telephony/ims/TestImsServiceControllerAdapter.java
index 22e114e..294d6f2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/TestImsServiceControllerAdapter.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/TestImsServiceControllerAdapter.java
@@ -17,12 +17,17 @@
 package com.android.internal.telephony.ims;
 
 import android.os.RemoteException;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.aidl.IImsRcsFeature;
+import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.aidl.IImsServiceController;
+import android.telephony.ims.aidl.IImsServiceControllerListener;
+import android.telephony.ims.stub.ImsConfigImplBase;
+import android.telephony.ims.stub.ImsFeatureConfiguration;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 
 import com.android.ims.internal.IImsFeatureStatusCallback;
-import com.android.ims.internal.IImsMMTelFeature;
-import com.android.ims.internal.IImsRcsFeature;
-import com.android.ims.internal.IImsRegistration;
-import com.android.ims.internal.IImsServiceController;
 
 import static org.mockito.Mockito.spy;
 
@@ -37,37 +42,58 @@
     public class ImsServiceControllerBinder extends IImsServiceController.Stub {
 
         @Override
-        public IImsMMTelFeature createEmergencyMMTelFeature(int slotId, IImsFeatureStatusCallback c)
-                throws RemoteException {
-            mStatusCallback = c;
-            return TestImsServiceControllerAdapter.this.createEmergencyMMTelFeature(slotId);
+        public void setListener(IImsServiceControllerListener l) {
         }
 
         @Override
-        public IImsMMTelFeature createMMTelFeature(int slotId, IImsFeatureStatusCallback c)
-                throws RemoteException {
-            mStatusCallback = c;
+        public IImsMmTelFeature createMmTelFeature(int slotId, IImsFeatureStatusCallback c) {
             return TestImsServiceControllerAdapter.this.createMMTelFeature(slotId);
         }
 
         @Override
-        public IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c)
-                throws RemoteException {
-            mStatusCallback = c;
+        public IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) {
             return TestImsServiceControllerAdapter.this.createRcsFeature(slotId);
         }
 
         @Override
-        public void removeImsFeature(int slotId, int feature, IImsFeatureStatusCallback c)
+        public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
                 throws RemoteException {
-            TestImsServiceControllerAdapter.this.removeImsFeature(slotId, feature);
+            TestImsServiceControllerAdapter.this.removeImsFeature(slotId, featureType);
         }
 
         @Override
-        public IImsRegistration getRegistration(int i) throws RemoteException {
+        public ImsFeatureConfiguration querySupportedImsFeatures() {
             return null;
         }
 
+        @Override
+        public void notifyImsServiceReadyForFeatureCreation() {
+        }
+
+        @Override
+        public void notifyImsFeatureReady(int slotId, int featureType)
+                throws RemoteException {
+        }
+
+        @Override
+        public IImsConfig getConfig(int slotId) throws RemoteException {
+            return new ImsConfigImplBase().getIImsConfig();
+        }
+
+        @Override
+        public IImsRegistration getRegistration(int slotId) throws RemoteException {
+            return new ImsRegistrationImplBase().getBinder();
+        }
+
+        @Override
+        public void enableIms(int slotId) {
+        }
+
+        @Override
+        public void disableIms(int slotId) {
+
+        }
+
     }
 
     private ImsServiceControllerBinder mBinder;
@@ -81,12 +107,7 @@
     }
 
     // Used by Mockito for verification that this method is being called in spy
-    public IImsMMTelFeature createEmergencyMMTelFeature(int slotId) {
-        return null;
-    }
-
-    // Used by Mockito for verification that this method is being called in spy
-    public IImsMMTelFeature createMMTelFeature(int slotId) {
+    public IImsMmTelFeature createMMTelFeature(int slotId) {
         return null;
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsCallTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsCallTest.java
index 120874b..2a2e38f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsCallTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsCallTest.java
@@ -17,14 +17,12 @@
 package com.android.internal.telephony.imsphone;
 
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
 import android.telephony.ServiceState;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.ims.ImsCall;
-import com.android.ims.ImsCallProfile;
-import com.android.internal.telephony.Call;
+import android.telephony.ims.ImsCallProfile;
+
 import com.android.internal.telephony.TelephonyTest;
 
 import org.junit.After;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsExternalCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsExternalCallTrackerTest.java
index 8baa6cd..80be96c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsExternalCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsExternalCallTrackerTest.java
@@ -23,8 +23,8 @@
 import android.net.Uri;
 import android.support.test.filters.FlakyTest;
 
-import com.android.ims.ImsCallProfile;
-import com.android.ims.ImsExternalCallState;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsExternalCallState;
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.Connection;
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTest.java
index 5f88bb1..c602108 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTest.java
@@ -26,8 +26,8 @@
 
 import android.support.test.filters.FlakyTest;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.telephony.ims.ImsStreamMediaProfile;
 
-import com.android.ims.ImsStreamMediaProfile;
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.TelephonyTest;
 
@@ -42,7 +42,7 @@
     ImsPhoneConnection mConnection1;
     @Mock
     ImsPhoneConnection mConnection2;
-    @Mock
+
     ImsStreamMediaProfile mMediaProfile;
 
     private ImsPhoneCall mImsCallUT;
@@ -53,6 +53,7 @@
         replaceInstance(ImsPhoneCallTracker.class, "mPhone", mImsCT, mImsPhone);
 
         mImsCallUT = new ImsPhoneCall(mImsCT, ImsPhoneCall.CONTEXT_FOREGROUND);
+        mMediaProfile = new ImsStreamMediaProfile();
     }
 
     @After
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
index 6f9b7de..4cda187 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
@@ -29,14 +29,13 @@
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.isNull;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
-import android.app.PendingIntent;
 import android.content.Context;
-import android.content.Intent;
 import android.content.SharedPreferences;
 import android.os.Bundle;
 import android.os.Handler;
@@ -47,18 +46,18 @@
 import android.telephony.DisconnectCause;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.ims.ImsCall;
-import com.android.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallProfile;
 import com.android.ims.ImsConfig;
-import com.android.ims.ImsConnectionStateListener;
 import com.android.ims.ImsException;
-import com.android.ims.ImsManager;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.ImsServiceClass;
-import com.android.ims.ImsStreamMediaProfile;
-import com.android.ims.internal.ImsCallSession;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsStreamMediaProfile;
+import com.android.ims.internal.IImsCallSession;
+import android.telephony.ims.ImsCallSession;
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.CallStateException;
 import com.android.internal.telephony.CommandsInterface;
@@ -79,7 +78,8 @@
 public class ImsPhoneCallTrackerTest extends TelephonyTest {
     private ImsPhoneCallTracker mCTUT;
     private ImsCTHandlerThread mImsCTHandlerThread;
-    private ImsConnectionStateListener mImsConnectionStateListener;
+    private MmTelFeature.Listener mMmTelListener;
+    private ImsFeature.CapabilityCallback mCapabilityCallback;
     private ImsCall.Listener mImsCallListener;
     private ImsCall mImsCall;
     private ImsCall mSecondImsCall;
@@ -89,6 +89,10 @@
     private ImsCallSession mImsCallSession;
     @Mock
     private SharedPreferences mSharedPreferences;
+    @Mock
+    private ImsPhoneConnection.Listener mImsPhoneConnectionListener;
+    @Mock
+    private ImsConfig mImsConfig;
     private Handler mCTHander;
 
     private class ImsCTHandlerThread extends HandlerThread {
@@ -170,20 +174,14 @@
         mSecondImsCall = spy(new ImsCall(mContext, mImsCallProfile));
         imsCallMocking(mImsCall);
         imsCallMocking(mSecondImsCall);
-        doReturn(ImsFeature.STATE_READY).when(mImsManager).getImsServiceStatus();
-        doReturn(mImsCallProfile).when(mImsManager).createCallProfile(eq(mServiceId),
-                anyInt(), anyInt());
+        doReturn(ImsFeature.STATE_READY).when(mImsManager).getImsServiceState();
+        doReturn(mImsCallProfile).when(mImsManager).createCallProfile(anyInt(), anyInt());
 
-        //cache the listener
-        doAnswer(new Answer<Integer>() {
-            @Override
-            public Integer answer(InvocationOnMock invocation) throws Throwable {
-                mImsConnectionStateListener =
-                        (ImsConnectionStateListener) invocation.getArguments()[2];
-                return mServiceId;
-            }
-        }).when(mImsManager).open(anyInt(), (PendingIntent) any(),
-                (ImsConnectionStateListener) any());
+        doAnswer(invocation -> {
+            mMmTelListener = (MmTelFeature.Listener) invocation.getArguments()[0];
+            return null;
+        }).when(mImsManager).open(any(MmTelFeature.Listener.class));
+
 
         doAnswer(new Answer<ImsCall>() {
             @Override
@@ -192,18 +190,27 @@
                         (ImsCall.Listener) invocation.getArguments()[2];
                 return mImsCall;
             }
-        }).when(mImsManager).takeCall(eq(mServiceId), (Intent) any(), (ImsCall.Listener) any());
+        }).when(mImsManager).takeCall(any(), any(), any());
 
         doAnswer(new Answer<ImsCall>() {
             @Override
             public ImsCall answer(InvocationOnMock invocation) throws Throwable {
-                mImsCallListener =
-                        (ImsCall.Listener) invocation.getArguments()[3];
+                mImsCallListener = (ImsCall.Listener) invocation.getArguments()[2];
+                mSecondImsCall.setListener(mImsCallListener);
+
                 return mSecondImsCall;
             }
-        }).when(mImsManager).makeCall(eq(mServiceId), eq(mImsCallProfile), (String []) any(),
+        }).when(mImsManager).makeCall(eq(mImsCallProfile), (String []) any(),
                 (ImsCall.Listener) any());
 
+        doAnswer(invocation -> {
+            mCapabilityCallback = (ImsFeature.CapabilityCallback) invocation.getArguments()[0];
+            return mCapabilityCallback;
+
+        }).when(mImsManager).addCapabilitiesCallback(any(ImsFeature.CapabilityCallback.class));
+
+        doReturn(mImsConfig).when(mImsManager).getConfigInterface();
+
         mImsCTHandlerThread = new ImsCTHandlerThread(this.getClass().getSimpleName());
         mImsCTHandlerThread.start();
 
@@ -223,28 +230,28 @@
     @Test
     @SmallTest
     public void testImsFeatureCapabilityChange() {
-        int[] featureEnableArray = {-1, -1, -1, -1, -1, -1},
-                featureDisableArray = {-1, -1, -1, -1, -1, -1};
+        doReturn(ImsRegistrationImplBase.REGISTRATION_TECH_LTE).when(
+                mImsManager).getRegistrationTech();
         assertFalse(mCTUT.isVolteEnabled());
         assertFalse(mCTUT.isVideoCallEnabled());
-        //enable VoLTE feature
-        featureEnableArray[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE] =
-                ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE;
-        mImsConnectionStateListener.onFeatureCapabilityChanged(ImsServiceClass.MMTEL,
-                featureEnableArray,
-                featureDisableArray);
+
+        // enable only Voice
+        ImsFeature.Capabilities caps = new ImsFeature.Capabilities();
+        caps.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
+        mCapabilityCallback.onCapabilitiesStatusChanged(caps);
         waitForHandlerAction(mCTHander, 1000);
+
         assertTrue(mCTUT.isVolteEnabled());
         assertFalse(mCTUT.isVideoCallEnabled());
         // video call not enabled
         verify(mImsPhone, times(0)).notifyForVideoCapabilityChanged(anyBoolean());
         verify(mImsPhone, times(1)).onFeatureCapabilityChanged();
+
         // enable video call
-        featureEnableArray[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE] =
-                ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE;
-        mImsConnectionStateListener.onFeatureCapabilityChanged(ImsServiceClass.MMTEL,
-                featureEnableArray,
-                featureDisableArray);
+        ImsFeature.Capabilities capsVideo = new ImsFeature.Capabilities();
+        capsVideo.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
+        capsVideo.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO);
+        mCapabilityCallback.onCapabilitiesStatusChanged(capsVideo);
         waitForHandlerAction(mCTHander, 1000);
         assertTrue(mCTUT.isVideoCallEnabled());
         verify(mImsPhone, times(1)).notifyForVideoCapabilityChanged(eq(true));
@@ -256,8 +263,7 @@
         assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
         assertFalse(mCTUT.mRingingCall.isRinging());
         // mock a MT call
-        Intent mIntent = new Intent(ImsManager.ACTION_IMS_INCOMING_CALL);
-        mContext.sendBroadcast(mIntent);
+        mMmTelListener.onIncomingCall(mock(IImsCallSession.class), Bundle.EMPTY);
         verify(mImsPhone, times(1)).notifyNewRingingConnection((Connection) any());
         verify(mImsPhone, times(1)).notifyIncomingRing();
         assertEquals(PhoneConstants.State.RINGING, mCTUT.getState());
@@ -326,14 +332,13 @@
         assertEquals(PhoneConstants.State.OFFHOOK, mCTUT.getState());
         // mock a new MT
         try {
-            doReturn(mSecondImsCall).when(mImsManager).takeCall(eq(0), (Intent) any(),
-                    (ImsCall.Listener) any());
+            doReturn(mSecondImsCall).when(mImsManager).takeCall(any(IImsCallSession.class),
+                    any(Bundle.class), any(ImsCall.Listener.class));
         } catch (Exception ex) {
             ex.printStackTrace();
             Assert.fail("unexpected exception thrown" + ex.getMessage());
         }
-        Intent mIntent = new Intent(ImsManager.ACTION_IMS_INCOMING_CALL);
-        mContext.sendBroadcast(mIntent);
+        mMmTelListener.onIncomingCall(mock(IImsCallSession.class), Bundle.EMPTY);
 
         verify(mImsPhone, times(2)).notifyNewRingingConnection((Connection) any());
         verify(mImsPhone, times(2)).notifyIncomingRing();
@@ -405,11 +410,11 @@
 
             ArgumentCaptor<ImsCallProfile> profileCaptor = ArgumentCaptor.forClass(
                     ImsCallProfile.class);
-            verify(mImsManager, times(1)).makeCall(eq(0), eq(mImsCallProfile),
-                    eq(new String[]{"+17005554141"}), (ImsCall.Listener) any());
+            verify(mImsManager, times(1)).makeCall(eq(mImsCallProfile),
+                    eq(new String[]{"+17005554141"}), any());
 
             // Because this is an emergency call, we expect caller id to be visible now.
-            verify(mImsCallProfile).setCallExtraInt(ImsCallProfile.EXTRA_OIR,
+            assertEquals(mImsCallProfile.getCallExtraInt(ImsCallProfile.EXTRA_OIR),
                     CommandsInterface.CLIR_SUPPRESSION);
         } catch (CallStateException cse) {
             cse.printStackTrace();
@@ -429,7 +434,7 @@
         assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
         try {
             mCTUT.dial("+17005554141", ImsCallProfile.CALL_TYPE_VOICE, null);
-            verify(mImsManager, times(1)).makeCall(eq(0), eq(mImsCallProfile),
+            verify(mImsManager, times(1)).makeCall(eq(mImsCallProfile),
                     eq(new String[]{"+17005554141"}), (ImsCall.Listener) any());
         } catch (Exception ex) {
             ex.printStackTrace();
@@ -456,7 +461,7 @@
         assertEquals(Call.State.IDLE, mCTUT.mBackgroundCall.getState());
         try {
             mCTUT.dial("+17005554141", ImsCallProfile.CALL_TYPE_VOICE, null);
-            verify(mImsManager, times(1)).makeCall(eq(mServiceId), eq(mImsCallProfile),
+            verify(mImsManager, times(1)).makeCall(eq(mImsCallProfile),
                     eq(new String[]{"+17005554141"}), (ImsCall.Listener) any());
         } catch (Exception ex) {
             ex.printStackTrace();
@@ -492,10 +497,9 @@
         verify(mImsCall, times(1)).sendDtmf(eq(PhoneNumberUtils.PAUSE), (Message) isNull());
         // mock a new MT
         try {
-            doReturn(mSecondImsCall).when(mImsManager).takeCall(eq(mServiceId), (Intent) any(),
-                    (ImsCall.Listener) any());
-            Intent mIntent = new Intent(ImsManager.ACTION_IMS_INCOMING_CALL);
-            mContext.sendBroadcast(mIntent);
+            doReturn(mSecondImsCall).when(mImsManager).takeCall(any(IImsCallSession.class),
+                    any(Bundle.class), any(ImsCall.Listener.class));
+            mMmTelListener.onIncomingCall(mock(IImsCallSession.class), Bundle.EMPTY);
             mCTUT.acceptCall(ImsCallProfile.CALL_TYPE_VOICE);
         } catch (Exception ex) {
             ex.printStackTrace();
@@ -529,7 +533,7 @@
     @SmallTest
     public void testDialImsServiceUnavailable() throws ImsException {
         doThrow(new ImsException("Test Exception", ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN)).when(
-                mImsManager).createCallProfile(anyInt(), anyInt(), anyInt());
+                mImsManager).createCallProfile(anyInt(), anyInt());
         mCTUT.mRetryTimeout = () -> 0; //ms
         assertEquals(Call.State.IDLE, mCTUT.mForegroundCall.getState());
         assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
@@ -542,12 +546,12 @@
 
         // wait for handler to process ImsService connection retry
         waitForHandlerAction(mCTHander, 1000); // 1 second timeout
-        verify(mImsManager, never()).makeCall(anyInt(), nullable(ImsCallProfile.class),
+        verify(mImsManager, never()).makeCall(nullable(ImsCallProfile.class),
                 eq(new String[]{"+17005554141"}), nullable(ImsCall.Listener.class));
         // Make sure that open is called in ImsPhoneCallTracker when it was first connected and
         // again after retry.
-        verify(mImsManager, times(2)).open(anyInt(), nullable(PendingIntent.class),
-                nullable(ImsConnectionStateListener.class));
+        verify(mImsManager, times(2)).open(
+                nullable(MmTelFeature.Listener.class));
     }
 
     @FlakyTest
@@ -567,8 +571,8 @@
         waitForHandlerAction(mCTHander, 100);
         // Make sure that open is called in ImsPhoneCallTracker to re-establish connection to
         // ImsService
-        verify(mImsManager, times(2)).open(anyInt(), nullable(PendingIntent.class),
-                nullable(ImsConnectionStateListener.class));
+        verify(mImsManager, times(2)).open(
+                nullable(MmTelFeature.Listener.class));
     }
 
     @Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneConnectionTest.java
index c089706..87d4467 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneConnectionTest.java
@@ -23,10 +23,10 @@
 import android.telephony.DisconnectCause;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.ServiceState;
+import android.telephony.ims.ImsCallProfile;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import com.android.ims.ImsCallProfile;
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.GsmCdmaCall;
@@ -159,10 +159,10 @@
         mConnectionUT = new ImsPhoneConnection(mImsPhone, mImsCall, mImsCT, mForeGroundCall, false);
         assertEquals(PhoneConstants.PRESENTATION_UNKNOWN, mConnectionUT.getNumberPresentation());
         assertEquals(PhoneConstants.PRESENTATION_UNKNOWN, mConnectionUT.getCnapNamePresentation());
-        doReturn(ImsCallProfile.OIR_PRESENTATION_PAYPHONE).when(mImsCallProfile)
-                .getCallExtraInt(eq(ImsCallProfile.EXTRA_CNAP));
-        doReturn(ImsCallProfile.OIR_PRESENTATION_NOT_RESTRICTED).when(mImsCallProfile)
-                .getCallExtraInt(eq(ImsCallProfile.EXTRA_OIR));
+        mImsCallProfile.setCallExtraInt(ImsCallProfile.EXTRA_CNAP,
+                ImsCallProfile.OIR_PRESENTATION_PAYPHONE);
+        mImsCallProfile.setCallExtraInt(ImsCallProfile.EXTRA_OIR,
+                ImsCallProfile.OIR_PRESENTATION_NOT_RESTRICTED);
 
         mConnectionUT.updateAddressDisplay(mImsCall);
         assertEquals(ImsCallProfile.OIRToPresentation(ImsCallProfile.OIR_PRESENTATION_PAYPHONE),
@@ -287,8 +287,7 @@
             mConnectionUT = new ImsPhoneConnection(mImsPhone, testAddress[0], mImsCT,
                     mForeGroundCall, false);
             mConnectionUT.setIsIncoming(true);
-            doReturn(testAddress[1]).when(mImsCallProfile)
-                    .getCallExtra(eq(ImsCallProfile.EXTRA_OI));
+            mImsCallProfile.setCallExtra(ImsCallProfile.EXTRA_OI, testAddress[1]);
             mConnectionUT.updateAddressDisplay(mImsCall);
             assertEquals(testAddress[2], mConnectionUT.getAddress());
         }
@@ -306,8 +305,7 @@
         mConnectionUT = new ImsPhoneConnection(mImsPhone, inputAddress, mImsCT, mForeGroundCall,
                 false);
         mConnectionUT.setIsIncoming(false);
-        doReturn(updateAddress).when(mImsCallProfile)
-                .getCallExtra(eq(ImsCallProfile.EXTRA_OI));
+        mImsCallProfile.setCallExtra(ImsCallProfile.EXTRA_OI, updateAddress);
         mConnectionUT.updateAddressDisplay(mImsCall);
         assertEquals(inputAddress, mConnectionUT.getAddress());
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
index 5bc4df8..25ff7f5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
@@ -52,10 +52,10 @@
 import android.telephony.ServiceState;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import com.android.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallProfile;
 import com.android.ims.ImsEcbmStateListener;
 import com.android.ims.ImsManager;
-import com.android.ims.ImsReasonInfo;
+import android.telephony.ims.ImsReasonInfo;
 import com.android.ims.ImsUtInterface;
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.CommandsInterface;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsRttTextHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsRttTextHandlerTest.java
index 146afd3..22d9f78 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsRttTextHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsRttTextHandlerTest.java
@@ -18,6 +18,7 @@
 
 import android.os.HandlerThread;
 import android.os.ParcelFileDescriptor;
+import android.support.test.filters.FlakyTest;
 import android.telecom.Connection;
 
 import com.android.internal.telephony.TelephonyTest;
@@ -122,6 +123,7 @@
      * Test that the text handler sends after enough characters have been sent from in-call
      * @throws Exception
      */
+    @FlakyTest
     @Test
     public void testSendAfterEnoughChars() throws Exception {
         // Register a read notifier
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
index 6d45db0..9d924ea 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
@@ -18,6 +18,7 @@
 
 import static android.telephony.ServiceState.RIL_RADIO_TECHNOLOGY_LTE;
 import static android.telephony.ServiceState.ROAMING_TYPE_DOMESTIC;
+
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DEACTIVATE_DATA_CALL;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SETUP_DATA_CALL;
@@ -27,23 +28,25 @@
 import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_IFNAME;
 import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_PCSCF_ADDRESS;
 import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_IPV4V6;
+
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.doReturn;
 
-import android.net.LinkAddress;
-import android.net.NetworkUtils;
+import android.hardware.radio.V1_0.SetupDataCallResult;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
-import android.telephony.data.DataCallResponse;
+import android.telephony.ims.ImsCallSession;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Base64;
 
-import com.android.ims.ImsConfig;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.internal.ImsCallSession;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsCallSession;
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.GsmCdmaConnection;
 import com.android.internal.telephony.PhoneConstants;
@@ -69,7 +72,6 @@
 import org.mockito.Mock;
 
 import java.lang.reflect.Method;
-import java.util.Arrays;
 
 public class TelephonyMetricsTest extends TelephonyTest {
 
@@ -77,9 +79,6 @@
     private ImsCallSession mImsCallSession;
 
     @Mock
-    private ImsReasonInfo mImsReasonInfo;
-
-    @Mock
     private ServiceState mServiceState;
 
     @Mock
@@ -89,15 +88,18 @@
 
     private UUSInfo mUusInfo;
 
+    private ImsReasonInfo mImsReasonInfo;
+
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
         mMetrics = new TelephonyMetrics();
         mUusInfo = new UUSInfo(1, 2, new byte[]{1, 2});
         doReturn("123").when(mImsCallSession).getCallId();
-        doReturn("extramessage").when(mImsReasonInfo).getExtraMessage();
-        doReturn(123).when(mImsReasonInfo).getCode();
-        doReturn(456).when(mImsReasonInfo).getExtraCode();
+        mImsReasonInfo = new ImsReasonInfo();
+        mImsReasonInfo.mExtraMessage = "extramessage";
+        mImsReasonInfo.mCode = 123;
+        mImsReasonInfo.mExtraCode = 456;
 
         doReturn(ROAMING_TYPE_DOMESTIC).when(mServiceState).getVoiceRoamingType();
         doReturn(ROAMING_TYPE_DOMESTIC).when(mServiceState).getDataRoamingType();
@@ -287,9 +289,11 @@
     public void testWriteImsSetFeatureValue() throws Exception {
         mMetrics.writeOnImsCallStart(mPhone.getPhoneId(), mImsCallSession);
         mMetrics.writeImsSetFeatureValue(mPhone.getPhoneId(),
-                ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE, 0, 1, 0);
+                MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+                ImsRegistrationImplBase.REGISTRATION_TECH_LTE, 1);
         mMetrics.writeImsSetFeatureValue(mPhone.getPhoneId(),
-                ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE, 0, 1, 0);
+                MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+                ImsRegistrationImplBase.REGISTRATION_TECH_LTE, 1);
         mMetrics.writePhoneState(mPhone.getPhoneId(), PhoneConstants.State.IDLE);
         TelephonyLog log = buildProto();
 
@@ -386,16 +390,21 @@
     @Test
     @SmallTest
     public void testWriteOnSetupDataCallResponse() throws Exception {
-        DataCallResponse response = new DataCallResponse(5, 6, 7, 8, "IPV4V6", FAKE_IFNAME,
-                Arrays.asList(new LinkAddress(NetworkUtils.numericToInetAddress(FAKE_ADDRESS), 0)),
-                Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_DNS)),
-                Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_GATEWAY)),
-                Arrays.asList(FAKE_PCSCF_ADDRESS),
-                1440);
-
+        SetupDataCallResult result = new SetupDataCallResult();
+        result.status = 5;
+        result.suggestedRetryTime = 6;
+        result.cid = 7;
+        result.active = 8;
+        result.type = "IPV4V6";
+        result.ifname = FAKE_IFNAME;
+        result.addresses = FAKE_ADDRESS;
+        result.dnses = FAKE_DNS;
+        result.gateways = FAKE_GATEWAY;
+        result.pcscf = FAKE_PCSCF_ADDRESS;
+        result.mtu = 1440;
 
         mMetrics.writeOnRilSolicitedResponse(mPhone.getPhoneId(), 1, 2,
-                RIL_REQUEST_SETUP_DATA_CALL, response);
+                RIL_REQUEST_SETUP_DATA_CALL, result);
         TelephonyLog log = buildProto();
 
         assertEquals(1, log.events.length);
@@ -655,13 +664,19 @@
     @Test
     @SmallTest
     public void testWriteOnImsCapabilities() throws Exception {
-        boolean[] caps1 = new boolean[]{true, false, true, false, true, false};
-        mMetrics.writeOnImsCapabilities(mPhone.getPhoneId(), caps1);
-        boolean[] caps2 = new boolean[]{true, false, true, false, true, false};
+        MmTelFeature.MmTelCapabilities caps1 = new MmTelFeature.MmTelCapabilities();
+        caps1.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
+        caps1.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT);
+        mMetrics.writeOnImsCapabilities(mPhone.getPhoneId(),
+                ImsRegistrationImplBase.REGISTRATION_TECH_LTE, caps1);
         // The duplicate one should be filtered out.
-        mMetrics.writeOnImsCapabilities(mPhone.getPhoneId(), caps2);
-        boolean[] caps3 = new boolean[]{false, true, false, true, false, true};
-        mMetrics.writeOnImsCapabilities(mPhone.getPhoneId(), caps3);
+        mMetrics.writeOnImsCapabilities(mPhone.getPhoneId(),
+                ImsRegistrationImplBase.REGISTRATION_TECH_LTE, caps1);
+        MmTelFeature.MmTelCapabilities caps2 = new MmTelFeature.MmTelCapabilities();
+        caps2.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO);
+        caps2.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT);
+        mMetrics.writeOnImsCapabilities(mPhone.getPhoneId(),
+                ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN, caps2);
         TelephonyLog log = buildProto();
 
         assertEquals(2, log.events.length);
@@ -671,21 +686,27 @@
         TelephonyEvent event = log.events[0];
 
         assertEquals(TelephonyEvent.Type.IMS_CAPABILITIES_CHANGED, event.type);
-        assertEquals(caps1[0], event.imsCapabilities.voiceOverLte);
-        assertEquals(caps1[1], event.imsCapabilities.videoOverLte);
-        assertEquals(caps1[2], event.imsCapabilities.voiceOverWifi);
-        assertEquals(caps1[3], event.imsCapabilities.videoOverWifi);
-        assertEquals(caps1[4], event.imsCapabilities.utOverLte);
-        assertEquals(caps1[5], event.imsCapabilities.utOverWifi);
+        assertEquals(caps1.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE),
+                event.imsCapabilities.voiceOverLte);
+        assertEquals(caps1.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO),
+                event.imsCapabilities.videoOverLte);
+        assertEquals(caps1.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT),
+                event.imsCapabilities.utOverLte);
+        assertEquals(false, event.imsCapabilities.voiceOverWifi);
+        assertEquals(false, event.imsCapabilities.videoOverWifi);
+        assertEquals(false, event.imsCapabilities.utOverWifi);
 
         event = log.events[1];
 
         assertEquals(TelephonyEvent.Type.IMS_CAPABILITIES_CHANGED, event.type);
-        assertEquals(caps3[0], event.imsCapabilities.voiceOverLte);
-        assertEquals(caps3[1], event.imsCapabilities.videoOverLte);
-        assertEquals(caps3[2], event.imsCapabilities.voiceOverWifi);
-        assertEquals(caps3[3], event.imsCapabilities.videoOverWifi);
-        assertEquals(caps3[4], event.imsCapabilities.utOverLte);
-        assertEquals(caps3[5], event.imsCapabilities.utOverWifi);
+        assertEquals(caps2.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE),
+                event.imsCapabilities.voiceOverWifi);
+        assertEquals(caps2.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO),
+                event.imsCapabilities.videoOverWifi);
+        assertEquals(caps2.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT),
+                event.imsCapabilities.utOverWifi);
+        assertEquals(false, event.imsCapabilities.voiceOverLte);
+        assertEquals(false, event.imsCapabilities.videoOverLte);
+        assertEquals(false, event.imsCapabilities.utOverLte);
     }
 }