Merge "Set additional info for LaunchBrowser error case."
diff --git a/Android.mk b/Android.mk
index c5668fa..915ad01 100644
--- a/Android.mk
+++ b/Android.mk
@@ -26,13 +26,16 @@
 	$(call all-proto-files-under, proto)
 
 LOCAL_JAVA_LIBRARIES := voip-common ims-common services
-LOCAL_STATIC_JAVA_LIBRARIES := android.hardware.radio-V1.1-java-static \
-    android.hardware.radio.deprecated-V1.0-java-static
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android.hardware.radio-V1.0-java \
+    android.hardware.radio-V1.1-java \
+    android.hardware.radio.deprecated-V1.0-java \
+    android.hidl.base-V1.0-java
 
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE := telephony-common
 LOCAL_PROTOC_OPTIMIZE_TYPE := nano
-LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors,store_unknown_fields=true,enum_style=java
+LOCAL_PROTO_JAVA_OUTPUT_PARAMS := store_unknown_fields=true,enum_style=java
 
 LOCAL_JARJAR_RULES := $(LOCAL_PATH)/jarjar-rules.txt
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/jarjar-rules.txt
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..0786c0e
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,7 @@
+amitmahajan@google.com
+breadley@google.com
+fionaxu@google.com
+jackyu@google.com
+hallliu@google.com
+rgreenwalt@google.com
+tgunn@google.com
diff --git a/proto/telephony.proto b/proto/telephony.proto
index 70d210b..1f1a2bb 100644
--- a/proto/telephony.proto
+++ b/proto/telephony.proto
@@ -95,13 +95,13 @@
 // Telephony related user settings
 message TelephonySettings {
 
-  // NETWORK_MODE_* See ril.h RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE
+  // NETWORK_MODE_* See ril.h PREF_NET_TYPE_XXXX
   enum RilNetworkMode {
 
     // Mode is unknown.
     NETWORK_MODE_UNKNOWN = 0;
 
-    // GSM/WCDMA (WCDMA preferred)
+    // GSM/WCDMA (WCDMA preferred). Note the following values are all off by 1.
     NETWORK_MODE_WCDMA_PREF = 1;
 
     // GSM only
@@ -236,6 +236,9 @@
   // Roaming type
   enum RoamingType {
 
+    // Unknown. The default value. Different from ROAMING_TYPE_UNKNOWN.
+    UNKNOWN = -1;
+
     // In home network
     ROAMING_TYPE_NOT_ROAMING = 0;
 
@@ -257,21 +260,26 @@
   optional TelephonyOperator data_operator = 2;
 
   // Current voice network roaming type
-  optional RoamingType voice_roaming_type = 3;
+  optional RoamingType voice_roaming_type = 3 [default = UNKNOWN];
 
   // Current data network roaming type
-  optional RoamingType data_roaming_type = 4;
+  optional RoamingType data_roaming_type = 4 [default = UNKNOWN];
 
   // Current voice radio technology
-  optional RadioAccessTechnology voice_rat = 5;
+  optional RadioAccessTechnology voice_rat = 5 [default = UNKNOWN];
 
   // Current data radio technology
-  optional RadioAccessTechnology data_rat = 6;
+  optional RadioAccessTechnology data_rat = 6 [default = UNKNOWN];
 }
 
 // Radio access families
 enum RadioAccessTechnology {
 
+  // This is the default value. Different from RAT_UNKNOWN.
+  UNKNOWN = -1;
+
+  // Airplane mode, out of service, or when the modem cannot determine
+  // the RAT.
   RAT_UNKNOWN = 0;
 
   RAT_GPRS = 1;
@@ -377,6 +385,7 @@
   // type is unknown.
   RIL_E_UNKNOWN = 0;
 
+  // Note the following values are all off by 1.
   RIL_E_SUCCESS = 1;
 
   // If radio did not start or is resetting
@@ -458,8 +467,12 @@
   // SS request modified to different SS request
   RIL_E_SS_MODIFIED_TO_SS = 28;
 
-  // LCE service not supported(36 in RILConstants.java)
-  RIL_E_LCE_NOT_SUPPORTED = 36;
+  // LCE service not supported(36 in RILConstants.java. This is a mistake.
+  // The value should be off by 1 ideally.)
+  RIL_E_LCE_NOT_SUPPORTED = 36 [deprecated=true];
+
+  // LCE service not supported
+  RIL_E_LCE_NOT_SUPPORTED_NEW = 37;
 }
 
 // PDP_type values in TS 27.007 section 10.1.1.
@@ -561,7 +574,7 @@
     }
 
     // Radio technology to use
-    optional RadioAccessTechnology rat = 1;
+    optional RadioAccessTechnology rat = 1 [default = UNKNOWN];
 
     // optional RIL_DataProfile
     optional RilDataProfile data_profile = 2;
@@ -576,8 +589,7 @@
   // RIL response to RilSetupDataCall
   message RilSetupDataCallResponse {
 
-    // Copy of enum RIL_DataCallFailCause defined at
-    // https://cs.corp.google.com/android/hardware/ril/include/telephony/ril.h
+    // Copy of enum RIL_DataCallFailCause defined at ril.h
     enum RilDataCallFailCause {
 
       // Failure reason is unknown.
@@ -1097,10 +1109,10 @@
     optional ImsReasonInfo reason_info = 18;
 
     // Original access technology
-    optional RadioAccessTechnology src_access_tech = 19;
+    optional RadioAccessTechnology src_access_tech = 19 [default = UNKNOWN];
 
     // New access technology
-    optional RadioAccessTechnology target_access_tech = 20;
+    optional RadioAccessTechnology target_access_tech = 20 [default = UNKNOWN];
 
     // NITZ time in milliseconds
     optional int64 nitz_timestamp_millis = 21;
diff --git a/src/java/com/android/internal/telephony/AppSmsManager.java b/src/java/com/android/internal/telephony/AppSmsManager.java
new file mode 100644
index 0000000..f2a783f
--- /dev/null
+++ b/src/java/com/android/internal/telephony/AppSmsManager.java
@@ -0,0 +1,177 @@
+/*
+ * 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 android.app.AppOpsManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.provider.Telephony.Sms.Intents;
+import android.telephony.SmsMessage;
+import android.util.ArrayMap;
+import android.util.Base64;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.security.SecureRandom;
+import java.util.Map;
+
+
+/**
+ *  Manager for app specific incoming SMS requests. This can be used to implement SMS based
+ *  communication channels (e.g. for SMS based phone number verification) without needing the
+ *  {@link Manifest.permission#RECEIVE_SMS} permission.
+ *
+ *  {@link #createAppSpecificSmsRequest} allows an application to provide a {@link PendingIntent}
+ *  that is triggered when an incoming SMS is received that contains the provided token.
+ */
+public class AppSmsManager {
+    private static final String LOG_TAG = "AppSmsManager";
+
+    private final SecureRandom mRandom;
+    private final Context mContext;
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private final Map<String, AppRequestInfo> mTokenMap;
+    @GuardedBy("mLock")
+    private final Map<String, AppRequestInfo> mPackageMap;
+
+    public AppSmsManager(Context context) {
+        mRandom = new SecureRandom();
+        mTokenMap = new ArrayMap<>();
+        mPackageMap = new ArrayMap<>();
+        mContext = context;
+    }
+
+    /**
+     * Create an app specific incoming SMS request for the the calling package.
+     *
+     * This method returns a token that if included in a subsequent incoming SMS message the
+     * {@link Intents.SMS_RECEIVED_ACTION} intent will be delivered only to the calling package and
+     * will not require the application have the {@link Manifest.permission#RECEIVE_SMS} permission.
+     *
+     * An app can only have one request at a time, if the app already has a request it will be
+     * dropped and the new one will be added.
+     *
+     * @return Token to include in an SMS to have it delivered directly to the app.
+     */
+    public String createAppSpecificSmsToken(String callingPkg, PendingIntent intent) {
+        // Check calling uid matches callingpkg.
+        AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+        appOps.checkPackage(Binder.getCallingUid(), callingPkg);
+
+        // Generate a nonce to store the request under.
+        String token = generateNonce();
+        synchronized (mLock) {
+            // Only allow one request in flight from a package.
+            if (mPackageMap.containsKey(callingPkg)) {
+                removeRequestLocked(mPackageMap.get(callingPkg));
+            }
+            // Store state.
+            AppRequestInfo info = new AppRequestInfo(callingPkg, intent, token);
+            addRequestLocked(info);
+        }
+        return token;
+    }
+
+    /**
+     * Handle an incoming SMS_DELIVER_ACTION intent if it is an app-only SMS.
+     */
+    public boolean handleSmsReceivedIntent(Intent intent) {
+        // Sanity check the action.
+        if (intent.getAction() != Intents.SMS_DELIVER_ACTION) {
+            Log.wtf(LOG_TAG, "Got intent with incorrect action: " + intent.getAction());
+            return false;
+        }
+
+        synchronized (mLock) {
+            AppRequestInfo info = findAppRequestInfoSmsIntentLocked(intent);
+            if (info == null) {
+                // The message didn't contain a token -- nothing to do.
+                return false;
+            }
+            try {
+                Intent fillIn = new Intent();
+                fillIn.putExtras(intent.getExtras());
+                info.pendingIntent.send(mContext, 0, fillIn);
+            } catch (PendingIntent.CanceledException e) {
+                // The pending intent is canceled, send this SMS as normal.
+                removeRequestLocked(info);
+                return false;
+            }
+
+            removeRequestLocked(info);
+            return true;
+        }
+    }
+
+    private AppRequestInfo findAppRequestInfoSmsIntentLocked(Intent intent) {
+        SmsMessage[] messages = Intents.getMessagesFromIntent(intent);
+        if (messages == null) {
+            return null;
+        }
+        StringBuilder fullMessageBuilder = new StringBuilder();
+        for (SmsMessage message : messages) {
+            if (message.getMessageBody() == null) {
+                continue;
+            }
+            fullMessageBuilder.append(message.getMessageBody());
+        }
+
+        String fullMessage = fullMessageBuilder.toString();
+
+        // Look for any tokens in the full message.
+        for (String token : mTokenMap.keySet()) {
+            if (fullMessage.contains(token)) {
+                return mTokenMap.get(token);
+            }
+        }
+        return null;
+    }
+
+    private String generateNonce() {
+        byte[] bytes = new byte[8];
+        mRandom.nextBytes(bytes);
+        return Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING);
+    }
+
+    private void removeRequestLocked(AppRequestInfo info) {
+        mTokenMap.remove(info.token);
+        mPackageMap.remove(info.packageName);
+    }
+
+    private void addRequestLocked(AppRequestInfo info) {
+        mTokenMap.put(info.token, info);
+        mPackageMap.put(info.packageName, info);
+    }
+
+    private final class AppRequestInfo {
+        public final String packageName;
+        public final PendingIntent pendingIntent;
+        public final String token;
+
+        AppRequestInfo(String packageName, PendingIntent pendingIntent, String token) {
+            this.packageName = packageName;
+            this.pendingIntent = pendingIntent;
+            this.token = token;
+        }
+    }
+
+}
diff --git a/src/java/com/android/internal/telephony/CallFailCause.java b/src/java/com/android/internal/telephony/CallFailCause.java
index 8d2093e..ed39b4d 100644
--- a/src/java/com/android/internal/telephony/CallFailCause.java
+++ b/src/java/com/android/internal/telephony/CallFailCause.java
@@ -60,6 +60,10 @@
     int DIAL_MODIFIED_TO_SS   = 245;
     int DIAL_MODIFIED_TO_DIAL = 246;
 
+    //Emergency Redial
+    int EMERGENCY_TEMP_FAILURE = 325;
+    int EMERGENCY_PERM_FAILURE = 326;
+
     int CDMA_LOCKED_UNTIL_POWER_CYCLE  = 1000;
     int CDMA_DROP                      = 1001;
     int CDMA_INTERCEPT                 = 1002;
diff --git a/src/java/com/android/internal/telephony/CallManager.java b/src/java/com/android/internal/telephony/CallManager.java
index 4016217..2775fe6 100644
--- a/src/java/com/android/internal/telephony/CallManager.java
+++ b/src/java/com/android/internal/telephony/CallManager.java
@@ -1493,6 +1493,7 @@
      * <code>obj.result</code> will be an "MmiCode" object
      */
     public void registerForMmiComplete(Handler h, int what, Object obj){
+        Rlog.d(LOG_TAG, "registerForMmiComplete");
         mMmiCompleteRegistrants.addUnique(h, what, obj);
     }
 
@@ -2317,7 +2318,7 @@
                     mMmiInitiateRegistrants.notifyRegistrants((AsyncResult) msg.obj);
                     break;
                 case EVENT_MMI_COMPLETE:
-                    if (VDBG) Rlog.d(LOG_TAG, " handleMessage (EVENT_MMI_COMPLETE)");
+                    Rlog.d(LOG_TAG, "CallManager: handleMessage (EVENT_MMI_COMPLETE)");
                     mMmiCompleteRegistrants.notifyRegistrants((AsyncResult) msg.obj);
                     break;
                 case EVENT_ECM_TIMER_RESET:
diff --git a/src/java/com/android/internal/telephony/CallTracker.java b/src/java/com/android/internal/telephony/CallTracker.java
index ad64a4a..23874e2 100644
--- a/src/java/com/android/internal/telephony/CallTracker.java
+++ b/src/java/com/android/internal/telephony/CallTracker.java
@@ -16,10 +16,13 @@
 
 package com.android.internal.telephony;
 
+import android.content.Context;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Message;
+import android.os.PersistableBundle;
 import android.os.SystemProperties;
+import android.telephony.CarrierConfigManager;
 import android.text.TextUtils;
 
 import java.io.FileDescriptor;
@@ -203,8 +206,20 @@
         if (dialNumber == null) {
             return dialNumber;
         }
-        String[] convertMaps = phone.getContext().getResources().getStringArray(
-                com.android.internal.R.array.dial_string_replace);
+        String[] convertMaps = null;
+        CarrierConfigManager configManager = (CarrierConfigManager)
+                phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        PersistableBundle bundle = configManager.getConfig();
+        if (bundle != null) {
+            convertMaps =
+                    bundle.getStringArray(CarrierConfigManager.KEY_DIAL_STRING_REPLACE_STRING_ARRAY);
+        }
+        if (convertMaps == null) {
+            // By default no replacement is necessary
+            log("convertNumberIfNecessary convertMaps is null");
+            return dialNumber;
+        }
+
         log("convertNumberIfNecessary Roaming"
             + " convertMaps.length " + convertMaps.length
             + " dialNumber.length() " + dialNumber.length());
@@ -214,39 +229,30 @@
         }
 
         String[] entry;
-        String[] tmpArray;
         String outNumber = "";
-        boolean needConvert = false;
         for(String convertMap : convertMaps) {
             log("convertNumberIfNecessary: " + convertMap);
+            // entry format is  "dialStringToReplace:dialStringReplacement"
             entry = convertMap.split(":");
-            if (entry.length > 1) {
-                tmpArray = entry[1].split(",");
-                if (!TextUtils.isEmpty(entry[0]) && dialNumber.equals(entry[0])) {
-                    if (tmpArray.length >= 2 && !TextUtils.isEmpty(tmpArray[1])) {
-                        if (compareGid1(phone, tmpArray[1])) {
-                            needConvert = true;
-                        }
-                    } else if (outNumber.isEmpty()) {
-                        needConvert = true;
-                    }
-
-                    if (needConvert) {
-                        if(!TextUtils.isEmpty(tmpArray[0]) && tmpArray[0].endsWith("MDN")) {
-                            String mdn = phone.getLine1Number();
-                            if (!TextUtils.isEmpty(mdn) ) {
-                                if (mdn.startsWith("+")) {
-                                    outNumber = mdn;
-                                } else {
-                                    outNumber = tmpArray[0].substring(0, tmpArray[0].length() -3)
-                                            + mdn;
-                                }
+            if (entry != null && entry.length > 1) {
+                String dsToReplace = entry[0];
+                String dsReplacement = entry[1];
+                if (!TextUtils.isEmpty(dsToReplace) && dialNumber.equals(dsToReplace)) {
+                    // Needs to be converted
+                    if (!TextUtils.isEmpty(dsReplacement) && dsReplacement.endsWith("MDN")) {
+                        String mdn = phone.getLine1Number();
+                        if (!TextUtils.isEmpty(mdn)) {
+                            if (mdn.startsWith("+")) {
+                                outNumber = mdn;
+                            } else {
+                                outNumber = dsReplacement.substring(0, dsReplacement.length() -3)
+                                        + mdn;
                             }
-                        } else {
-                            outNumber = tmpArray[0];
                         }
-                        needConvert = false;
+                    } else {
+                        outNumber = dsReplacement;
                     }
+                    break;
                 }
             }
         }
@@ -290,6 +296,14 @@
     public abstract PhoneConstants.State getState();
     protected abstract void log(String msg);
 
+    /**
+     * Called when the call tracker should attempt to reconcile its calls against its underlying
+     * phone implementation and cleanup any stale calls.
+     */
+    public void cleanupCalls() {
+        // no base implementation
+    }
+
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("CallTracker:");
         pw.println(" mPendingOperations=" + mPendingOperations);
diff --git a/src/java/com/android/internal/telephony/CarrierActionAgent.java b/src/java/com/android/internal/telephony/CarrierActionAgent.java
index 6d2daf0..d67636b 100644
--- a/src/java/com/android/internal/telephony/CarrierActionAgent.java
+++ b/src/java/com/android/internal/telephony/CarrierActionAgent.java
@@ -27,6 +27,7 @@
 import android.os.RegistrantList;
 import android.provider.Settings;
 import android.telephony.Rlog;
+import android.telephony.TelephonyManager;
 import android.util.LocalLog;
 import android.util.Log;
 
@@ -56,6 +57,10 @@
     public static final int CARRIER_ACTION_SET_METERED_APNS_ENABLED      = 0;
     public static final int CARRIER_ACTION_SET_RADIO_ENABLED             = 1;
     public static final int CARRIER_ACTION_RESET                         = 2;
+    public static final int EVENT_APM_SETTINGS_CHANGED                   = 3;
+    public static final int EVENT_MOBILE_DATA_SETTINGS_CHANGED           = 4;
+    public static final int EVENT_DATA_ROAMING_OFF                       = 5;
+    public static final int EVENT_SIM_STATE_CHANGED                      = 6;
 
     /** Member variables */
     private final Phone mPhone;
@@ -77,37 +82,31 @@
             final String action = intent.getAction();
             final String iccState = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
             if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)){
-                if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(iccState) ||
-                        IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(iccState)) {
+                if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(iccState)) {
                     sendEmptyMessage(CARRIER_ACTION_RESET);
+                    String mobileData = Settings.Global.MOBILE_DATA;
+                    if (TelephonyManager.getDefault().getSimCount() != 1) {
+                        mobileData = mobileData + mPhone.getSubId();
+                    }
+                    mSettingsObserver.observe(Settings.Global.getUriFor(mobileData),
+                            EVENT_MOBILE_DATA_SETTINGS_CHANGED);
+                    mSettingsObserver.observe(
+                            Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON),
+                            EVENT_APM_SETTINGS_CHANGED);
+                } else if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(iccState)) {
+                    sendEmptyMessage(CARRIER_ACTION_RESET);
+                    mSettingsObserver.unobserve();
                 }
             }
         }
     };
 
-    private class SettingsObserver extends ContentObserver {
-        SettingsObserver() {
-            super(null);
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            if (Settings.Global.getInt(mPhone.getContext().getContentResolver(),
-                    Settings.Global.AIRPLANE_MODE_ON, 0) != 0) {
-                sendEmptyMessage(CARRIER_ACTION_RESET);
-            }
-        }
-    }
-
     /** Constructor */
     public CarrierActionAgent(Phone phone) {
         mPhone = phone;
         mPhone.getContext().registerReceiver(mReceiver,
                 new IntentFilter(TelephonyIntents.ACTION_SIM_STATE_CHANGED));
-        mSettingsObserver = new SettingsObserver();
-        mPhone.getContext().getContentResolver().registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON),
-                false, mSettingsObserver);
+        mSettingsObserver = new SettingsObserver(mPhone.getContext(), this);
         if (DBG) log("Creating CarrierActionAgent");
     }
 
@@ -131,11 +130,50 @@
                 break;
             case CARRIER_ACTION_RESET:
                 log("CARRIER_ACTION_RESET");
-                carrierActionSetMeteredApnsEnabled(true);
-                carrierActionSetRadioEnabled(true);
-                // notify configured carrier apps for reset
-                mPhone.getCarrierSignalAgent().notifyCarrierSignalReceivers(
-                        new Intent(TelephonyIntents.ACTION_CARRIER_SIGNAL_RESET));
+                carrierActionReset();
+                break;
+            case EVENT_APM_SETTINGS_CHANGED:
+                log("EVENT_APM_SETTINGS_CHANGED");
+                if ((Settings.Global.getInt(mPhone.getContext().getContentResolver(),
+                        Settings.Global.AIRPLANE_MODE_ON, 0) != 0)) {
+                    carrierActionReset();
+                }
+                break;
+            case EVENT_MOBILE_DATA_SETTINGS_CHANGED:
+                log("EVENT_MOBILE_DATA_SETTINGS_CHANGED");
+                if (!mPhone.getDataEnabled()) carrierActionReset();
+                break;
+            case EVENT_DATA_ROAMING_OFF:
+                log("EVENT_DATA_ROAMING_OFF");
+                // reset carrier actions when exit roaming state.
+                carrierActionReset();
+                break;
+            case EVENT_SIM_STATE_CHANGED:
+                String iccState = (String) msg.obj;
+                if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(iccState)) {
+                    log("EVENT_SIM_STATE_CHANGED status: " + iccState);
+                    carrierActionReset();
+                    String mobileData = Settings.Global.MOBILE_DATA;
+                    if (TelephonyManager.getDefault().getSimCount() != 1) {
+                        mobileData = mobileData + mPhone.getSubId();
+                    }
+                    mSettingsObserver.observe(Settings.Global.getUriFor(mobileData),
+                            EVENT_MOBILE_DATA_SETTINGS_CHANGED);
+                    mSettingsObserver.observe(
+                            Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON),
+                            EVENT_APM_SETTINGS_CHANGED);
+                    if (mPhone.getServiceStateTracker() != null) {
+                        mPhone.getServiceStateTracker().registerForDataRoamingOff(
+                                this, EVENT_DATA_ROAMING_OFF, null, false);
+                    }
+                } else if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(iccState)) {
+                    log("EVENT_SIM_STATE_CHANGED status: " + iccState);
+                    carrierActionReset();
+                    mSettingsObserver.unobserve();
+                    if (mPhone.getServiceStateTracker() != null) {
+                        mPhone.getServiceStateTracker().unregisterForDataRoamingOff(this);
+                    }
+                }
                 break;
             default:
                 loge("Unknown carrier action: " + msg.what);
@@ -167,6 +205,14 @@
         sendMessage(obtainMessage(CARRIER_ACTION_SET_METERED_APNS_ENABLED, enabled));
     }
 
+    private void carrierActionReset() {
+        carrierActionSetMeteredApnsEnabled(true);
+        carrierActionSetRadioEnabled(true);
+        // notify configured carrier apps for reset
+        mPhone.getCarrierSignalAgent().notifyCarrierSignalReceivers(
+                new Intent(TelephonyIntents.ACTION_CARRIER_SIGNAL_RESET));
+    }
+
     private RegistrantList getRegistrantsFromAction(int action) {
         switch (action) {
             case CARRIER_ACTION_SET_METERED_APNS_ENABLED:
diff --git a/src/java/com/android/internal/telephony/CarrierInfoManager.java b/src/java/com/android/internal/telephony/CarrierInfoManager.java
new file mode 100644
index 0000000..96dd8c4
--- /dev/null
+++ b/src/java/com/android/internal/telephony/CarrierInfoManager.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.telephony.ImsiEncryptionInfo;
+
+ /**
+ * This class provides methods to retreive information from the CarrierKeyProvider.
+ */
+public class CarrierInfoManager {
+    private static final String TAG = "CarrierInfoManager";
+
+    /**
+     * Returns Carrier specific information that will be used to encrypt the IMSI and IMPI.
+     * @param keyType whether the key is being used for WLAN or ePDG.
+     * @return ImsiEncryptionInfo which contains the information including the public key to be
+     *         used for encryption.
+     */
+    public static ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType) {
+        //TODO implementation will be done in subsequent CL.
+        return null;
+    }
+
+    /**
+     * Sets the Carrier specific information that will be used to encrypt the IMSI and IMPI.
+     * This includes the public key and the key identifier. This information will be stored in the
+     * device keystore.
+     * @param imsiEncryptionInfo which includes the Key Type, the Public Key
+     *        {@link java.security.PublicKey} and the Key Identifier.
+     *        The keyIdentifier Attribute value pair that helps a server locate
+     *        the private key to decrypt the permanent identity.
+     */
+    public static void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo) {
+        //TODO implementation will be done in subsequent CL.
+        return;
+    }
+}
+
diff --git a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
index 735bfe2..ab52c4a 100644
--- a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
@@ -30,6 +30,8 @@
 import android.telephony.CarrierConfigManager;
 import android.telephony.Rlog;
 
+import com.android.internal.telephony.util.NotificationChannelController;
+
 /**
  * This contains Carrier specific logic based on the states/events
  * managed in ServiceStateTracker.
@@ -129,7 +131,7 @@
         public void onReceive(Context context, Intent intent) {
             CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
                     context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-            PersistableBundle b = carrierConfigManager.getConfig();
+            PersistableBundle b = carrierConfigManager.getConfigForSubId(mPhone.getSubId());
             mDelay = b.getInt(CarrierConfigManager.KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT);
             Rlog.i(LOG_TAG, "reading time to delay notification: " + mDelay);
             handleConfigChanges();
@@ -174,6 +176,7 @@
                 .setStyle(new Notification.BigTextStyle().bigText(details))
                 .setContentText(details)
                 .setContentIntent(settingsIntent)
+                .setChannel(NotificationChannelController.CHANNEL_ID_ALERT)
                 .build();
 
         notificationManager.notify(NOTIFICATION_ID, mNotification);
@@ -189,4 +192,4 @@
                 context.getSystemService(Context.NOTIFICATION_SERVICE);
         notificationManager.cancel(NOTIFICATION_ID);
     }
-}
\ No newline at end of file
+}
diff --git a/src/java/com/android/internal/telephony/CarrierSignalAgent.java b/src/java/com/android/internal/telephony/CarrierSignalAgent.java
index 4d6d464..c6958cf 100644
--- a/src/java/com/android/internal/telephony/CarrierSignalAgent.java
+++ b/src/java/com/android/internal/telephony/CarrierSignalAgent.java
@@ -34,11 +34,9 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -67,22 +65,24 @@
     private final Phone mPhone;
 
     /**
-     * This is a map of intent action -> array list of component name of statically registered
+     * This is a map of intent action -> set of component name of statically registered
      * carrier signal receivers(wakeup receivers).
      * Those intents are declared in the Manifest files, aiming to wakeup broadcast receivers.
      * Carrier apps should be careful when configuring the wake signal list to avoid unnecessary
-     * wakeup.
+     * wakeup. Note we use Set as the entry value to compare config directly regardless of element
+     * order.
      * @see CarrierConfigManager#KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY
      */
-    private final Map<String, List<ComponentName>> mCachedWakeSignalConfigs = new HashMap<>();
+    private Map<String, Set<ComponentName>> mCachedWakeSignalConfigs = new HashMap<>();
 
     /**
-     * This is a map of intent action -> array list of component name of dynamically registered
+     * This is a map of intent action -> set of component name of dynamically registered
      * carrier signal receivers(non-wakeup receivers). Those intents will not wake up the apps.
      * Note Carrier apps should avoid configuring no wake signals in there Manifest files.
+     * Note we use Set as the entry value to compare config directly regardless of element order.
      * @see CarrierConfigManager#KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY
      */
-    private final Map<String, List<ComponentName>> mCachedNoWakeSignalConfigs = new HashMap<>();
+    private Map<String, Set<ComponentName>> mCachedNoWakeSignalConfigs = new HashMap<>();
 
     /**
      * This is a list of supported signals from CarrierSignalAgent
@@ -100,12 +100,6 @@
             String action = intent.getAction();
             if (DBG) log("CarrierSignalAgent receiver action: " + action);
             if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
-                // notify carrier apps before cache get purged
-                if (mPhone.getIccCard() != null
-                        && IccCardConstants.State.ABSENT == mPhone.getIccCard().getState()) {
-                    notifyCarrierSignalReceivers(
-                            new Intent(TelephonyIntents.ACTION_CARRIER_SIGNAL_RESET));
-                }
                 loadCarrierConfig();
             }
         }
@@ -132,18 +126,36 @@
         }
         if (b != null) {
             synchronized (mCachedWakeSignalConfigs) {
-                mCachedWakeSignalConfigs.clear();
                 log("Loading carrier config: " + KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY);
-                parseAndCache(b.getStringArray(KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY),
-                        mCachedWakeSignalConfigs);
+                Map<String, Set<ComponentName>> config = parseAndCache(
+                        b.getStringArray(KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY));
+                // In some rare cases, up-to-date config could be fetched with delay and all signals
+                // have already been delivered the receivers from the default carrier config.
+                // To handle this raciness, we should notify those receivers (from old configs)
+                // and reset carrier actions. This should be done before cached Config got purged
+                // and written with the up-to-date value, Otherwise those receivers from the
+                // old config might lingers without properly clean-up.
+                if (!mCachedWakeSignalConfigs.isEmpty()
+                        && !config.equals(mCachedWakeSignalConfigs)) {
+                    if (VDBG) log("carrier config changed, reset receivers from old config");
+                    mPhone.getCarrierActionAgent().sendEmptyMessage(
+                            CarrierActionAgent.CARRIER_ACTION_RESET);
+                }
+                mCachedWakeSignalConfigs = config;
             }
 
             synchronized (mCachedNoWakeSignalConfigs) {
-                mCachedNoWakeSignalConfigs.clear();
                 log("Loading carrier config: "
                         + KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY);
-                parseAndCache(b.getStringArray(KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY),
-                        mCachedNoWakeSignalConfigs);
+                Map<String, Set<ComponentName>> config = parseAndCache(
+                        b.getStringArray(KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY));
+                if (!mCachedNoWakeSignalConfigs.isEmpty()
+                        && !config.equals(mCachedNoWakeSignalConfigs)) {
+                    if (VDBG) log("carrier config changed, reset receivers from old config");
+                    mPhone.getCarrierActionAgent().sendEmptyMessage(
+                            CarrierActionAgent.CARRIER_ACTION_RESET);
+                }
+                mCachedNoWakeSignalConfigs = config;
             }
         }
     }
@@ -155,8 +167,8 @@
      * @see #COMPONENT_NAME_DELIMITER
      * @param configs raw information from carrier config
      */
-    private void parseAndCache(String[] configs,
-                               Map<String, List<ComponentName>> cachedConfigs) {
+    private Map<String, Set<ComponentName>> parseAndCache(String[] configs) {
+        Map<String, Set<ComponentName>> newCachedWakeSignalConfigs = new HashMap<>();
         if (!ArrayUtils.isEmpty(configs)) {
             for (String config : configs) {
                 if (!TextUtils.isEmpty(config)) {
@@ -174,10 +186,10 @@
                                 loge("Invalid signal name: " + s);
                                 continue;
                             }
-                            List<ComponentName> componentList = cachedConfigs.get(s);
+                            Set<ComponentName> componentList = newCachedWakeSignalConfigs.get(s);
                             if (componentList == null) {
-                                componentList = new ArrayList<>();
-                                cachedConfigs.put(s, componentList);
+                                componentList = new HashSet<>();
+                                newCachedWakeSignalConfigs.put(s, componentList);
                             }
                             componentList.add(componentName);
                             if (VDBG) {
@@ -191,6 +203,7 @@
                 }
             }
         }
+        return newCachedWakeSignalConfigs;
     }
 
     /**
@@ -215,7 +228,7 @@
      *                  registered during run-time.
      * @param wakeup true indicate wakeup receivers otherwise non-wakeup receivers
      */
-    private void broadcast(Intent intent, List<ComponentName> receivers, boolean wakeup) {
+    private void broadcast(Intent intent, Set<ComponentName> receivers, boolean wakeup) {
         final PackageManager packageManager = mPhone.getContext().getPackageManager();
         for (ComponentName name : receivers) {
             Intent signal = new Intent(intent);
@@ -257,19 +270,19 @@
      *
      */
     public void notifyCarrierSignalReceivers(Intent intent) {
-        List<ComponentName> receiverList;
+        Set<ComponentName> receiverSet;
 
         synchronized (mCachedWakeSignalConfigs) {
-            receiverList = mCachedWakeSignalConfigs.get(intent.getAction());
-            if (!ArrayUtils.isEmpty(receiverList)) {
-                broadcast(intent, receiverList, WAKE);
+            receiverSet = mCachedWakeSignalConfigs.get(intent.getAction());
+            if (!ArrayUtils.isEmpty(receiverSet)) {
+                broadcast(intent, receiverSet, WAKE);
             }
         }
 
         synchronized (mCachedNoWakeSignalConfigs) {
-            receiverList = mCachedNoWakeSignalConfigs.get(intent.getAction());
-            if (!ArrayUtils.isEmpty(receiverList)) {
-                broadcast(intent, receiverList, NO_WAKE);
+            receiverSet = mCachedNoWakeSignalConfigs.get(intent.getAction());
+            if (!ArrayUtils.isEmpty(receiverSet)) {
+                broadcast(intent, receiverSet, NO_WAKE);
             }
         }
     }
@@ -291,14 +304,14 @@
         IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
         pw.println("mCachedWakeSignalConfigs:");
         ipw.increaseIndent();
-        for (Map.Entry<String, List<ComponentName>> entry : mCachedWakeSignalConfigs.entrySet()) {
+        for (Map.Entry<String, Set<ComponentName>> entry : mCachedWakeSignalConfigs.entrySet()) {
             pw.println("signal: " + entry.getKey() + " componentName list: " + entry.getValue());
         }
         ipw.decreaseIndent();
 
         pw.println("mCachedNoWakeSignalConfigs:");
         ipw.increaseIndent();
-        for (Map.Entry<String, List<ComponentName>> entry : mCachedNoWakeSignalConfigs.entrySet()) {
+        for (Map.Entry<String, Set<ComponentName>> entry : mCachedNoWakeSignalConfigs.entrySet()) {
             pw.println("signal: " + entry.getKey() + " componentName list: " + entry.getValue());
         }
         ipw.decreaseIndent();
diff --git a/src/java/com/android/internal/telephony/CarrierSmsUtils.java b/src/java/com/android/internal/telephony/CarrierSmsUtils.java
index 845860c..a64aea7 100644
--- a/src/java/com/android/internal/telephony/CarrierSmsUtils.java
+++ b/src/java/com/android/internal/telephony/CarrierSmsUtils.java
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.os.Binder;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.Rlog;
@@ -78,13 +79,17 @@
             return null;
         }
 
-        PersistableBundle config = cm.getConfigForSubId(phone.getSubId());
-        if (config == null) {
-            if (VDBG) Rlog.v(TAG, "No CarrierConfig for subId:" + phone.getSubId());
-            return null;
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            PersistableBundle config = cm.getConfigForSubId(phone.getSubId());
+            if (config == null) {
+                if (VDBG) Rlog.v(TAG, "No CarrierConfig for subId:" + phone.getSubId());
+                return null;
+            }
+            return config.getString(CARRIER_IMS_PACKAGE_KEY, null);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
-
-        return config.getString(CARRIER_IMS_PACKAGE_KEY, null);
     }
 
     private CarrierSmsUtils() {}
diff --git a/src/java/com/android/internal/telephony/CellBroadcastHandler.java b/src/java/com/android/internal/telephony/CellBroadcastHandler.java
index 44479c5..4227148 100644
--- a/src/java/com/android/internal/telephony/CellBroadcastHandler.java
+++ b/src/java/com/android/internal/telephony/CellBroadcastHandler.java
@@ -86,11 +86,17 @@
         if (message.isEmergencyMessage()) {
             log("Dispatching emergency SMS CB, SmsCbMessage is: " + message);
             intent = new Intent(Telephony.Sms.Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION);
+            // Explicitly send the intent to the default cell broadcast receiver.
+            intent.setPackage(mContext.getResources().getString(
+                    com.android.internal.R.string.config_defaultCellBroadcastReceiverPkg));
             receiverPermission = Manifest.permission.RECEIVE_EMERGENCY_BROADCAST;
             appOp = AppOpsManager.OP_RECEIVE_EMERGECY_SMS;
         } else {
             log("Dispatching SMS CB, SmsCbMessage is: " + message);
             intent = new Intent(Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION);
+            // Send implicit intent since there are various 3rd party carrier apps listen to
+            // this intent.
+            intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
             receiverPermission = Manifest.permission.RECEIVE_SMS;
             appOp = AppOpsManager.OP_RECEIVE_SMS;
         }
@@ -112,8 +118,6 @@
             }
         }
 
-        intent.setPackage(mContext.getResources().getString(
-                com.android.internal.R.string.config_defaultCellBroadcastReceiverPkg));
         mContext.sendOrderedBroadcastAsUser(intent, UserHandle.ALL, receiverPermission, appOp,
                 mReceiver, getHandler(), Activity.RESULT_OK, null, null);
     }
diff --git a/src/java/com/android/internal/telephony/ClientWakelockAccountant.java b/src/java/com/android/internal/telephony/ClientWakelockAccountant.java
index ba4d86f..c47faab 100644
--- a/src/java/com/android/internal/telephony/ClientWakelockAccountant.java
+++ b/src/java/com/android/internal/telephony/ClientWakelockAccountant.java
@@ -20,7 +20,6 @@
 import android.telephony.Rlog;
 
 import com.android.internal.annotations.VisibleForTesting;
-
 import java.util.ArrayList;
 
 public class ClientWakelockAccountant {
diff --git a/src/java/com/android/internal/telephony/CommandsInterface.java b/src/java/com/android/internal/telephony/CommandsInterface.java
index a5e4c7b..524b813 100644
--- a/src/java/com/android/internal/telephony/CommandsInterface.java
+++ b/src/java/com/android/internal/telephony/CommandsInterface.java
@@ -21,13 +21,14 @@
 import android.os.WorkSource;
 import android.service.carrier.CarrierIdentifier;
 import android.telephony.ClientRequestStats;
+import android.telephony.ImsiEncryptionInfo;
+import android.telephony.NetworkScanRequest;
 
 import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
 import com.android.internal.telephony.dataconnection.DataProfile;
 import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
 import com.android.internal.telephony.uicc.IccCardStatus;
 
-import java.security.PublicKey;
 import java.util.List;
 
 /**
@@ -1293,7 +1294,7 @@
      *
      * ((AsyncResult)response.obj).result is a NetworkScanResult object
      */
-    void startNetworkScan(Message response);
+    void startNetworkScan(NetworkScanRequest nsr, Message response);
 
     /**
      * Stops the ongoing network scan
@@ -1460,7 +1461,7 @@
      *        specific.
      * @param response callback message
      */
-    void setCarrierInfoForImsiEncryption(PublicKey publicKey, String keyIdentifier,
+    void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo,
                                          Message response);
 
     void invokeOemRilRequestStrings(String[] strings, Message response);
diff --git a/src/java/com/android/internal/telephony/Connection.java b/src/java/com/android/internal/telephony/Connection.java
index dc53172..74ee5c5 100644
--- a/src/java/com/android/internal/telephony/Connection.java
+++ b/src/java/com/android/internal/telephony/Connection.java
@@ -34,6 +34,7 @@
  * {@hide}
  */
 public abstract class Connection {
+    private static final String TAG = "Connection";
 
     public interface PostDialListener {
         void onPostDialWait();
@@ -311,6 +312,15 @@
     }
 
     /**
+     * Sets the Connection connect time in {@link SystemClock#elapsedRealtime()} format.
+     *
+     * @param connectTimeReal the new connect time.
+     */
+    public void setConnectTimeReal(long connectTimeReal) {
+        mConnectTimeReal = connectTimeReal;
+    }
+
+    /**
      * Connection connect time in elapsedRealtime() format.
      * For outgoing calls: Begins at (DIALING|ALERTING) -> ACTIVE transition.
      * For incoming calls: Begins at (INCOMING|WAITING) -> ACTIVE transition.
@@ -818,6 +828,16 @@
     public void setConnectionExtras(Bundle extras) {
         if (extras != null) {
             mExtras = new Bundle(extras);
+
+            int previousCount = mExtras.size();
+            // Prevent vendors from passing in extras other than primitive types and android API
+            // parcelables.
+            mExtras = mExtras.filterValues();
+            int filteredCount = mExtras.size();
+            if (filteredCount != previousCount) {
+                Rlog.i(TAG, "setConnectionExtras: filtering " + (previousCount - filteredCount)
+                        + " invalid extras.");
+            }
         } else {
             mExtras = null;
         }
diff --git a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
index de3be01..5960051 100755
--- a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
@@ -24,9 +24,11 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.os.PersistableBundle;
 import android.os.Registrant;
 import android.os.RegistrantList;
 import android.os.SystemProperties;
+import android.telephony.CarrierConfigManager;
 import android.telephony.CellLocation;
 import android.telephony.DisconnectCause;
 import android.telephony.PhoneNumberUtils;
@@ -38,6 +40,7 @@
 import android.text.TextUtils;
 import android.util.EventLog;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.cdma.CdmaCallWaitingNotification;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 
@@ -66,7 +69,8 @@
     private static final int MAX_CONNECTIONS_PER_CALL_CDMA = 1; //only 1 connection allowed per call
 
     //***** Instance Variables
-    private GsmCdmaConnection mConnections[];
+    @VisibleForTesting
+    public GsmCdmaConnection[] mConnections;
     private RegistrantList mVoiceCallEndedRegistrants = new RegistrantList();
     private RegistrantList mVoiceCallStartedRegistrants = new RegistrantList();
 
@@ -166,6 +170,11 @@
         if (mPhone.isPhoneTypeGsm()) {
             mConnections = new GsmCdmaConnection[MAX_CONNECTIONS_GSM];
             mCi.unregisterForCallWaitingInfo(this);
+            // Prior to phone switch to GSM, if CDMA has any emergency call
+            // data will be in disabled state, after switching to GSM enable data.
+            if (mIsInEmergencyCall) {
+                mPhone.mDcTracker.setInternalDataEnabled(true);
+            }
         } else {
             mConnections = new GsmCdmaConnection[MAX_CONNECTIONS_CDMA];
             mPendingCallInEcm = false;
@@ -180,10 +189,9 @@
     private void reset() {
         Rlog.d(LOG_TAG, "reset");
 
-        clearDisconnected();
-
         for (GsmCdmaConnection gsmCdmaConnection : mConnections) {
             if (gsmCdmaConnection != null) {
+                gsmCdmaConnection.onDisconnect(DisconnectCause.ERROR_UNSPECIFIED);
                 gsmCdmaConnection.dispose();
             }
         }
@@ -194,7 +202,7 @@
 
         mConnections = null;
         mPendingMO = null;
-        mState = PhoneConstants.State.IDLE;
+        clearDisconnected();
     }
 
     @Override
@@ -465,9 +473,17 @@
             mPendingMO = new GsmCdmaConnection(mPhone,
                     checkForTestEmergencyNumber(dialString), this, mForegroundCall,
                     mIsInEmergencyCall);
-            // Some network need a empty flash before sending the normal one
-            m3WayCallFlashDelay = mPhone.getContext().getResources()
-                    .getInteger(com.android.internal.R.integer.config_cdma_3waycall_flash_delay);
+            // Some networks need an empty flash before sending the normal one
+            CarrierConfigManager configManager = (CarrierConfigManager)
+                    mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+            PersistableBundle bundle = configManager.getConfig();
+            if (bundle != null) {
+                m3WayCallFlashDelay =
+                        bundle.getInt(CarrierConfigManager.KEY_CDMA_3WAYCALL_FLASH_DELAY_INT);
+            } else {
+                // The default 3-way call flash delay is 0s
+                m3WayCallFlashDelay = 0;
+            }
             if (m3WayCallFlashDelay > 0) {
                 mCi.sendCDMAFeatureCode("", obtainMessage(EVENT_THREE_WAY_DIAL_BLANK_FLASH));
             } else {
@@ -1089,10 +1105,10 @@
         //dumpState();
     }
 
-    private void updateMetrics(GsmCdmaConnection []connections) {
+    private void updateMetrics(GsmCdmaConnection[] connections) {
         ArrayList<GsmCdmaConnection> activeConnections = new ArrayList<>();
-        for(GsmCdmaConnection conn : connections) {
-            if(conn != null) activeConnections.add(conn);
+        for (GsmCdmaConnection conn : connections) {
+            if (conn != null) activeConnections.add(conn);
         }
         mMetrics.writeRilCallList(mPhone.getPhoneId(), activeConnections);
     }
@@ -1376,11 +1392,14 @@
 
             case EVENT_CONFERENCE_RESULT:
                 if (isPhoneTypeGsm()) {
-                    // The conference merge failed, so notify listeners.  Ultimately this bubbles up
-                    // to Telecom, which will inform the InCall UI of the failure.
-                    Connection connection = mForegroundCall.getLatestConnection();
-                    if (connection != null) {
-                        connection.onConferenceMergeFailed();
+                    ar = (AsyncResult) msg.obj;
+                    if (ar.exception != null) {
+                        // The conference merge failed, so notify listeners.  Ultimately this
+                        // bubbles up to Telecom, which will inform the InCall UI of the failure.
+                        Connection connection = mForegroundCall.getLatestConnection();
+                        if (connection != null) {
+                            connection.onConferenceMergeFailed();
+                        }
                     }
                 }
                 // fall through
@@ -1662,4 +1681,13 @@
                 MAX_CONNECTIONS_PER_CALL_GSM :
                 MAX_CONNECTIONS_PER_CALL_CDMA;
     }
+
+    /**
+     * Called to force the call tracker to cleanup any stale calls.  Does this by triggering
+     * {@code GET_CURRENT_CALLS} on the RIL.
+     */
+    @Override
+    public void cleanupCalls() {
+        pollCallsWhenSafe();
+    }
 }
diff --git a/src/java/com/android/internal/telephony/GsmCdmaConnection.java b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
index 1b453e6..f126600 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaConnection.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
@@ -441,6 +441,11 @@
             case CallFailCause.BEARER_NOT_AVAIL:
                 return DisconnectCause.CONGESTION;
 
+            case CallFailCause.EMERGENCY_TEMP_FAILURE:
+                return DisconnectCause.EMERGENCY_TEMP_FAILURE;
+            case CallFailCause.EMERGENCY_PERM_FAILURE:
+                return DisconnectCause.EMERGENCY_PERM_FAILURE;
+
             case CallFailCause.ACM_LIMIT_EXCEEDED:
                 return DisconnectCause.LIMIT_EXCEEDED;
 
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index 27cbd77..0c34749 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -28,7 +28,7 @@
 import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
 
-import android.app.ActivityManagerNative;
+import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentValues;
 import android.content.Context;
@@ -55,6 +55,8 @@
 import android.telecom.VideoProfile;
 import android.telephony.CarrierConfigManager;
 import android.telephony.CellLocation;
+import android.telephony.ImsiEncryptionInfo;
+import android.telephony.NetworkScanRequest;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
@@ -73,6 +75,7 @@
 import com.android.internal.telephony.cdma.EriManager;
 import com.android.internal.telephony.gsm.GsmMmiCode;
 import com.android.internal.telephony.gsm.SuppServiceNotification;
+import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.test.SimulatedRadioControl;
 import com.android.internal.telephony.uicc.IccCardProxy;
 import com.android.internal.telephony.uicc.IccException;
@@ -88,7 +91,9 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
+import java.util.Deque;
 import java.util.List;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -278,7 +283,7 @@
             tm.setPhoneType(getPhoneId(), PhoneConstants.PHONE_TYPE_GSM);
             mIccCardProxy.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS);
         } else {
-            mCdmaSubscriptionSource = CdmaSubscriptionSourceManager.SUBSCRIPTION_SOURCE_UNKNOWN;
+            mCdmaSubscriptionSource = mCdmaSSM.getCdmaSubscriptionSource();
             // This is needed to handle phone process crashes
             mIsPhoneInEcmState = getInEcmMode();
             if (mIsPhoneInEcmState) {
@@ -497,7 +502,7 @@
 
             ret = PhoneConstants.DataState.DISCONNECTED;
         } else if (mSST.getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE
-                && (isPhoneTypeCdma() ||
+                && (isPhoneTypeCdma() || isPhoneTypeCdmaLte() ||
                 (isPhoneTypeGsm() && !apnType.equals(PhoneConstants.APN_TYPE_EMERGENCY)))) {
             // If we're out of service, open TCP sockets may still work
             // but no data will flow
@@ -614,22 +619,13 @@
         }
     }
 
-    @Override
-    public boolean isInEcm() {
-        if (isPhoneTypeGsm()) {
-            return false;
-        } else {
-            return mIsPhoneInEcmState;
-        }
-    }
-
     //CDMA
     private void sendEmergencyCallbackModeChange(){
         //Send an Intent
         Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
-        intent.putExtra(PhoneConstants.PHONE_IN_ECM_STATE, mIsPhoneInEcmState);
+        intent.putExtra(PhoneConstants.PHONE_IN_ECM_STATE, isInEcm());
         SubscriptionManager.putPhoneIdAndSubIdExtra(intent, getPhoneId());
-        ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);
+        ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL);
         if (DBG) logd("sendEmergencyCallbackModeChange");
     }
 
@@ -639,7 +635,7 @@
             Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED);
             intent.putExtra(PhoneConstants.PHONE_IN_EMERGENCY_CALL, callActive);
             SubscriptionManager.putPhoneIdAndSubIdExtra(intent, getPhoneId());
-            ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);
+            ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL);
             if (DBG) Rlog.d(LOG_TAG, "sendEmergencyCallStateChange: callActive " + callActive);
         }
     }
@@ -1056,7 +1052,7 @@
             throw new CallStateException("Sending UUS information NOT supported in CDMA!");
         }
 
-        boolean isEmergency = PhoneNumberUtils.isEmergencyNumber(dialString);
+        boolean isEmergency = PhoneNumberUtils.isEmergencyNumber(getSubId(), dialString);
         Phone imsPhone = mImsPhone;
 
         CarrierConfigManager configManager =
@@ -1074,7 +1070,7 @@
                 && isEmergency
                 && alwaysTryImsForEmergencyCarrierConfig
                 && ImsManager.isNonTtyOrTtyOnVolteEnabled(mContext)
-                && (imsPhone.getServiceState().getState() != ServiceState.STATE_POWER_OFF);
+                && imsPhone.isImsAvailable();
 
         String dialPart = PhoneNumberUtils.extractNetworkPortionAlt(PhoneNumberUtils.
                 stripSeparators(dialString));
@@ -1108,7 +1104,12 @@
             } catch (CallStateException e) {
                 if (DBG) logd("IMS PS call exception " + e +
                         "imsUseEnabled =" + imsUseEnabled + ", imsPhone =" + imsPhone);
-                if (!Phone.CS_FALLBACK.equals(e.getMessage())) {
+                // Do not throw a CallStateException and instead fall back to Circuit switch
+                // for emergency calls and MMI codes.
+                if (Phone.CS_FALLBACK.equals(e.getMessage()) || isEmergency) {
+                    logi("IMS call failed with Exception: " + e.getMessage() + ". Falling back "
+                            + "to CS.");
+                } else {
                     CallStateException ce = new CallStateException(e.getMessage());
                     ce.setStackTrace(e.getStackTrace());
                     throw ce;
@@ -1208,8 +1209,8 @@
             // Only look at the Network portion for mmi
             String networkPortion = PhoneNumberUtils.extractNetworkPortionAlt(newDialString);
             GsmMmiCode mmi = GsmMmiCode.newFromDialString(networkPortion, this,
-                                                          mUiccApplication.get(), wrappedCallback);
-            if (DBG) logd("dialing w/ mmi '" + mmi + "'...");
+                    mUiccApplication.get(), wrappedCallback);
+            if (DBG) logd("dialInternal: dialing w/ mmi '" + mmi + "'...");
 
             if (mmi == null) {
                 return mCT.dial(newDialString, uusInfo, intentExtras);
@@ -1218,13 +1219,7 @@
             } else {
                 mPendingMMIs.add(mmi);
                 mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
-                try {
-                    mmi.processCode();
-                } catch (CallStateException e) {
-                    //do nothing
-                }
-
-                // FIXME should this return null or something else?
+                mmi.processCode();
                 return null;
             }
         } else {
@@ -1232,7 +1227,7 @@
         }
     }
 
-    @Override
+   @Override
     public boolean handlePinMmi(String dialString) {
         MmiCode mmi;
         if (isPhoneTypeGsm()) {
@@ -1256,15 +1251,50 @@
         return false;
     }
 
+    private void sendUssdResponse(String ussdRequest, CharSequence message, int returnCode,
+                                   ResultReceiver wrappedCallback) {
+        UssdResponse response = new UssdResponse(ussdRequest, message);
+        Bundle returnData = new Bundle();
+        returnData.putParcelable(TelephonyManager.USSD_RESPONSE, response);
+        wrappedCallback.send(returnCode, returnData);
+    }
+
     @Override
     public boolean handleUssdRequest(String ussdRequest, ResultReceiver wrappedCallback) {
-        try {
-            dialInternal(ussdRequest, null, VideoProfile.STATE_AUDIO_ONLY, null, wrappedCallback);
+        if (!isPhoneTypeGsm() || mPendingMMIs.size() > 0) {
+            //todo: replace the generic failure with specific error code.
+            sendUssdResponse(ussdRequest, null, TelephonyManager.USSD_RETURN_FAILURE,
+                    wrappedCallback );
             return true;
-         } catch (Exception e) {
-           logd("exception" + e);
-           return false;
-         }
+        }
+
+        // Try over IMS if possible.
+        Phone imsPhone = mImsPhone;
+        if ((imsPhone != null)
+                && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)
+                || imsPhone.isUtEnabled())) {
+            try {
+                logd("handleUssdRequest: attempting over IMS");
+                return imsPhone.handleUssdRequest(ussdRequest, wrappedCallback);
+            } catch (CallStateException cse) {
+                if (!CS_FALLBACK.equals(cse.getMessage())) {
+                    return false;
+                }
+                // At this point we've tried over IMS but have been informed we need to handover
+                // back to GSM.
+                logd("handleUssdRequest: fallback to CS required");
+            }
+        }
+
+        // Try USSD over GSM.
+        try {
+            dialInternal(ussdRequest, null, VideoProfile.STATE_AUDIO_ONLY, null,
+                    wrappedCallback);
+        } catch (Exception e) {
+            logd("handleUssdRequest: exception" + e);
+            return false;
+        }
+        return true;
     }
 
     @Override
@@ -1491,6 +1521,16 @@
     }
 
     @Override
+    public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType) {
+        return CarrierInfoManager.getCarrierInfoForImsiEncryption(keyType);
+    }
+
+    @Override
+    public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo) {
+        CarrierInfoManager.setCarrierInfoForImsiEncryption(imsiEncryptionInfo);
+    }
+
+    @Override
     public String getGroupIdLevel1() {
         if (isPhoneTypeGsm()) {
             IccRecords r = mIccRecords.get();
@@ -1779,8 +1819,8 @@
     }
 
     @Override
-    public void startNetworkScan(Message response) {
-        mCi.startNetworkScan(response);
+    public void startNetworkScan(NetworkScanRequest nsr, Message response) {
+        mCi.startNetworkScan(nsr, response);
     }
 
     @Override
@@ -1811,6 +1851,15 @@
     }
 
     @Override
+    public void setTTYMode(int ttyMode, Message onComplete) {
+        // Send out the TTY Mode change over RIL as well
+        super.setTTYMode(ttyMode, onComplete);
+        if (mImsPhone != null) {
+            mImsPhone.setTTYMode(ttyMode, onComplete);
+        }
+    }
+
+    @Override
     public void setUiTTYMode(int uiTtyMode, Message onComplete) {
        if (mImsPhone != null) {
            mImsPhone.setUiTTYMode(uiTtyMode, onComplete);
@@ -1854,7 +1903,7 @@
 
     @Override
     public void setDataRoamingEnabled(boolean enable) {
-        mDcTracker.setDataRoamingEnabled(enable);
+        mDcTracker.setDataRoamingEnabledByUser(enable);
     }
 
     @Override
@@ -1919,21 +1968,22 @@
          * The exception is cancellation of an incoming USSD-REQUEST, which is
          * not on the list.
          */
-        logd("USSD Response:" + mmi);
         if (mPendingMMIs.remove(mmi) || (isPhoneTypeGsm() && (mmi.isUssdRequest() ||
                 ((GsmMmiCode)mmi).isSsInfo()))) {
 
             ResultReceiver receiverCallback = mmi.getUssdCallbackReceiver();
             if (receiverCallback != null) {
-                UssdResponse response = new UssdResponse(mmi.getDialString(), mmi.getMessage());
-                Bundle returnData = new Bundle();
-                returnData.putParcelable(TelephonyManager.USSD_RESPONSE, response);
+                Rlog.i(LOG_TAG, "onMMIDone: invoking callback: " + mmi);
                 int returnCode = (mmi.getState() ==  MmiCode.State.COMPLETE) ?
                     TelephonyManager.USSD_RETURN_SUCCESS : TelephonyManager.USSD_RETURN_FAILURE;
-                receiverCallback.send(returnCode, returnData);
+                sendUssdResponse(mmi.getDialString(), mmi.getMessage(), returnCode,
+                        receiverCallback );
             } else {
+                Rlog.i(LOG_TAG, "onMMIDone: notifying registrants: " + mmi);
                 mMmiCompleteRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
             }
+        } else {
+            Rlog.i(LOG_TAG, "onMMIDone: invalid response or already handled; ignoring: " + mmi);
         }
     }
 
@@ -1951,6 +2001,7 @@
     }
 
     private void onNetworkInitiatedUssd(MmiCode mmi) {
+        Rlog.v(LOG_TAG, "onNetworkInitiatedUssd: mmi=" + mmi);
         mMmiCompleteRegistrants.notifyRegistrants(
             new AsyncResult(null, mmi, null));
     }
@@ -1995,19 +2046,18 @@
             } else {
                 found.onUssdFinished(ussdMessage, isUssdRequest);
             }
-        } else { // pending USSD not found
+        } else if (!isUssdError && ussdMessage != null) {
+            // pending USSD not found
             // The network may initiate its own USSD request
 
             // ignore everything that isnt a Notify or a Request
             // also, discard if there is no message to present
-            if (!isUssdError && ussdMessage != null) {
-                GsmMmiCode mmi;
-                mmi = GsmMmiCode.newNetworkInitiatedUssd(ussdMessage,
+            GsmMmiCode mmi;
+            mmi = GsmMmiCode.newNetworkInitiatedUssd(ussdMessage,
                                                    isUssdRequest,
                                                    GsmCdmaPhone.this,
                                                    mUiccApplication.get());
-                onNetworkInitiatedUssd(mmi);
-            }
+            onNetworkInitiatedUssd(mmi);
         }
     }
 
@@ -2017,6 +2067,7 @@
     private void syncClirSetting() {
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
         int clirSetting = sp.getInt(CLIR_KEY + getPhoneId(), -1);
+        Rlog.i(LOG_TAG, "syncClirSetting: " + CLIR_KEY + getPhoneId() + "=" + clirSetting);
         if (clirSetting >= 0) {
             mCi.setCLIR(clirSetting, null);
         }
@@ -2056,10 +2107,6 @@
                 }
             }
         }
-        Phone imsPhone = mImsPhone;
-        if (imsPhone != null) {
-            imsPhone.getServiceState().setStateOff();
-        }
         mRadioOffOrNotAvailableRegistrants.notifyRegistrants();
     }
 
@@ -2228,17 +2275,16 @@
                 break;
 
             case EVENT_SIM_RECORDS_LOADED:
-                if (isPhoneTypeGsm()) {
-                    updateCurrentCarrierInProvider();
+                updateCurrentCarrierInProvider();
 
-                    // Check if this is a different SIM than the previous one. If so unset the
-                    // voice mail number.
-                    String imsi = getVmSimImsi();
-                    String imsiFromSIM = getSubscriberId();
-                    if (imsi != null && imsiFromSIM != null && !imsiFromSIM.equals(imsi)) {
-                        storeVoiceMailNumber(null);
-                        setVmSimImsi(null);
-                    }
+                // Check if this is a different SIM than the previous one. If so unset the
+                // voice mail number.
+                String imsi = getVmSimImsi();
+                String imsiFromSIM = getSubscriberId();
+                if ((!isPhoneTypeGsm() || imsi != null) && imsiFromSIM != null
+                        && !imsiFromSIM.equals(imsi)) {
+                    storeVoiceMailNumber(null);
+                    setVmSimImsi(null);
                 }
 
                 mSimRecordsLoadedRegistrants.notifyRegistrants();
@@ -2668,26 +2714,35 @@
         return (r != null) ? r.isCspPlmnEnabled() : false;
     }
 
-    public boolean isManualNetSelAllowed() {
+    /**
+     * Whether manual select is now allowed and we should set
+     * to auto network select mode.
+     */
+    public boolean shouldForceAutoNetworkSelect() {
 
         int nwMode = Phone.PREFERRED_NT_MODE;
         int subId = getSubId();
 
+        // If it's invalid subId, we shouldn't force to auto network select mode.
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            return false;
+        }
+
         nwMode = android.provider.Settings.Global.getInt(mContext.getContentResolver(),
                     android.provider.Settings.Global.PREFERRED_NETWORK_MODE + subId, nwMode);
 
-        logd("isManualNetSelAllowed in mode = " + nwMode);
+        logd("shouldForceAutoNetworkSelect in mode = " + nwMode);
         /*
          *  For multimode targets in global mode manual network
-         *  selection is disallowed
+         *  selection is disallowed. So we should force auto select mode.
          */
         if (isManualSelProhibitedInGlobalMode()
                 && ((nwMode == Phone.NT_MODE_LTE_CDMA_EVDO_GSM_WCDMA)
                         || (nwMode == Phone.NT_MODE_GLOBAL)) ){
-            logd("Manual selection not supported in mode = " + nwMode);
-            return false;
+            logd("Should force auto network select mode = " + nwMode);
+            return true;
         } else {
-            logd("Manual selection is supported in mode = " + nwMode);
+            logd("Should not force auto network select mode = " + nwMode);
         }
 
         /*
@@ -2697,7 +2752,7 @@
          *  Note: the actual enabling/disabling manual selection for these
          *  cases will be controlled by csp
          */
-        return true;
+        return false;
     }
 
     private boolean isManualSelProhibitedInGlobalMode() {
@@ -2751,6 +2806,10 @@
 
     @Override
     public void exitEmergencyCallbackMode() {
+        if (DBG) {
+            Rlog.d(LOG_TAG, "exitEmergencyCallbackMode: mImsPhone=" + mImsPhone
+                    + " isPhoneTypeGsm=" + isPhoneTypeGsm());
+        }
         if (isPhoneTypeGsm()) {
             if (mImsPhone != null) {
                 mImsPhone.exitEmergencyCallbackMode();
@@ -2767,11 +2826,11 @@
     //CDMA
     private void handleEnterEmergencyCallbackMode(Message msg) {
         if (DBG) {
-            Rlog.d(LOG_TAG, "handleEnterEmergencyCallbackMode,mIsPhoneInEcmState= "
-                    + mIsPhoneInEcmState);
+            Rlog.d(LOG_TAG, "handleEnterEmergencyCallbackMode, isInEcm()="
+                    + isInEcm());
         }
         // if phone is not in Ecm mode, and it's changed to Ecm mode
-        if (!mIsPhoneInEcmState) {
+        if (!isInEcm()) {
             setIsInEcm(true);
 
             // notify change
@@ -2791,8 +2850,8 @@
     private void handleExitEmergencyCallbackMode(Message msg) {
         AsyncResult ar = (AsyncResult)msg.obj;
         if (DBG) {
-            Rlog.d(LOG_TAG, "handleExitEmergencyCallbackMode,ar.exception , mIsPhoneInEcmState "
-                    + ar.exception + mIsPhoneInEcmState);
+            Rlog.d(LOG_TAG, "handleExitEmergencyCallbackMode,ar.exception , isInEcm="
+                    + ar.exception + isInEcm());
         }
         // Remove pending exit Ecm runnable, if any
         removeCallbacks(mExitEcmRunnable);
@@ -2802,7 +2861,7 @@
         }
         // if exiting ecm success
         if (ar.exception == null) {
-            if (mIsPhoneInEcmState) {
+            if (isInEcm()) {
                 setIsInEcm(false);
             }
 
@@ -3174,7 +3233,7 @@
         Intent intent = new Intent(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED);
         intent.putExtra(PhoneConstants.PHONE_NAME_KEY, getPhoneName());
         SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhoneId);
-        ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);
+        ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL);
     }
 
     private void switchVoiceRadioTech(int newVoiceRadioTech) {
@@ -3237,7 +3296,7 @@
         pw.println(" mCdmaSubscriptionSource=" + mCdmaSubscriptionSource);
         pw.println(" mEriManager=" + mEriManager);
         pw.println(" mWakeLock=" + mWakeLock);
-        pw.println(" mIsPhoneInEcmState=" + mIsPhoneInEcmState);
+        pw.println(" isInEcm()=" + isInEcm());
         if (VDBG) pw.println(" mEsn=" + mEsn);
         if (VDBG) pw.println(" mMeid=" + mMeid);
         pw.println(" mCarrierOtaSpNumSchema=" + mCarrierOtaSpNumSchema);
@@ -3389,6 +3448,10 @@
         Rlog.d(LOG_TAG, "[GsmCdmaPhone] " + s);
     }
 
+    private void logi(String s) {
+        Rlog.i(LOG_TAG, "[GsmCdmaPhone] " + s);
+    }
+
     private void loge(String s) {
         Rlog.e(LOG_TAG, "[GsmCdmaPhone] " + s);
     }
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index 04bb568..59195f8 100644
--- a/src/java/com/android/internal/telephony/InboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -20,7 +20,7 @@
 import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
 
 import android.app.Activity;
-import android.app.ActivityManagerNative;
+import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.BroadcastOptions;
 import android.app.Notification;
@@ -64,6 +64,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.util.NotificationChannelController;
 import com.android.internal.util.HexDump;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
@@ -145,7 +146,7 @@
     /** Sent on exit from {@link WaitingState} to return to idle after sending all broadcasts. */
     private static final int EVENT_RETURN_TO_IDLE = 4;
 
-    /** Release wakelock after a short timeout when returning to idle state. */
+    /** Release wakelock after {@link mWakeLockTimeout} when returning to idle state. */
     private static final int EVENT_RELEASE_WAKELOCK = 5;
 
     /** Sent by {@link SmsBroadcastUndelivered} after cleaning the raw table. */
@@ -215,6 +216,9 @@
     private static String ACTION_OPEN_SMS_APP =
         "com.android.internal.telephony.OPEN_DEFAULT_SMS_APP";
 
+    /** Timeout for releasing wakelock */
+    private int mWakeLockTimeout;
+
     /**
      * Create a new SMS broadcast helper.
      * @param name the class name for logging
@@ -324,6 +328,14 @@
      */
     private class StartupState extends State {
         @Override
+        public void enter() {
+            if (DBG) log("entering Startup state");
+            // Set wakelock timeout to 0 during startup, this will ensure that the wakelock is not
+            // held if there are no pending messages to be handled.
+            setWakeLockTimeout(0);
+        }
+
+        @Override
         public boolean processMessage(Message msg) {
             log("StartupState.processMessage:" + msg.what);
             switch (msg.what) {
@@ -355,7 +367,7 @@
         @Override
         public void enter() {
             if (DBG) log("entering Idle state");
-            sendMessageDelayed(EVENT_RELEASE_WAKELOCK, WAKELOCK_TIMEOUT);
+            sendMessageDelayed(EVENT_RELEASE_WAKELOCK, getWakeLockTimeout());
         }
 
         @Override
@@ -482,6 +494,14 @@
      */
     private class WaitingState extends State {
         @Override
+        public void exit() {
+            if (DBG) log("exiting Waiting state");
+            // Before moving to idle state, set wakelock timeout to WAKE_LOCK_TIMEOUT milliseconds
+            // to give any receivers time to take their own wake locks
+            setWakeLockTimeout(WAKELOCK_TIMEOUT);
+        }
+
+        @Override
         public boolean processMessage(Message msg) {
             log("WaitingState.processMessage:" + msg.what);
             switch (msg.what) {
@@ -641,6 +661,9 @@
             // broadcast SMS_REJECTED_ACTION intent
             Intent intent = new Intent(Intents.SMS_REJECTED_ACTION);
             intent.putExtra("result", result);
+            // Allow registered broadcast receivers to get this intent even
+            // when they are in the background.
+            intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
             mContext.sendBroadcast(intent, android.Manifest.permission.RECEIVE_SMS);
         }
         acknowledgeLastIncomingSms(success, result, response);
@@ -914,7 +937,8 @@
                 .setDefaults(Notification.DEFAULT_ALL)
                 .setContentTitle(mContext.getString(R.string.new_sms_notification_title))
                 .setContentText(mContext.getString(R.string.new_sms_notification_content))
-                .setContentIntent(intent);
+                .setContentIntent(intent)
+                .setChannelId(NotificationChannelController.CHANNEL_ID_SMS);
         NotificationManager mNotificationManager =
             (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
         mNotificationManager.notify(
@@ -997,7 +1021,7 @@
             // Get a list of currently started users.
             int[] users = null;
             try {
-                users = ActivityManagerNative.getDefault().getRunningUserIds();
+                users = ActivityManager.getService().getRunningUserIds();
             } catch (RemoteException re) {
             }
             if (users == null) {
@@ -1066,7 +1090,8 @@
     }
 
     /**
-     * Creates and dispatches the intent to the default SMS app or the appropriate port.
+     * Creates and dispatches the intent to the default SMS app, appropriate port or via the {@link
+     * AppSmsManager}.
      *
      * @param pdus message pdus
      * @param format the message format, typically "3gpp" or "3gpp2"
@@ -1074,7 +1099,7 @@
      * @param resultReceiver the receiver handling the delivery result
      */
     private void dispatchSmsDeliveryIntent(byte[][] pdus, String format, int destPort,
-            BroadcastReceiver resultReceiver) {
+            SmsBroadcastReceiver resultReceiver) {
         Intent intent = new Intent();
         intent.putExtra("pdus", pdus);
         intent.putExtra("format", format);
@@ -1102,11 +1127,22 @@
                     intent.putExtra("uri", uri.toString());
                 }
             }
+
+            // Handle app specific sms messages.
+            AppSmsManager appManager = mPhone.getAppSmsManager();
+            if (appManager.handleSmsReceivedIntent(intent)) {
+                // The AppSmsManager handled this intent, we're done.
+                dropSms(resultReceiver);
+                return;
+            }
         } else {
             intent.setAction(Intents.DATA_SMS_RECEIVED_ACTION);
             Uri uri = Uri.parse("sms://localhost:" + destPort);
             intent.setData(uri);
             intent.setComponent(null);
+            // Allow registered broadcast receivers to get this intent even
+            // when they are in the background.
+            intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
         }
 
         Bundle options = handleSmsWhitelisting(intent.getComponent());
@@ -1260,8 +1296,10 @@
                 intent.setComponent(null);
                 // All running users will be notified of the received sms.
                 Bundle options = handleSmsWhitelisting(null);
+
                 dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
-                        AppOpsManager.OP_RECEIVE_SMS, options, this, UserHandle.ALL);
+                        AppOpsManager.OP_RECEIVE_SMS,
+                        options, this, UserHandle.ALL);
             } else if (action.equals(Intents.WAP_PUSH_DELIVER_ACTION)) {
                 // Now dispatch the notification only intent
                 intent.setAction(Intents.WAP_PUSH_RECEIVED_ACTION);
@@ -1500,7 +1538,14 @@
 
     @VisibleForTesting
     public int getWakeLockTimeout() {
-        return WAKELOCK_TIMEOUT;
+        return mWakeLockTimeout;
+    }
+
+    /**
+    * Sets the wakelock timeout to {@link timeOut} milliseconds
+    */
+    private void setWakeLockTimeout(int timeOut) {
+        mWakeLockTimeout = timeOut;
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/IntentBroadcaster.java b/src/java/com/android/internal/telephony/IntentBroadcaster.java
new file mode 100644
index 0000000..7528819
--- /dev/null
+++ b/src/java/com/android/internal/telephony/IntentBroadcaster.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.UserHandle;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * This class is used to broadcast intents that need to be rebroadcast after the device is unlocked.
+ * NOTE: Currently this is used only for SIM_STATE_CHANGED so logic is hardcoded for that;
+ * for example broadcasts are always sticky, only the last intent for the slotId is rebroadcast,
+ * etc.
+ */
+public class IntentBroadcaster {
+    private static final String TAG = "IntentBroadcaster";
+
+    private Map<Integer, Intent> mRebroadcastIntents = new HashMap<>();
+    private static IntentBroadcaster sIntentBroadcaster;
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
+                synchronized (mRebroadcastIntents) {
+                    // rebroadcast intents
+                    Iterator iterator = mRebroadcastIntents.entrySet().iterator();
+                    while (iterator.hasNext()) {
+                        Map.Entry pair = (Map.Entry) iterator.next();
+                        Intent i = (Intent) pair.getValue();
+                        i.putExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK, true);
+                        iterator.remove();
+                        logd("Rebroadcasting intent " + i.getAction() + " "
+                                + i.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE)
+                                + " for slotId " + pair.getKey());
+                        ActivityManager.broadcastStickyIntent(i, UserHandle.USER_ALL);
+                    }
+                }
+            }
+        }
+    };
+
+    private IntentBroadcaster(Context context) {
+        context.registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED));
+    }
+
+    /**
+     * Method to get an instance of IntentBroadcaster after creating one if needed.
+     * @return IntentBroadcaster instance
+     */
+    public static IntentBroadcaster getInstance(Context context) {
+        if (sIntentBroadcaster == null) {
+            sIntentBroadcaster = new IntentBroadcaster(context);
+        }
+        return sIntentBroadcaster;
+    }
+
+    public static IntentBroadcaster getInstance() {
+        return sIntentBroadcaster;
+    }
+
+    /**
+     * Wrapper for ActivityManager.broadcastStickyIntent() that also stores intent to be rebroadcast
+     * on USER_UNLOCKED
+     */
+    public void broadcastStickyIntent(Intent intent, int slotId) {
+        logd("Broadcasting and adding intent for rebroadcast: " + intent.getAction() + " "
+                + intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE)
+                + " for slotId " + slotId);
+        synchronized (mRebroadcastIntents) {
+            ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL);
+            mRebroadcastIntents.put(slotId, intent);
+        }
+    }
+
+    private void logd(String s) {
+        Log.d(TAG, s);
+    }
+}
diff --git a/src/java/com/android/internal/telephony/MccTable.java b/src/java/com/android/internal/telephony/MccTable.java
index 07d7af9..5714b29 100644
--- a/src/java/com/android/internal/telephony/MccTable.java
+++ b/src/java/com/android/internal/telephony/MccTable.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.telephony;
 
-import android.app.ActivityManagerNative;
+import android.app.ActivityManager;
 import android.app.AlarmManager;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -97,7 +97,21 @@
         Locale locale = new Locale("", entry.mIso);
         String[] tz = TimeZoneNames.forLocale(locale);
         if (tz.length == 0) return null;
-        return tz[0];
+
+        String zoneName = tz[0];
+
+        /* Use Australia/Sydney instead of Australia/Lord_Howe for Australia.
+         * http://b/33228250
+         * Todo: remove the code, see b/62418027
+         */
+        if (mcc == 505  /* Australia / Norfolk Island */) {
+            for (String zone : tz) {
+                if (zone.contains("Sydney")) {
+                    zoneName = zone;
+                }
+            }
+        }
+        return zoneName;
     }
 
     /**
@@ -214,7 +228,7 @@
 
                     if (updateConfig) {
                         Slog.d(LOG_TAG, "updateMccMncConfiguration updateConfig config=" + config);
-                        ActivityManagerNative.getDefault().updateConfiguration(config);
+                        ActivityManager.getService().updateConfiguration(config);
                     } else {
                         Slog.d(LOG_TAG, "updateMccMncConfiguration nothing to update");
                     }
@@ -362,7 +376,14 @@
      */
     private static void setTimezoneFromMccIfNeeded(Context context, int mcc) {
         String timezone = SystemProperties.get(ServiceStateTracker.TIMEZONE_PROPERTY);
-        if (timezone == null || timezone.length() == 0) {
+        // timezone.equals("GMT") will be true and only true if the timezone was
+        // set to a default value by the system server (when starting, system server.
+        // sets the persist.sys.timezone to "GMT" if it's not set)."GMT" is not used by
+        // any code that sets it explicitly (in case where something sets GMT explicitly,
+        // "Etc/GMT" Olsen ID would be used).
+        // TODO(b/64056758): Remove "timezone.equals("GMT")" hack when there's a
+        // better way of telling if the value has been defaulted.
+        if (timezone == null || timezone.length() == 0 || timezone.equals("GMT")) {
             String zoneId = defaultTimeZoneForMcc(mcc);
             if (zoneId != null && zoneId.length() > 0) {
                 // Set time zone based on MCC
diff --git a/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java b/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
new file mode 100644
index 0000000..14c6810
--- /dev/null
+++ b/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
@@ -0,0 +1,538 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static android.telephony.RadioNetworkConstants.RadioAccessNetworks.EUTRAN;
+import static android.telephony.RadioNetworkConstants.RadioAccessNetworks.GERAN;
+import static android.telephony.RadioNetworkConstants.RadioAccessNetworks.UTRAN;
+
+import android.hardware.radio.V1_0.RadioError;
+import android.os.AsyncResult;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Process;
+import android.os.RemoteException;
+import android.telephony.CellInfo;
+import android.telephony.NetworkScan;
+import android.telephony.NetworkScanRequest;
+import android.telephony.RadioAccessSpecifier;
+import android.telephony.TelephonyScanManager;
+import android.util.Log;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Manages radio access network scan requests.
+ *
+ * Provides methods to start and stop network scan requests, and keeps track of all the live scans.
+ *
+ * {@hide}
+ */
+public final class NetworkScanRequestTracker {
+
+    private static final String TAG = "ScanRequestTracker";
+
+    private static final int CMD_START_NETWORK_SCAN = 1;
+    private static final int EVENT_START_NETWORK_SCAN_DONE = 2;
+    private static final int EVENT_RECEIVE_NETWORK_SCAN_RESULT = 3;
+    private static final int CMD_STOP_NETWORK_SCAN = 4;
+    private static final int EVENT_STOP_NETWORK_SCAN_DONE = 5;
+    private static final int CMD_INTERRUPT_NETWORK_SCAN = 6;
+    private static final int EVENT_INTERRUPT_NETWORK_SCAN_DONE = 7;
+
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case CMD_START_NETWORK_SCAN:
+                    mScheduler.doStartScan((NetworkScanRequestInfo) msg.obj);
+                    break;
+
+                case EVENT_START_NETWORK_SCAN_DONE:
+                    mScheduler.startScanDone((AsyncResult) msg.obj);
+                    break;
+
+                case EVENT_RECEIVE_NETWORK_SCAN_RESULT:
+                    mScheduler.receiveResult((AsyncResult) msg.obj);
+                    break;
+
+                case CMD_STOP_NETWORK_SCAN:
+                    mScheduler.doStopScan(msg.arg1);
+                    break;
+
+                case EVENT_STOP_NETWORK_SCAN_DONE:
+                    mScheduler.stopScanDone((AsyncResult) msg.obj);
+                    break;
+
+                case CMD_INTERRUPT_NETWORK_SCAN:
+                    mScheduler.doInterruptScan(msg.arg1);
+                    break;
+
+                case EVENT_INTERRUPT_NETWORK_SCAN_DONE:
+                    mScheduler.interruptScanDone((AsyncResult) msg.obj);
+                    break;
+            }
+        }
+    };
+
+    // The sequence number of NetworkScanRequests
+    private final AtomicInteger mNextNetworkScanRequestId = new AtomicInteger(1);
+    private final NetworkScanRequestScheduler mScheduler = new NetworkScanRequestScheduler();
+
+    private void logEmptyResultOrException(AsyncResult ar) {
+        if (ar.result == null) {
+            Log.e(TAG, "NetworkScanResult: Empty result");
+        } else {
+            Log.e(TAG, "NetworkScanResult: Exception: " + ar.exception);
+        }
+    }
+
+    private boolean isValidScan(NetworkScanRequestInfo nsri) {
+        if (nsri.mRequest.specifiers == null) {
+            return false;
+        }
+        if (nsri.mRequest.specifiers.length > NetworkScanRequest.MAX_RADIO_ACCESS_NETWORKS) {
+            return false;
+        }
+        for (RadioAccessSpecifier ras : nsri.mRequest.specifiers) {
+            if (ras.radioAccessNetwork != GERAN && ras.radioAccessNetwork != UTRAN
+                    && ras.radioAccessNetwork != EUTRAN) {
+                return false;
+            }
+            if (ras.bands != null && ras.bands.length > NetworkScanRequest.MAX_BANDS) {
+                return false;
+            }
+            if (ras.channels != null && ras.channels.length > NetworkScanRequest.MAX_CHANNELS) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /** Sends a message back to the application via its callback. */
+    private void notifyMessenger(NetworkScanRequestInfo nsri, int what, int err,
+            List<CellInfo> result) {
+        Messenger messenger = nsri.mMessenger;
+        Message message = Message.obtain();
+        message.what = what;
+        message.arg1 = err;
+        message.arg2 = nsri.mScanId;
+        if (result != null) {
+            CellInfo[] ci = result.toArray(new CellInfo[result.size()]);
+            Bundle b = new Bundle();
+            b.putParcelableArray(TelephonyScanManager.SCAN_RESULT_KEY, ci);
+            message.setData(b);
+        } else {
+            message.obj = null;
+        }
+        try {
+            messenger.send(message);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception in notifyMessenger: " + e);
+        }
+    }
+
+    /**
+    * Tracks info about the radio network scan.
+     *
+    * Also used to notice when the calling process dies so we can self-expire.
+    */
+    class NetworkScanRequestInfo implements IBinder.DeathRecipient {
+        private final NetworkScanRequest mRequest;
+        private final Messenger mMessenger;
+        private final IBinder mBinder;
+        private final Phone mPhone;
+        private final int mScanId;
+        private final int mUid;
+        private final int mPid;
+        private boolean mIsBinderDead;
+
+        NetworkScanRequestInfo(NetworkScanRequest r, Messenger m, IBinder b, int id, Phone phone) {
+            super();
+            mRequest = r;
+            mMessenger = m;
+            mBinder = b;
+            mScanId = id;
+            mPhone = phone;
+            mUid = Binder.getCallingUid();
+            mPid = Binder.getCallingPid();
+            mIsBinderDead = false;
+
+            try {
+                mBinder.linkToDeath(this, 0);
+            } catch (RemoteException e) {
+                binderDied();
+            }
+        }
+
+        synchronized void setIsBinderDead(boolean val) {
+            mIsBinderDead = val;
+        }
+
+        synchronized boolean getIsBinderDead() {
+            return mIsBinderDead;
+        }
+
+        NetworkScanRequest getRequest() {
+            return mRequest;
+        }
+
+        void unlinkDeathRecipient() {
+            if (mBinder != null) {
+                mBinder.unlinkToDeath(this, 0);
+            }
+        }
+
+        @Override
+        public void binderDied() {
+            Log.e(TAG, "PhoneInterfaceManager NetworkScanRequestInfo binderDied("
+                    + mRequest + ", " + mBinder + ")");
+            setIsBinderDead(true);
+            interruptNetworkScan(mScanId);
+        }
+    }
+
+    /**
+     * Handles multiplexing and scheduling for multiple requests.
+     */
+    private class NetworkScanRequestScheduler {
+
+        private NetworkScanRequestInfo mLiveRequestInfo;
+        private NetworkScanRequestInfo mPendingRequestInfo;
+
+        private int rilErrorToScanError(int rilError) {
+            switch (rilError) {
+                case RadioError.NONE:
+                    return NetworkScan.SUCCESS;
+                case RadioError.RADIO_NOT_AVAILABLE:
+                    Log.e(TAG, "rilErrorToScanError: RADIO_NOT_AVAILABLE");
+                    return NetworkScan.ERROR_MODEM_ERROR;
+                case RadioError.REQUEST_NOT_SUPPORTED:
+                    Log.e(TAG, "rilErrorToScanError: REQUEST_NOT_SUPPORTED");
+                    return NetworkScan.ERROR_UNSUPPORTED;
+                case RadioError.NO_MEMORY:
+                    Log.e(TAG, "rilErrorToScanError: NO_MEMORY");
+                    return NetworkScan.ERROR_MODEM_ERROR;
+                case RadioError.INTERNAL_ERR:
+                    Log.e(TAG, "rilErrorToScanError: INTERNAL_ERR");
+                    return NetworkScan.ERROR_MODEM_ERROR;
+                case RadioError.MODEM_ERR:
+                    Log.e(TAG, "rilErrorToScanError: MODEM_ERR");
+                    return NetworkScan.ERROR_MODEM_ERROR;
+                case RadioError.OPERATION_NOT_ALLOWED:
+                    Log.e(TAG, "rilErrorToScanError: OPERATION_NOT_ALLOWED");
+                    return NetworkScan.ERROR_MODEM_ERROR;
+                case RadioError.INVALID_ARGUMENTS:
+                    Log.e(TAG, "rilErrorToScanError: INVALID_ARGUMENTS");
+                    return NetworkScan.ERROR_INVALID_SCAN;
+                case RadioError.DEVICE_IN_USE:
+                    Log.e(TAG, "rilErrorToScanError: DEVICE_IN_USE");
+                    return NetworkScan.ERROR_MODEM_BUSY;
+                default:
+                    Log.e(TAG, "rilErrorToScanError: Unexpected RadioError " +  rilError);
+                    return NetworkScan.ERROR_RIL_ERROR;
+            }
+        }
+
+        private int commandExceptionErrorToScanError(CommandException.Error error) {
+            switch (error) {
+                case RADIO_NOT_AVAILABLE:
+                    Log.e(TAG, "commandExceptionErrorToScanError: RADIO_NOT_AVAILABLE");
+                    return NetworkScan.ERROR_MODEM_ERROR;
+                case REQUEST_NOT_SUPPORTED:
+                    Log.e(TAG, "commandExceptionErrorToScanError: REQUEST_NOT_SUPPORTED");
+                    return NetworkScan.ERROR_UNSUPPORTED;
+                case NO_MEMORY:
+                    Log.e(TAG, "commandExceptionErrorToScanError: NO_MEMORY");
+                    return NetworkScan.ERROR_MODEM_ERROR;
+                case INTERNAL_ERR:
+                    Log.e(TAG, "commandExceptionErrorToScanError: INTERNAL_ERR");
+                    return NetworkScan.ERROR_MODEM_ERROR;
+                case MODEM_ERR:
+                    Log.e(TAG, "commandExceptionErrorToScanError: MODEM_ERR");
+                    return NetworkScan.ERROR_MODEM_ERROR;
+                case OPERATION_NOT_ALLOWED:
+                    Log.e(TAG, "commandExceptionErrorToScanError: OPERATION_NOT_ALLOWED");
+                    return NetworkScan.ERROR_MODEM_ERROR;
+                case INVALID_ARGUMENTS:
+                    Log.e(TAG, "commandExceptionErrorToScanError: INVALID_ARGUMENTS");
+                    return NetworkScan.ERROR_INVALID_SCAN;
+                case DEVICE_IN_USE:
+                    Log.e(TAG, "commandExceptionErrorToScanError: DEVICE_IN_USE");
+                    return NetworkScan.ERROR_MODEM_BUSY;
+                default:
+                    Log.e(TAG, "commandExceptionErrorToScanError: Unexpected CommandExceptionError "
+                            +  error);
+                    return NetworkScan.ERROR_RIL_ERROR;
+            }
+        }
+
+        private void doStartScan(NetworkScanRequestInfo nsri) {
+            if (nsri == null) {
+                Log.e(TAG, "CMD_START_NETWORK_SCAN: nsri is null");
+                return;
+            }
+            if (!isValidScan(nsri)) {
+                notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_ERROR,
+                        NetworkScan.ERROR_INVALID_SCAN, null);
+                return;
+            }
+            if (nsri.getIsBinderDead()) {
+                Log.e(TAG, "CMD_START_NETWORK_SCAN: Binder has died");
+                return;
+            }
+            if (!startNewScan(nsri)) {
+                if (!interruptLiveScan(nsri)) {
+                    if (!cacheScan(nsri)) {
+                        notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_ERROR,
+                                NetworkScan.ERROR_MODEM_BUSY, null);
+                    }
+                }
+            }
+        }
+
+        private synchronized void startScanDone(AsyncResult ar) {
+            NetworkScanRequestInfo nsri = (NetworkScanRequestInfo) ar.userObj;
+            if (nsri == null) {
+                Log.e(TAG, "EVENT_START_NETWORK_SCAN_DONE: nsri is null");
+                return;
+            }
+            if (mLiveRequestInfo == null || nsri.mScanId != mLiveRequestInfo.mScanId) {
+                Log.e(TAG, "EVENT_START_NETWORK_SCAN_DONE: nsri does not match mLiveRequestInfo");
+                return;
+            }
+            if (ar.exception == null && ar.result != null) {
+                // Register for the scan results if the scan started successfully.
+                nsri.mPhone.mCi.registerForNetworkScanResult(mHandler,
+                        EVENT_RECEIVE_NETWORK_SCAN_RESULT, nsri);
+            } else {
+                logEmptyResultOrException(ar);
+                if (ar.exception != null) {
+                    CommandException.Error error =
+                            ((CommandException) (ar.exception)).getCommandError();
+                    deleteScanAndMayNotify(nsri, commandExceptionErrorToScanError(error), true);
+                } else {
+                    Log.wtf(TAG, "EVENT_START_NETWORK_SCAN_DONE: ar.exception can not be null!");
+                }
+            }
+        }
+
+        private void receiveResult(AsyncResult ar) {
+            NetworkScanRequestInfo nsri = (NetworkScanRequestInfo) ar.userObj;
+            if (nsri == null) {
+                Log.e(TAG, "EVENT_RECEIVE_NETWORK_SCAN_RESULT: nsri is null");
+                return;
+            }
+            if (ar.exception == null && ar.result != null) {
+                NetworkScanResult nsr = (NetworkScanResult) ar.result;
+                if (nsr.scanError == NetworkScan.SUCCESS) {
+                    notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_RESULTS,
+                            rilErrorToScanError(nsr.scanError), nsr.networkInfos);
+                    if (nsr.scanStatus == NetworkScanResult.SCAN_STATUS_COMPLETE) {
+                        deleteScanAndMayNotify(nsri, NetworkScan.SUCCESS, true);
+                        nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
+                    }
+                } else {
+                    if (nsr.networkInfos != null) {
+                        notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_RESULTS,
+                                NetworkScan.SUCCESS, nsr.networkInfos);
+                    }
+                    deleteScanAndMayNotify(nsri, rilErrorToScanError(nsr.scanError), true);
+                    nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
+                }
+            } else {
+                logEmptyResultOrException(ar);
+                deleteScanAndMayNotify(nsri, NetworkScan.ERROR_RIL_ERROR, true);
+                nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
+            }
+        }
+
+
+        // Stops the scan if the scanId and uid match the mScanId and mUid.
+        // If the scan to be stopped is the live scan, we only send the request to RIL, while the
+        // mLiveRequestInfo will not be cleared and the user will not be notified either.
+        // If the scan to be stopped is the pending scan, we will clear mPendingRequestInfo and
+        // notify the user.
+        private synchronized void doStopScan(int scanId) {
+            if (mLiveRequestInfo != null && scanId == mLiveRequestInfo.mScanId) {
+                mLiveRequestInfo.mPhone.stopNetworkScan(
+                        mHandler.obtainMessage(EVENT_STOP_NETWORK_SCAN_DONE, mLiveRequestInfo));
+            } else if (mPendingRequestInfo != null && scanId == mPendingRequestInfo.mScanId) {
+                notifyMessenger(mPendingRequestInfo,
+                        TelephonyScanManager.CALLBACK_SCAN_COMPLETE, NetworkScan.SUCCESS, null);
+                mPendingRequestInfo = null;
+            } else {
+                Log.e(TAG, "stopScan: scan " + scanId + " does not exist!");
+            }
+        }
+
+        private void stopScanDone(AsyncResult ar) {
+            NetworkScanRequestInfo nsri = (NetworkScanRequestInfo) ar.userObj;
+            if (nsri == null) {
+                Log.e(TAG, "EVENT_STOP_NETWORK_SCAN_DONE: nsri is null");
+                return;
+            }
+            if (ar.exception == null && ar.result != null) {
+                deleteScanAndMayNotify(nsri, NetworkScan.SUCCESS, true);
+            } else {
+                logEmptyResultOrException(ar);
+                if (ar.exception != null) {
+                    CommandException.Error error =
+                            ((CommandException) (ar.exception)).getCommandError();
+                    deleteScanAndMayNotify(nsri, commandExceptionErrorToScanError(error), true);
+                } else {
+                    Log.wtf(TAG, "EVENT_STOP_NETWORK_SCAN_DONE: ar.exception can not be null!");
+                }
+            }
+            nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
+        }
+
+        // Interrupts the live scan is the scanId matches the mScanId of the mLiveRequestInfo.
+        private synchronized void doInterruptScan(int scanId) {
+            if (mLiveRequestInfo != null && scanId == mLiveRequestInfo.mScanId) {
+                mLiveRequestInfo.mPhone.stopNetworkScan(mHandler.obtainMessage(
+                        EVENT_INTERRUPT_NETWORK_SCAN_DONE, mLiveRequestInfo));
+            } else {
+                Log.e(TAG, "doInterruptScan: scan " + scanId + " does not exist!");
+            }
+        }
+
+        private void interruptScanDone(AsyncResult ar) {
+            NetworkScanRequestInfo nsri = (NetworkScanRequestInfo) ar.userObj;
+            if (nsri == null) {
+                Log.e(TAG, "EVENT_INTERRUPT_NETWORK_SCAN_DONE: nsri is null");
+                return;
+            }
+            nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
+            deleteScanAndMayNotify(nsri, 0, false);
+        }
+
+        // Interrupts the live scan and caches nsri in mPendingRequestInfo. Once the live scan is
+        // stopped, a new scan will automatically start with nsri.
+        // The new scan can interrupt the live scan only when all the below requirements are met:
+        //   1. There is 1 live scan and no other pending scan
+        //   2. The new scan is requested by system process
+        //   3. The live scan is not requested by system process
+        private synchronized boolean interruptLiveScan(NetworkScanRequestInfo nsri) {
+            if (mLiveRequestInfo != null && mPendingRequestInfo == null
+                    && nsri.mUid == Process.SYSTEM_UID
+                            && mLiveRequestInfo.mUid != Process.SYSTEM_UID) {
+                doInterruptScan(mLiveRequestInfo.mScanId);
+                mPendingRequestInfo = nsri;
+                notifyMessenger(mLiveRequestInfo, TelephonyScanManager.CALLBACK_SCAN_ERROR,
+                        NetworkScan.ERROR_INTERRUPTED, null);
+                return true;
+            }
+            return false;
+        }
+
+        private boolean cacheScan(NetworkScanRequestInfo nsri) {
+            // TODO(30954762): Cache periodic scan for OC-MR1.
+            return false;
+        }
+
+        // Starts a new scan with nsri if there is no live scan running.
+        private synchronized boolean startNewScan(NetworkScanRequestInfo nsri) {
+            if (mLiveRequestInfo == null) {
+                mLiveRequestInfo = nsri;
+                nsri.mPhone.startNetworkScan(nsri.getRequest(),
+                        mHandler.obtainMessage(EVENT_START_NETWORK_SCAN_DONE, nsri));
+                return true;
+            }
+            return false;
+        }
+
+
+        // Deletes the mLiveRequestInfo and notify the user if it matches nsri.
+        private synchronized void deleteScanAndMayNotify(NetworkScanRequestInfo nsri, int error,
+                boolean notify) {
+            if (mLiveRequestInfo != null && nsri.mScanId == mLiveRequestInfo.mScanId) {
+                if (notify) {
+                    if (error == NetworkScan.SUCCESS) {
+                        notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_COMPLETE, error,
+                                null);
+                    } else {
+                        notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_ERROR, error,
+                                null);
+                    }
+                }
+                mLiveRequestInfo = null;
+                if (mPendingRequestInfo != null) {
+                    startNewScan(mPendingRequestInfo);
+                    mPendingRequestInfo = null;
+                }
+            }
+        }
+    }
+
+    /**
+     * Interrupts an ongoing network scan
+     *
+     * This method is similar to stopNetworkScan, since they both stops an ongoing scan. The
+     * difference is that stopNetworkScan is only used by the callers to stop their own scans, so
+     * sanity check will be done to make sure the request is valid; while this method is only
+     * internally used by NetworkScanRequestTracker so sanity check is not needed.
+     */
+    private void interruptNetworkScan(int scanId) {
+        // scanId will be stored at Message.arg1
+        mHandler.obtainMessage(CMD_INTERRUPT_NETWORK_SCAN, scanId, 0).sendToTarget();
+    }
+
+    /**
+     * Starts a new network scan
+     *
+     * This function only wraps all the incoming information and delegate then to the handler thread
+     * which will actually handles the scan request. So a new scanId will always be generated and
+     * returned to the user, no matter how this scan will be actually handled.
+     */
+    public int startNetworkScan(
+            NetworkScanRequest request, Messenger messenger, IBinder binder, Phone phone) {
+        int scanId = mNextNetworkScanRequestId.getAndIncrement();
+        NetworkScanRequestInfo nsri =
+                new NetworkScanRequestInfo(request, messenger, binder, scanId, phone);
+        // nsri will be stored as Message.obj
+        mHandler.obtainMessage(CMD_START_NETWORK_SCAN, nsri).sendToTarget();
+        return scanId;
+    }
+
+    /**
+     * Stops an ongoing network scan
+     *
+     * The ongoing scan will be stopped only when the input scanId and caller's uid matches the
+     * corresponding information associated with it.
+     */
+    public void stopNetworkScan(int scanId) {
+        synchronized (mScheduler) {
+            if ((mScheduler.mLiveRequestInfo != null
+                    && scanId == mScheduler.mLiveRequestInfo.mScanId
+                    && Binder.getCallingUid() == mScheduler.mLiveRequestInfo.mUid)
+                    || (mScheduler.mPendingRequestInfo != null
+                    && scanId == mScheduler.mPendingRequestInfo.mScanId
+                    && Binder.getCallingUid() == mScheduler.mPendingRequestInfo.mUid)) {
+                // scanId will be stored at Message.arg1
+                mHandler.obtainMessage(CMD_STOP_NETWORK_SCAN, scanId, 0).sendToTarget();
+            } else {
+                throw new IllegalArgumentException("Scan with id: " + scanId + " does not exist!");
+            }
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 8f2f048..fad0ddb 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -23,6 +23,7 @@
 import android.content.SharedPreferences;
 import android.net.LinkProperties;
 import android.net.NetworkCapabilities;
+import android.net.NetworkStats;
 import android.net.Uri;
 import android.net.wifi.WifiManager;
 import android.os.AsyncResult;
@@ -44,6 +45,7 @@
 import android.telephony.CellInfoCdma;
 import android.telephony.CellLocation;
 import android.telephony.ClientRequestStats;
+import android.telephony.ImsiEncryptionInfo;
 import android.telephony.PhoneStateListener;
 import android.telephony.RadioAccessFamily;
 import android.telephony.Rlog;
@@ -57,7 +59,9 @@
 import com.android.ims.ImsConfig;
 import com.android.ims.ImsManager;
 import com.android.internal.R;
+import com.android.internal.telephony.dataconnection.DataConnectionReasons;
 import com.android.internal.telephony.dataconnection.DcTracker;
+import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneCall;
 import com.android.internal.telephony.test.SimulatedRadioControl;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
@@ -95,6 +99,8 @@
 
     protected final static Object lockForRadioTechnologyChange = new Object();
 
+    protected final int USSD_MAX_QUEUE = 10;
+
     private BroadcastReceiver mImsIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -137,6 +143,9 @@
     // Key used to read/write "disable data connection on boot" pref (used for testing)
     public static final String DATA_DISABLED_ON_BOOT_KEY = "disabled_on_boot_key";
 
+    // Key used to read/write data_roaming_is_user_setting pref
+    public static final String DATA_ROAMING_IS_USER_SETTING_KEY = "data_roaming_is_user_setting_key";
+
     /* Event Constants */
     protected static final int EVENT_RADIO_AVAILABLE             = 1;
     /** Supplementary Service Notification received. */
@@ -215,6 +224,9 @@
     // Key used to read/write "disable DNS server check" pref (used for testing)
     private static final String DNS_SERVER_CHECK_DISABLED_KEY = "dns_server_check_disabled_key";
 
+    // Integer used to let the calling application know that the we are ignoring auto mode switch.
+    private static final int ALREADY_IN_AUTO_SELECTION = 1;
+
     /**
      * Small container class used to hold information relevant to
      * the carrier selection process. operatorNumeric can be ""
@@ -241,6 +253,7 @@
     private int mCallRingContinueToken;
     private int mCallRingDelay;
     private boolean mIsVoiceCapable = true;
+    private final AppSmsManager mAppSmsManager;
     private SimActivationTracker mSimActivationTracker;
     // Keep track of whether or not the phone is in Emergency Callback Mode for Phone and
     // subclasses
@@ -275,6 +288,11 @@
     protected TelephonyComponentFactory mTelephonyComponentFactory;
 
     //IMS
+    /**
+     * {@link CallStateException} message text used to indicate that an IMS call has failed because
+     * it needs to be retried using GSM or CDMA (e.g. CS fallback).
+     * TODO: Replace this with a proper exception; {@link CallStateException} doesn't make sense.
+     */
     public static final String CS_FALLBACK = "cs_fallback";
     public static final String EXTRA_KEY_ALERT_TITLE = "alertTitle";
     public static final String EXTRA_KEY_ALERT_MESSAGE = "alertMessage";
@@ -446,6 +464,7 @@
         mCi = ci;
         mActionDetached = this.getClass().getPackage().getName() + ".action_detached";
         mActionAttached = this.getClass().getPackage().getName() + ".action_attached";
+        mAppSmsManager = telephonyComponentFactory.makeAppSmsManager(context);
 
         if (Build.IS_DEBUGGABLE) {
             mTelephonyTester = new TelephonyTester(this);
@@ -551,7 +570,6 @@
                 if (imsManager.isDynamicBinding() || imsManager.isServiceAvailable()) {
                     mImsServiceReady = true;
                     updateImsPhone();
-                    ImsManager.updateImsServiceConfig(mContext, mPhoneId, false);
                 }
             }
         }
@@ -1160,6 +1178,11 @@
             mCi.setNetworkSelectionModeAutomatic(msg);
         } else {
             Rlog.d(LOG_TAG, "setNetworkSelectionModeAutomatic - already auto, ignoring");
+            // let the calling application know that the we are ignoring automatic mode switch.
+            if (nsm.message != null) {
+                nsm.message.arg1 = ALREADY_IN_AUTO_SELECTION;
+            }
+
             ar.userObj = nsm;
             handleSetSelectNetwork(ar);
         }
@@ -1318,6 +1341,8 @@
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
         SharedPreferences.Editor editor = sp.edit();
         editor.putInt(CLIR_KEY + getPhoneId(), commandInterfaceCLIRMode);
+        Rlog.i(LOG_TAG, "saveClirSetting: " + CLIR_KEY + getPhoneId() + "=" +
+                commandInterfaceCLIRMode);
 
         // Commit and log the result.
         if (!editor.commit()) {
@@ -2147,8 +2172,6 @@
      * @return {@code true} if we are in emergency call back mode. This is a period where the phone
      * should be using as little power as possible and be ready to receive an incoming call from the
      * emergency operator.
-     *
-     * This method is overridden for GSM phones to return false always
      */
     public boolean isInEcm() {
         return mIsPhoneInEcmState;
@@ -2282,6 +2305,21 @@
     }
 
     /**
+     * send secret dialer codes to launch arbitrary activities.
+     * an Intent is started with the android_secret_code://<code> URI.
+     *
+     * @param code stripped version of secret code without *#*# prefix and #*#* suffix
+     */
+    public void sendDialerSpecialCode(String code) {
+        if (!TextUtils.isEmpty(code)) {
+            Intent intent = new Intent(TelephonyIntents.SECRET_CODE_ACTION,
+                    Uri.parse("android_secret_code://" + code));
+            intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+            mContext.sendBroadcast(intent);
+        }
+    }
+
+    /**
      * Returns the CDMA ERI icon index to display
      */
     public int getCdmaEriIconIndex() {
@@ -2731,12 +2769,25 @@
 
     /**
      * Report on whether data connectivity is allowed.
+     *
+     * @return True if data is allowed to be established.
      */
     public boolean isDataAllowed() {
         return ((mDcTracker != null) && (mDcTracker.isDataAllowed(null)));
     }
 
     /**
+     * Report on whether data connectivity is allowed.
+     *
+     * @param reasons The reasons that data can/can't be established. This is an output param.
+     * @return True if data is allowed to be established
+     */
+    public boolean isDataAllowed(DataConnectionReasons reasons) {
+        return ((mDcTracker != null) && (mDcTracker.isDataAllowed(reasons)));
+    }
+
+
+    /**
      * Action set from carrier signalling broadcast receivers to enable/disable metered apns.
      */
     public void carrierActionSetMeteredApnsEnabled(boolean enabled) {
@@ -2925,6 +2976,28 @@
     }
 
     /**
+     * Returns Carrier specific information that will be used to encrypt the IMSI and IMPI.
+     * @param keyType whether the key is being used for WLAN or ePDG.
+     * @return ImsiEncryptionInfo which includes the Key Type, the Public Key
+     *        {@link java.security.PublicKey} and the Key Identifier.
+     *        The keyIdentifier This is used by the server to help it locate the private key to
+     *        decrypt the permanent identity.
+     */
+    public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType) {
+        return null;
+    }
+
+    /**
+     * Sets the carrier information needed to encrypt the IMSI and IMPI.
+     * @param imsiEncryptionInfo Carrier specific information that will be used to encrypt the
+     *        IMSI and IMPI. This includes the Key type, the Public key
+     *        {@link java.security.PublicKey} and the Key identifier.
+     */
+    public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo) {
+        return;
+    }
+
+    /**
      * Return if UT capability of ImsPhone is enabled or not
      */
     public boolean isUtEnabled() {
@@ -3263,14 +3336,25 @@
     }
 
     /**
+     * Determines if the connection to IMS services are available yet.
+     * @return {@code true} if the connection to IMS services are available.
+     */
+    public boolean isImsAvailable() {
+        if (mImsPhone == null) {
+            return false;
+        }
+
+        return mImsPhone.isImsAvailable();
+    }
+
+    /**
      * Determines if video calling is enabled for the phone.
      *
      * @return {@code true} if video calling is enabled, {@code false} otherwise.
      */
     public boolean isVideoEnabled() {
         Phone imsPhone = mImsPhone;
-        if ((imsPhone != null)
-                && (imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)) {
+        if (imsPhone != null) {
             return imsPhone.isVideoEnabled();
         }
         return false;
@@ -3412,9 +3496,16 @@
         return this;
     }
 
-    public long getVtDataUsage() {
-        if (mImsPhone == null) return 0;
-        return mImsPhone.getVtDataUsage();
+    /**
+     * Get aggregated video call data usage since boot.
+     * Permissions android.Manifest.permission.READ_NETWORK_USAGE_HISTORY is required.
+     *
+     * @param perUidStats True if requesting data usage per uid, otherwise overall usage.
+     * @return Snapshot of video call data usage
+     */
+    public NetworkStats getVtDataUsage(boolean perUidStats) {
+        if (mImsPhone == null) return null;
+        return mImsPhone.getVtDataUsage(perUidStats);
     }
 
     /**
@@ -3434,6 +3525,10 @@
         return null;
     }
 
+    public AppSmsManager getAppSmsManager() {
+        return mAppSmsManager;
+    }
+
     /**
      * Set SIM card power state.
      * @param state State of SIM (power down, power up, pass through)
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index c506b85..4fcf37e 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -21,12 +21,15 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
 import android.net.LocalServerSocket;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.preference.PreferenceManager;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.telephony.Rlog;
@@ -43,11 +46,13 @@
 import com.android.internal.telephony.sip.SipPhoneFactory;
 import com.android.internal.telephony.uicc.IccCardProxy;
 import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.telephony.util.NotificationChannelController;
 import com.android.internal.util.IndentingPrintWriter;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.HashMap;
+import java.util.Map;
 
 /**
  * {@hide}
@@ -69,6 +74,7 @@
 
     static private ProxyController sProxyController;
     static private UiccController sUiccController;
+    private static IntentBroadcaster sIntentBroadcaster;
 
     static private CommandsInterface sCommandsInterface = null;
     static private SubscriptionInfoUpdater sSubInfoRecordUpdater = null;
@@ -80,6 +86,7 @@
     static private SubscriptionMonitor sSubscriptionMonitor;
     static private TelephonyNetworkFactory[] sTelephonyNetworkFactories;
     static private ImsResolver sImsResolver;
+    static private NotificationChannelController sNotificationChannelController;
 
     static private final HashMap<String, LocalLog>sLocalLogs = new HashMap<String, LocalLog>();
 
@@ -232,6 +239,10 @@
                 sProxyController = ProxyController.getInstance(context, sPhones,
                         sUiccController, sCommandsInterfaces, sPhoneSwitcher);
 
+                sIntentBroadcaster = IntentBroadcaster.getInstance(context);
+
+                sNotificationChannelController = new NotificationChannelController(context);
+
                 sTelephonyNetworkFactories = new TelephonyNetworkFactory[numPhones];
                 for (int i = 0; i < numPhones; i++) {
                     sTelephonyNetworkFactories[i] = new TelephonyNetworkFactory(
@@ -473,5 +484,22 @@
             pw.flush();
         }
         pw.decreaseIndent();
+        pw.println("++++++++++++++++++++++++++++++++");
+
+        pw.println("SharedPreferences:");
+        pw.increaseIndent();
+        try {
+            if (sContext != null) {
+                SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(sContext);
+                Map spValues = sp.getAll();
+                for (Object key : spValues.keySet()) {
+                    pw.println(key + " : " + spValues.get(key));
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        pw.flush();
+        pw.decreaseIndent();
     }
 }
diff --git a/src/java/com/android/internal/telephony/PhoneInternalInterface.java b/src/java/com/android/internal/telephony/PhoneInternalInterface.java
index 849cf99..6cf6358 100644
--- a/src/java/com/android/internal/telephony/PhoneInternalInterface.java
+++ b/src/java/com/android/internal/telephony/PhoneInternalInterface.java
@@ -23,6 +23,8 @@
 import android.os.WorkSource;
 import android.telephony.CarrierConfigManager;
 import android.telephony.CellLocation;
+import android.telephony.ImsiEncryptionInfo;
+import android.telephony.NetworkScanRequest;
 import android.telephony.ServiceState;
 
 import com.android.internal.telephony.PhoneConstants.*; // ????
@@ -105,6 +107,7 @@
     static final String REASON_CARRIER_CHANGE = "carrierChange";
     static final String REASON_CARRIER_ACTION_DISABLE_METERED_APN =
             "carrierActionDisableMeteredApn";
+    static final String REASON_CSS_INDICATOR_CHANGED = "cssIndicatorChanged";
 
     // Used for band mode selection methods
     static final int BM_UNSPECIFIED = RILConstants.BAND_MODE_UNSPECIFIED; // automatic
@@ -442,7 +445,8 @@
      * @param ussdRequest the USSD command to be executed.
      * @param wrappedCallback receives the callback result.
      */
-    boolean handleUssdRequest(String ussdRequest, ResultReceiver wrappedCallback);
+    boolean handleUssdRequest(String ussdRequest, ResultReceiver wrappedCallback)
+            throws CallStateException;
 
     /**
      * Handles in-call MMI commands. While in a call, or while receiving a
@@ -652,7 +656,7 @@
      * on failure.</li>
      * </ul>
      */
-    void startNetworkScan(Message response);
+    void startNetworkScan(NetworkScanRequest nsr, Message response);
 
     /**
      * Stop ongoing network scan. This method is asynchronous; .
@@ -825,4 +829,22 @@
      *            Callback message is empty on completion
      */
     public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response);
+
+    /*
+    * Sets the carrier information needed to encrypt the IMSI and IMPI.
+    * @param imsiEncryptionInfo Carrier specific information that will be used to encrypt the
+    *        IMSI and IMPI. This includes the Key type, the Public key
+    *        {@link java.security.PublicKey} and the Key identifier.
+    */
+    public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo);
+
+    /**
+     * Returns Carrier specific information that will be used to encrypt the IMSI and IMPI.
+     * @param keyType whether the key is being used for WLAN or ePDG.
+     * @return ImsiEncryptionInfo which includes the Key Type, the Public Key
+     *        {@link java.security.PublicKey} and the Key Identifier.
+     *        The keyIdentifier This is used by the server to help it locate the private key to
+     *        decrypt the permanent identity.
+     */
+    public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType);
 }
diff --git a/src/java/com/android/internal/telephony/PhoneSubInfoController.java b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
index a867d12..9cd088f 100644
--- a/src/java/com/android/internal/telephony/PhoneSubInfoController.java
+++ b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
@@ -24,6 +24,7 @@
 import android.os.Binder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.telephony.ImsiEncryptionInfo;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.SubscriptionManager;
 import android.telephony.Rlog;
@@ -33,6 +34,7 @@
 import com.android.internal.telephony.uicc.UiccCardApplication;
 
 import static android.Manifest.permission.CALL_PRIVILEGED;
+import static android.Manifest.permission.READ_PHONE_NUMBERS;
 import static android.Manifest.permission.READ_PHONE_STATE;
 import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
 import static android.Manifest.permission.READ_SMS;
@@ -103,6 +105,35 @@
         }
     }
 
+    public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int subId, int keyType,
+            String callingPackage) {
+        Phone phone = getPhone(subId);
+        if (phone != null) {
+            if (!checkReadPhoneState(callingPackage, "getCarrierInfoForImsiEncryption")) {
+                return null;
+            }
+            return phone.getCarrierInfoForImsiEncryption(keyType);
+        } else {
+            loge("getCarrierInfoForImsiEncryption phone is null for Subscription:" + subId);
+            return null;
+        }
+    }
+
+    public void setCarrierInfoForImsiEncryption(int subId, String callingPackage,
+                                                ImsiEncryptionInfo imsiEncryptionInfo) {
+        Phone phone = getPhone(subId);
+        if (phone != null) {
+            if (!checkReadPhoneState(callingPackage, "setCarrierInfoForImsiEncryption")) {
+                return;
+            }
+            phone.setCarrierInfoForImsiEncryption(imsiEncryptionInfo);
+        } else {
+            loge("setCarrierInfoForImsiEncryption phone is null for Subscription:" + subId);
+            return;
+        }
+    }
+
+
     public String getDeviceSvn(String callingPackage) {
         return getDeviceSvnUsingSubId(getDefaultSubscription(), callingPackage);
     }
@@ -301,62 +332,102 @@
     }
 
 
-    public String getIsimImpi() {
-        Phone phone = getPhone(getDefaultSubscription());
-        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
-                "Requires READ_PRIVILEGED_PHONE_STATE");
-        IsimRecords isim = phone.getIsimRecords();
-        if (isim != null) {
-            return isim.getIsimImpi();
+    /**
+    * get the Isim Impi based on subId
+    */
+    public String getIsimImpi(int subId) {
+        Phone phone = getPhone(subId);
+        if (phone != null) {
+            mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
+                    "Requires READ_PRIVILEGED_PHONE_STATE");
+            IsimRecords isim = phone.getIsimRecords();
+            if (isim != null) {
+                return isim.getIsimImpi();
+            } else {
+                return null;
+            }
         } else {
+            loge("getIsimImpi phone is null for Subscription:" + subId);
             return null;
         }
     }
 
-    public String getIsimDomain() {
-        Phone phone = getPhone(getDefaultSubscription());
-        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
-                "Requires READ_PRIVILEGED_PHONE_STATE");
-        IsimRecords isim = phone.getIsimRecords();
-        if (isim != null) {
-            return isim.getIsimDomain();
+    /**
+    * get the Isim Domain based on subId
+    */
+    public String getIsimDomain(int subId) {
+        Phone phone = getPhone(subId);
+        if (phone != null) {
+            mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
+                    "Requires READ_PRIVILEGED_PHONE_STATE");
+            IsimRecords isim = phone.getIsimRecords();
+            if (isim != null) {
+                return isim.getIsimDomain();
+            } else {
+                return null;
+            }
         } else {
+            loge("getIsimDomain phone is null for Subscription:" + subId);
             return null;
         }
     }
 
-    public String[] getIsimImpu() {
-        Phone phone = getPhone(getDefaultSubscription());
-        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
-                "Requires READ_PRIVILEGED_PHONE_STATE");
-        IsimRecords isim = phone.getIsimRecords();
-        if (isim != null) {
-            return isim.getIsimImpu();
+    /**
+    * get the Isim Impu based on subId
+    */
+    public String[] getIsimImpu(int subId) {
+        Phone phone = getPhone(subId);
+        if (phone != null) {
+            mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
+                    "Requires READ_PRIVILEGED_PHONE_STATE");
+            IsimRecords isim = phone.getIsimRecords();
+            if (isim != null) {
+                return isim.getIsimImpu();
+            } else {
+                return null;
+            }
         } else {
+            loge("getIsimImpu phone is null for Subscription:" + subId);
             return null;
         }
     }
 
-    public String getIsimIst() throws RemoteException {
-        Phone phone = getPhone(getDefaultSubscription());
-        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
-                "Requires READ_PRIVILEGED_PHONE_STATE");
-        IsimRecords isim = phone.getIsimRecords();
-        if (isim != null) {
-            return isim.getIsimIst();
+    /**
+    * get the Isim Ist based on subId
+    */
+    public String getIsimIst(int subId) throws RemoteException {
+        Phone phone = getPhone(subId);
+        if (phone != null) {
+            mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
+                    "Requires READ_PRIVILEGED_PHONE_STATE");
+            IsimRecords isim = phone.getIsimRecords();
+            if (isim != null) {
+                return isim.getIsimIst();
+            } else {
+                return null;
+            }
         } else {
+            loge("getIsimIst phone is null for Subscription:" + subId);
             return null;
         }
     }
 
-    public String[] getIsimPcscf() throws RemoteException {
-        Phone phone = getPhone(getDefaultSubscription());
-        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
-                "Requires READ_PRIVILEGED_PHONE_STATE");
-        IsimRecords isim = phone.getIsimRecords();
-        if (isim != null) {
-            return isim.getIsimPcscf();
+    /**
+    * get the Isim Pcscf based on subId
+    */
+    public String[] getIsimPcscf(int subId) throws RemoteException {
+        Phone phone = getPhone(subId);
+        if (phone != null) {
+            mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
+                    "Requires READ_PRIVILEGED_PHONE_STATE");
+            IsimRecords isim = phone.getIsimRecords();
+            if (isim != null) {
+                return isim.getIsimPcscf();
+            } else {
+                return null;
+            }
         } else {
+            loge("getIsimPcscf phone is null for Subscription:" + subId);
             return null;
         }
     }
@@ -434,7 +505,8 @@
     }
 
     /**
-     * Besides READ_PHONE_STATE, WRITE_SMS and READ_SMS also allow apps to get phone numbers.
+     * Besides READ_PHONE_STATE, READ_PHONE_NUMBERS, WRITE_SMS and READ_SMS also allow apps to get
+     * phone numbers.
      */
     private boolean checkReadPhoneNumber(String callingPackage, String message) {
         // Default SMS app can always read it.
@@ -445,18 +517,36 @@
         try {
             return checkReadPhoneState(callingPackage, message);
         } catch (SecurityException readPhoneStateSecurityException) {
-            try {
-                // Can be read with READ_SMS too.
-                mContext.enforceCallingOrSelfPermission(READ_SMS, message);
-                return mAppOps.noteOp(AppOpsManager.OP_READ_SMS,
-                        Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED;
-            } catch (SecurityException readSmsSecurityException) {
-                // Throw exception with message including both READ_PHONE_STATE and READ_SMS
-                // permissions
-                throw new SecurityException(message + ": Neither user " + Binder.getCallingUid() +
-                        " nor current process has " + READ_PHONE_STATE + " or " + READ_SMS + ".");
-            }
         }
+        try {
+            // Can be read with READ_SMS too.
+            mContext.enforceCallingOrSelfPermission(READ_SMS, message);
+            int opCode = mAppOps.permissionToOpCode(READ_SMS);
+            if (opCode != AppOpsManager.OP_NONE) {
+                return mAppOps.noteOp(opCode, Binder.getCallingUid(), callingPackage)
+                        == AppOpsManager.MODE_ALLOWED;
+            } else {
+                return true;
+            }
+        } catch (SecurityException readSmsSecurityException) {
+        }
+        try {
+            // Can be read with READ_PHONE_NUMBERS too.
+            mContext.enforceCallingOrSelfPermission(READ_PHONE_NUMBERS, message);
+            int opCode = mAppOps.permissionToOpCode(READ_PHONE_NUMBERS);
+            if (opCode != AppOpsManager.OP_NONE) {
+                return mAppOps.noteOp(opCode, Binder.getCallingUid(), callingPackage)
+                        == AppOpsManager.MODE_ALLOWED;
+            } else {
+                return true;
+            }
+        } catch (SecurityException readPhoneNumberSecurityException) {
+        }
+        // Throw exception with message including READ_PHONE_STATE, READ_SMS, and READ_PHONE_NUMBERS
+        // permissions
+        throw new SecurityException(message + ": Neither user " + Binder.getCallingUid() +
+                " nor current process has " + READ_PHONE_STATE + ", " +
+                READ_SMS + ", or " + READ_PHONE_STATE + ".");
     }
 
     private void log(String s) {
diff --git a/src/java/com/android/internal/telephony/ProxyController.java b/src/java/com/android/internal/telephony/ProxyController.java
index 0eb3310..f6d42af 100644
--- a/src/java/com/android/internal/telephony/ProxyController.java
+++ b/src/java/com/android/internal/telephony/ProxyController.java
@@ -16,9 +16,6 @@
 
 package com.android.internal.telephony;
 
-import java.util.ArrayList;
-import java.util.Random;
-
 import android.content.Context;
 import android.content.Intent;
 import android.os.AsyncResult;
@@ -31,13 +28,12 @@
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
-import com.android.internal.telephony.PhoneSwitcher;
 import com.android.internal.telephony.uicc.UiccController;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicInteger;
 
 public class ProxyController {
     static final String LOG_TAG = "ProxyController";
@@ -130,7 +126,7 @@
 
         mUiccPhoneBookController = new UiccPhoneBookController(mPhones);
         mPhoneSubInfoController = new PhoneSubInfoController(mContext, mPhones);
-        mUiccSmsController = new UiccSmsController(mPhones);
+        mUiccSmsController = new UiccSmsController();
         mSetRadioAccessFamilyStatus = new int[mPhones.length];
         mNewRadioAccessFamily = new int[mPhones.length];
         mOldRadioAccessFamily = new int[mPhones.length];
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index ee0f31d..3ed591f 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -46,6 +46,7 @@
 import android.hardware.radio.V1_0.RadioIndicationType;
 import android.hardware.radio.V1_0.RadioResponseInfo;
 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;
@@ -67,6 +68,7 @@
 import android.service.carrier.CarrierIdentifier;
 import android.telephony.CellInfo;
 import android.telephony.ClientRequestStats;
+import android.telephony.ImsiEncryptionInfo;
 import android.telephony.ModemActivityInfo;
 import android.telephony.NeighboringCellInfo;
 import android.telephony.NetworkScanRequest;
@@ -83,13 +85,13 @@
 import android.util.Log;
 import android.util.SparseArray;
 
-import com.android.internal.telephony.TelephonyProto.SmsSession;
 import com.android.internal.telephony.cdma.CdmaInformationRecords;
 import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
 import com.android.internal.telephony.dataconnection.DataCallResponse;
 import com.android.internal.telephony.dataconnection.DataProfile;
 import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
+import com.android.internal.telephony.nano.TelephonyProto.SmsSession;
 import com.android.internal.telephony.uicc.IccUtils;
 
 import java.io.ByteArrayInputStream;
@@ -97,7 +99,6 @@
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.security.PublicKey;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -320,14 +321,15 @@
 
     final Integer mPhoneId;
 
-    private TelephonyMetrics mMetrics = TelephonyMetrics.getInstance();
-
     /* default work source which will blame phone process */
     private WorkSource mRILDefaultWorkSource;
 
     /* Worksource containing all applications causing wakelock to be held */
     private WorkSource mActiveWakelockWorkSource;
 
+    /** Telephony metrics instance for logging metrics event */
+    private TelephonyMetrics mMetrics = TelephonyMetrics.getInstance();
+
     boolean mIsMobileNetworkSupported;
     RadioResponse mRadioResponse;
     RadioIndication mRadioIndication;
@@ -499,6 +501,11 @@
     private IRadio getRadioProxy(Message result) {
         if (!mIsMobileNetworkSupported) {
             if (RILJ_LOGV) riljLog("getRadioProxy: Not calling getService(): wifi-only");
+            if (result != null) {
+                AsyncResult.forMessage(result, null,
+                        CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
+                result.sendToTarget();
+            }
             return null;
         }
 
@@ -540,6 +547,11 @@
     private IOemHook getOemHookProxy(Message result) {
         if (!mIsMobileNetworkSupported) {
             if (RILJ_LOGV) riljLog("getOemHookProxy: Not calling getService(): wifi-only");
+            if (result != null) {
+                AsyncResult.forMessage(result, null,
+                        CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
+                result.sendToTarget();
+            }
             return null;
         }
 
@@ -705,8 +717,8 @@
                     mRILDefaultWorkSource);
 
             if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " aid = " + aid
-                + " pin = " + pin);
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " aid = " + aid);
             }
 
             try {
@@ -732,8 +744,8 @@
                     mRILDefaultWorkSource);
 
             if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " puk = " + puk
-                        + " newPin = " + newPin + " aid = " + aid);
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " aid = " + aid);
             }
 
             try {
@@ -760,8 +772,8 @@
                     mRILDefaultWorkSource);
 
             if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " aid = " + aid
-                        + " pin = " + pin);
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " aid = " + aid);
             }
 
             try {
@@ -787,8 +799,8 @@
                     mRILDefaultWorkSource);
 
             if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " puk = " + puk
-                        + " newPin = " + newPin2 + " aid = " + aid);
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " aid = " + aid);
             }
 
             try {
@@ -1283,6 +1295,26 @@
     }
 
     /**
+     * Convert NV reset type into ResetNvType defined in types.hal.
+     * @param resetType NV reset type.
+     * @return Converted reset type in integer or -1 if param is invalid.
+     */
+    private static int convertToHalResetNvType(int resetType) {
+        /**
+         * resetType values
+         * 1 - reload all NV items
+         * 2 - erase NV reset (SCRTN)
+         * 3 - factory reset (RTN)
+         */
+        switch (resetType) {
+            case 1: return ResetNvType.RELOAD;
+            case 2: return ResetNvType.ERASE;
+            case 3: return ResetNvType.FACTORY_RESET;
+        }
+        return -1;
+    }
+
+    /**
      * Convert SetupDataCallResult defined in types.hal into DataCallResponse
      * @param dcResult setup data call result
      * @return converted DataCallResponse object
@@ -1501,8 +1533,8 @@
             cfInfo.reason = cfReason;
             cfInfo.serviceClass = serviceClass;
             cfInfo.toa = PhoneNumberUtils.toaFromString(number);
-            cfInfo.number = number;
-            cfInfo.timeSeconds = 0;
+            cfInfo.number = convertNullToEmptyString(number);
+            cfInfo.timeSeconds = timeSeconds;
 
             try {
                 radioProxy.setCallForward(rr.mSerial, cfInfo);
@@ -1778,7 +1810,7 @@
     }
 
     @Override
-    public void startNetworkScan(Message result) {
+    public void startNetworkScan(NetworkScanRequest nsr, Message result) {
         IRadio radioProxy = getRadioProxy(result);
         if (radioProxy != null) {
             android.hardware.radio.V1_1.IRadio radioProxy11 =
@@ -1790,7 +1822,6 @@
                     result.sendToTarget();
                 }
             } else {
-                NetworkScanRequest nsr = (NetworkScanRequest) result.obj;
                 android.hardware.radio.V1_1.NetworkScanRequest request =
                         new android.hardware.radio.V1_1.NetworkScanRequest();
                 request.type = nsr.scanType;
@@ -1815,11 +1846,15 @@
                                     + " not supported!");
                             return;
                     }
-                    for (int band : ras.bands) {
-                        bands.add(band);
+                    if (ras.bands != null) {
+                        for (int band : ras.bands) {
+                            bands.add(band);
+                        }
                     }
-                    for (int channel : ras.channels) {
-                        s.channels.add(channel);
+                    if (ras.channels != null) {
+                        for (int channel : ras.channels) {
+                            s.channels.add(channel);
+                        }
                     }
                     request.specifiers.add(s);
                 }
@@ -2680,25 +2715,26 @@
             RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG, result,
                     mRILDefaultWorkSource);
 
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                        + " with " + configs.length + " configs : ");
-                for (int i = 0; i < configs.length; i++) {
-                    riljLog(configs[i].toString());
+            ArrayList<CdmaBroadcastSmsConfigInfo> halConfigs = new ArrayList<>();
+
+            for (CdmaSmsBroadcastConfigInfo config: configs) {
+                for (int i = config.getFromServiceCategory();
+                        i <= config.getToServiceCategory();
+                        i++) {
+                    CdmaBroadcastSmsConfigInfo info = new CdmaBroadcastSmsConfigInfo();
+                    info.serviceCategory = i;
+                    info.language = config.getLanguage();
+                    info.selected = config.isSelected();
+                    halConfigs.add(info);
                 }
             }
 
-            ArrayList<CdmaBroadcastSmsConfigInfo> halConfigs = new ArrayList<>();
-
-            int numOfConfig = configs.length;
-            CdmaBroadcastSmsConfigInfo info;
-
-            for (int i = 0; i < numOfConfig; i++) {
-                info = new CdmaBroadcastSmsConfigInfo();
-                info.serviceCategory = configs[i].getFromServiceCategory();
-                info.language = configs[i].getLanguage();
-                info.selected = configs[i].isSelected();
-                halConfigs.add(info);
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " with " + halConfigs.size() + " configs : ");
+                for (CdmaBroadcastSmsConfigInfo config : halConfigs) {
+                    riljLog(config.toString());
+                }
             }
 
             try {
@@ -3307,7 +3343,7 @@
             }
 
             try {
-                radioProxy.nvResetConfig(rr.mSerial, resetType);
+                radioProxy.nvResetConfig(rr.mSerial, convertToHalResetNvType(resetType));
             } catch (RemoteException | RuntimeException e) {
                 handleRadioProxyExceptionForRR(rr, "nvResetConfig", e);
             }
@@ -3746,8 +3782,9 @@
     }
 
     @Override
-    public void setCarrierInfoForImsiEncryption(PublicKey publicKey, String keyIdentifier,
+    public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo,
                                                 Message result) {
+        checkNotNull(imsiEncryptionInfo, "ImsiEncryptionInfo cannot be null.");
         IRadio radioProxy = getRadioProxy(result);
         if (radioProxy != null) {
             android.hardware.radio.V1_1.IRadio radioProxy11 =
@@ -3764,8 +3801,21 @@
                 if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
 
                 try {
+                    android.hardware.radio.V1_1.ImsiEncryptionInfo halImsiInfo =
+                            new android.hardware.radio.V1_1.ImsiEncryptionInfo();
+                    halImsiInfo.mnc = imsiEncryptionInfo.getMnc();
+                    halImsiInfo.mcc = imsiEncryptionInfo.getMcc();
+                    halImsiInfo.keyIdentifier = imsiEncryptionInfo.getKeyIdentifier();
+                    if (imsiEncryptionInfo.getExpirationTime() != null) {
+                        halImsiInfo.expirationTime =
+                                imsiEncryptionInfo.getExpirationTime().getTime();
+                    }
+                    for (byte b : imsiEncryptionInfo.getPublicKey().getEncoded()) {
+                        halImsiInfo.carrierKey.add(new Byte(b));
+                    }
+
                     radioProxy11.setCarrierInfoForImsiEncryption(
-                            rr.mSerial, publicKeyToArrayList(publicKey), keyIdentifier);
+                            rr.mSerial, halImsiInfo);
                 } catch (RemoteException | RuntimeException e) {
                     handleRadioProxyExceptionForRR(rr, "setCarrierInfoForImsiEncryption", e);
                 }
@@ -4679,6 +4729,10 @@
                 return "RIL_RESPONSE_ACKNOWLEDGEMENT";
             case RIL_REQUEST_SET_CARRIER_INFO_IMSI_ENCRYPTION:
                 return "RIL_REQUEST_SET_CARRIER_INFO_IMSI_ENCRYPTION";
+            case RIL_REQUEST_START_NETWORK_SCAN:
+                return "RIL_REQUEST_START_NETWORK_SCAN";
+            case RIL_REQUEST_STOP_NETWORK_SCAN:
+                return "RIL_REQUEST_STOP_NETWORK_SCAN";
             default: return "<unknown request>";
         }
     }
@@ -4781,6 +4835,8 @@
                 return "UNSOL_MODEM_RESTART";
             case RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION:
                 return "RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION";
+            case RIL_UNSOL_NETWORK_SCAN_RESULT:
+                return "RIL_UNSOL_NETWORK_SCAN_RESULT";
             default:
                 return "<unknown response>";
         }
@@ -4869,19 +4925,6 @@
         return arrayList;
     }
 
-    /**
-     * Method to serialize the Public Key into ArrayList of bytes
-     * @param publicKey publicKey to be converted to ArrayList of bytes.
-     **/
-    public static ArrayList<Byte> publicKeyToArrayList(PublicKey publicKey) {
-        byte[] key = publicKey.getEncoded();
-        ArrayList<Byte> arrayList = new ArrayList<>(key.length);
-        for (byte b : key) {
-            arrayList.add(b);
-        }
-        return arrayList;
-    }
-
     public static byte[] arrayListToPrimitiveArray(ArrayList<Byte> bytes) {
         byte[] ret = new byte[bytes.size()];
         for (int i = 0; i < ret.length; i++) {
@@ -4951,7 +4994,7 @@
     static ArrayList<Integer> convertHalLceData(LceDataInfo lce, RIL ril) {
         final ArrayList<Integer> capacityResponse = new ArrayList<Integer>();
         final int capacityDownKbps = lce.lastHopCapacityKbps;
-        final int confidenceLevel = lce.confidenceLevel;
+        final int confidenceLevel = Byte.toUnsignedInt(lce.confidenceLevel);
         final int lceSuspended = lce.lceSuspended ? 1 : 0;
 
         ril.riljLog("LCE capacity information received:" +
diff --git a/src/java/com/android/internal/telephony/RadioIndication.java b/src/java/com/android/internal/telephony/RadioIndication.java
index 8c09b4b..663f8a4 100644
--- a/src/java/com/android/internal/telephony/RadioIndication.java
+++ b/src/java/com/android/internal/telephony/RadioIndication.java
@@ -81,6 +81,7 @@
 import android.hardware.radio.V1_0.StkCcUnsolSsResult;
 import android.hardware.radio.V1_0.SuppSvcNotification;
 import android.hardware.radio.V1_1.IRadioIndication;
+import android.hardware.radio.V1_1.KeepaliveStatus;
 import android.os.AsyncResult;
 import android.os.SystemProperties;
 import android.telephony.CellInfo;
@@ -88,13 +89,13 @@
 import android.telephony.SignalStrength;
 import android.telephony.SmsMessage;
 
-import com.android.internal.telephony.TelephonyProto.SmsSession;
 import com.android.internal.telephony.cdma.CdmaCallWaitingNotification;
 import com.android.internal.telephony.cdma.CdmaInformationRecords;
 import com.android.internal.telephony.cdma.SmsMessageConverter;
 import com.android.internal.telephony.dataconnection.DataCallResponse;
 import com.android.internal.telephony.gsm.SsData;
 import com.android.internal.telephony.gsm.SuppServiceNotification;
+import com.android.internal.telephony.nano.TelephonyProto.SmsSession;
 import com.android.internal.telephony.uicc.IccRefreshResponse;
 import com.android.internal.telephony.uicc.IccUtils;
 
@@ -802,6 +803,15 @@
                 new AsyncResult(null, null, null));
     }
 
+    /**
+     * Indicates a change in the status of an ongoing Keepalive session
+     * @param indicationType RadioIndicationType
+     * @param keepaliveStatus Status of the ongoing Keepalive session
+     */
+    public void keepaliveStatus(int indicationType, KeepaliveStatus keepaliveStatus) {
+        throw new UnsupportedOperationException("keepaliveStatus Indications are not implemented");
+    }
+
     private CommandsInterface.RadioState getRadioStateFromInt(int stateInt) {
         CommandsInterface.RadioState state;
 
diff --git a/src/java/com/android/internal/telephony/RadioResponse.java b/src/java/com/android/internal/telephony/RadioResponse.java
index 914a60a..caf4477 100644
--- a/src/java/com/android/internal/telephony/RadioResponse.java
+++ b/src/java/com/android/internal/telephony/RadioResponse.java
@@ -34,6 +34,7 @@
 import android.hardware.radio.V1_0.SetupDataCallResult;
 import android.hardware.radio.V1_0.VoiceRegStateResult;
 import android.hardware.radio.V1_1.IRadioResponse;
+import android.hardware.radio.V1_1.KeepaliveStatus;
 import android.os.AsyncResult;
 import android.os.Message;
 import android.os.SystemClock;
@@ -45,6 +46,7 @@
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.text.TextUtils;
 
 import com.android.internal.telephony.dataconnection.DataCallResponse;
 import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
@@ -1106,11 +1108,9 @@
         RILRequest rr = mRil.processResponse(responseInfo);
 
         if (rr != null) {
-            RadioCapability ret = null;
-            if (responseInfo.error == RadioError.NONE) {
-                ret = RIL.convertHalRadioCapability(rc, mRil);
-            } else if (responseInfo.error == RadioError.REQUEST_NOT_SUPPORTED ||
-                    responseInfo.error == RadioError.GENERIC_FAILURE) {
+            RadioCapability ret = RIL.convertHalRadioCapability(rc, mRil);
+            if (responseInfo.error == RadioError.REQUEST_NOT_SUPPORTED
+                    || responseInfo.error == RadioError.GENERIC_FAILURE) {
                 // we should construct the RAF bitmask the radio
                 // supports based on preferred network bitmasks
                 ret = mRil.makeStaticRadioCapability();
@@ -1218,46 +1218,60 @@
         responseVoid(responseInfo);
     }
 
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param keepaliveStatus status of the keepalive with a handle for the session
+     */
+    public void startKeepaliveResponse(RadioResponseInfo responseInfo,
+            KeepaliveStatus keepaliveStatus) {
+        throw new UnsupportedOperationException("startKeepaliveResponse not implemented");
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void stopKeepaliveResponse(RadioResponseInfo responseInfo) {
+        throw new UnsupportedOperationException("stopKeepaliveResponse not implemented");
+    }
+
     private void responseIccCardStatus(RadioResponseInfo responseInfo, CardStatus cardStatus) {
         RILRequest rr = mRil.processResponse(responseInfo);
 
         if (rr != null) {
-            Object ret = null;
-            if (responseInfo.error == RadioError.NONE) {
-                IccCardStatus iccCardStatus = new IccCardStatus();
-                iccCardStatus.setCardState(cardStatus.cardState);
-                iccCardStatus.setUniversalPinState(cardStatus.universalPinState);
-                iccCardStatus.mGsmUmtsSubscriptionAppIndex = cardStatus.gsmUmtsSubscriptionAppIndex;
-                iccCardStatus.mCdmaSubscriptionAppIndex = cardStatus.cdmaSubscriptionAppIndex;
-                iccCardStatus.mImsSubscriptionAppIndex = cardStatus.imsSubscriptionAppIndex;
-                int numApplications = cardStatus.applications.size();
+            IccCardStatus iccCardStatus = new IccCardStatus();
+            iccCardStatus.setCardState(cardStatus.cardState);
+            iccCardStatus.setUniversalPinState(cardStatus.universalPinState);
+            iccCardStatus.mGsmUmtsSubscriptionAppIndex = cardStatus.gsmUmtsSubscriptionAppIndex;
+            iccCardStatus.mCdmaSubscriptionAppIndex = cardStatus.cdmaSubscriptionAppIndex;
+            iccCardStatus.mImsSubscriptionAppIndex = cardStatus.imsSubscriptionAppIndex;
+            int numApplications = cardStatus.applications.size();
 
-                // limit to maximum allowed applications
-                if (numApplications
-                        > com.android.internal.telephony.uicc.IccCardStatus.CARD_MAX_APPS) {
-                    numApplications =
-                            com.android.internal.telephony.uicc.IccCardStatus.CARD_MAX_APPS;
-                }
-                iccCardStatus.mApplications = new IccCardApplicationStatus[numApplications];
-                for (int i = 0; i < numApplications; i++) {
-                    AppStatus rilAppStatus = cardStatus.applications.get(i);
-                    IccCardApplicationStatus appStatus = new IccCardApplicationStatus();
-                    appStatus.app_type       = appStatus.AppTypeFromRILInt(rilAppStatus.appType);
-                    appStatus.app_state      = appStatus.AppStateFromRILInt(rilAppStatus.appState);
-                    appStatus.perso_substate = appStatus.PersoSubstateFromRILInt(
-                            rilAppStatus.persoSubstate);
-                    appStatus.aid            = rilAppStatus.aidPtr;
-                    appStatus.app_label      = rilAppStatus.appLabelPtr;
-                    appStatus.pin1_replaced  = rilAppStatus.pin1Replaced;
-                    appStatus.pin1           = appStatus.PinStateFromRILInt(rilAppStatus.pin1);
-                    appStatus.pin2           = appStatus.PinStateFromRILInt(rilAppStatus.pin2);
-                    iccCardStatus.mApplications[i] = appStatus;
-                }
-                mRil.riljLog("responseIccCardStatus: from HIDL: " + iccCardStatus);
-                ret = iccCardStatus;
-                sendMessageResponse(rr.mResult, ret);
+            // limit to maximum allowed applications
+            if (numApplications
+                    > com.android.internal.telephony.uicc.IccCardStatus.CARD_MAX_APPS) {
+                numApplications =
+                        com.android.internal.telephony.uicc.IccCardStatus.CARD_MAX_APPS;
             }
-            mRil.processResponseDone(rr, responseInfo, ret);
+            iccCardStatus.mApplications = new IccCardApplicationStatus[numApplications];
+            for (int i = 0; i < numApplications; i++) {
+                AppStatus rilAppStatus = cardStatus.applications.get(i);
+                IccCardApplicationStatus appStatus = new IccCardApplicationStatus();
+                appStatus.app_type       = appStatus.AppTypeFromRILInt(rilAppStatus.appType);
+                appStatus.app_state      = appStatus.AppStateFromRILInt(rilAppStatus.appState);
+                appStatus.perso_substate = appStatus.PersoSubstateFromRILInt(
+                        rilAppStatus.persoSubstate);
+                appStatus.aid            = rilAppStatus.aidPtr;
+                appStatus.app_label      = rilAppStatus.appLabelPtr;
+                appStatus.pin1_replaced  = rilAppStatus.pin1Replaced;
+                appStatus.pin1           = appStatus.PinStateFromRILInt(rilAppStatus.pin1);
+                appStatus.pin2           = appStatus.PinStateFromRILInt(rilAppStatus.pin2);
+                iccCardStatus.mApplications[i] = appStatus;
+            }
+            mRil.riljLog("responseIccCardStatus: from HIDL: " + iccCardStatus);
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, iccCardStatus);
+            }
+            mRil.processResponseDone(rr, responseInfo, iccCardStatus);
         }
     }
 
@@ -1273,13 +1287,11 @@
         RILRequest rr = mRil.processResponse(responseInfo);
 
         if (rr != null) {
-            Object ret = null;
+            int[] ret = new int[var.size()];
+            for (int i = 0; i < var.size(); i++) {
+                ret[i] = var.get(i);
+            }
             if (responseInfo.error == RadioError.NONE) {
-                int[] response = new int[var.size()];
-                for (int i = 0; i < var.size(); i++) {
-                    response[i] = var.get(i);
-                }
-                ret = response;
                 sendMessageResponse(rr.mResult, ret);
             }
             mRil.processResponseDone(rr, responseInfo, ret);
@@ -1291,82 +1303,77 @@
         RILRequest rr = mRil.processResponse(responseInfo);
 
         if (rr != null) {
-            Object ret = null;
-            if (responseInfo.error == RadioError.NONE) {
-                int num;
-                ArrayList<DriverCall> dcCalls;
-                DriverCall dc;
+            int num = calls.size();
+            ArrayList<DriverCall> dcCalls = new ArrayList<DriverCall>(num);
+            DriverCall dc;
 
-                num = calls.size();
-                dcCalls = new ArrayList<DriverCall>(num);
-
-                for (int i = 0; i < num; i++) {
-                    dc = new DriverCall();
-                    // TODO: change name of function stateFromCLCC() in DriverCall.java to name
-                    // clarifying what is CLCC
-                    dc.state = DriverCall.stateFromCLCC((int) (calls.get(i).state));
-                    dc.index = calls.get(i).index;
-                    dc.TOA = calls.get(i).toa;
-                    dc.isMpty = calls.get(i).isMpty;
-                    dc.isMT = calls.get(i).isMT;
-                    dc.als = calls.get(i).als;
-                    dc.isVoice = calls.get(i).isVoice;
-                    dc.isVoicePrivacy = calls.get(i).isVoicePrivacy;
-                    dc.number = calls.get(i).number;
-                    dc.numberPresentation =
-                            DriverCall.presentationFromCLIP(
-                                    (int) (calls.get(i).numberPresentation));
-                    dc.name = calls.get(i).name;
-                    dc.namePresentation =
-                            DriverCall.presentationFromCLIP((int) (calls.get(i).namePresentation));
-                    if (calls.get(i).uusInfo.size() == 1) {
-                        dc.uusInfo = new UUSInfo();
-                        dc.uusInfo.setType(calls.get(i).uusInfo.get(0).uusType);
-                        dc.uusInfo.setDcs(calls.get(i).uusInfo.get(0).uusDcs);
-                        if (calls.get(i).uusInfo.get(0).uusData != null) {
-                            byte[] userData = calls.get(i).uusInfo.get(0).uusData.getBytes();
-                            dc.uusInfo.setUserData(userData);
-                        } else {
-                            mRil.riljLog("responseCurrentCalls: uusInfo data is null");
-                        }
-
-                        mRil.riljLogv(String.format("Incoming UUS : type=%d, dcs=%d, length=%d",
-                                dc.uusInfo.getType(), dc.uusInfo.getDcs(),
-                                dc.uusInfo.getUserData().length));
-                        mRil.riljLogv("Incoming UUS : data (hex): "
-                                + IccUtils.bytesToHexString(dc.uusInfo.getUserData()));
+            for (int i = 0; i < num; i++) {
+                dc = new DriverCall();
+                // TODO: change name of function stateFromCLCC() in DriverCall.java to name
+                // clarifying what is CLCC
+                dc.state = DriverCall.stateFromCLCC((int) (calls.get(i).state));
+                dc.index = calls.get(i).index;
+                dc.TOA = calls.get(i).toa;
+                dc.isMpty = calls.get(i).isMpty;
+                dc.isMT = calls.get(i).isMT;
+                dc.als = calls.get(i).als;
+                dc.isVoice = calls.get(i).isVoice;
+                dc.isVoicePrivacy = calls.get(i).isVoicePrivacy;
+                dc.number = calls.get(i).number;
+                dc.numberPresentation =
+                        DriverCall.presentationFromCLIP(
+                                (int) (calls.get(i).numberPresentation));
+                dc.name = calls.get(i).name;
+                dc.namePresentation =
+                        DriverCall.presentationFromCLIP((int) (calls.get(i).namePresentation));
+                if (calls.get(i).uusInfo.size() == 1) {
+                    dc.uusInfo = new UUSInfo();
+                    dc.uusInfo.setType(calls.get(i).uusInfo.get(0).uusType);
+                    dc.uusInfo.setDcs(calls.get(i).uusInfo.get(0).uusDcs);
+                    if (!TextUtils.isEmpty(calls.get(i).uusInfo.get(0).uusData)) {
+                        byte[] userData = calls.get(i).uusInfo.get(0).uusData.getBytes();
+                        dc.uusInfo.setUserData(userData);
                     } else {
-                        mRil.riljLogv("Incoming UUS : NOT present!");
+                        mRil.riljLog("responseCurrentCalls: uusInfo data is null or empty");
                     }
 
-                    // Make sure there's a leading + on addresses with a TOA of 145
-                    dc.number = PhoneNumberUtils.stringFromStringAndTOA(dc.number, dc.TOA);
-
-                    dcCalls.add(dc);
-
-                    if (dc.isVoicePrivacy) {
-                        mRil.mVoicePrivacyOnRegistrants.notifyRegistrants();
-                        mRil.riljLog("InCall VoicePrivacy is enabled");
-                    } else {
-                        mRil.mVoicePrivacyOffRegistrants.notifyRegistrants();
-                        mRil.riljLog("InCall VoicePrivacy is disabled");
-                    }
+                    mRil.riljLogv(String.format("Incoming UUS : type=%d, dcs=%d, length=%d",
+                            dc.uusInfo.getType(), dc.uusInfo.getDcs(),
+                            dc.uusInfo.getUserData().length));
+                    mRil.riljLogv("Incoming UUS : data (hex): "
+                            + IccUtils.bytesToHexString(dc.uusInfo.getUserData()));
+                } else {
+                    mRil.riljLogv("Incoming UUS : NOT present!");
                 }
 
-                Collections.sort(dcCalls);
+                // Make sure there's a leading + on addresses with a TOA of 145
+                dc.number = PhoneNumberUtils.stringFromStringAndTOA(dc.number, dc.TOA);
 
-                if ((num == 0) && mRil.mTestingEmergencyCall.getAndSet(false)) {
-                    if (mRil.mEmergencyCallbackModeRegistrant != null) {
-                        mRil.riljLog("responseCurrentCalls: call ended, testing emergency call,"
-                                + " notify ECM Registrants");
-                        mRil.mEmergencyCallbackModeRegistrant.notifyRegistrant();
-                    }
+                dcCalls.add(dc);
+
+                if (dc.isVoicePrivacy) {
+                    mRil.mVoicePrivacyOnRegistrants.notifyRegistrants();
+                    mRil.riljLog("InCall VoicePrivacy is enabled");
+                } else {
+                    mRil.mVoicePrivacyOffRegistrants.notifyRegistrants();
+                    mRil.riljLog("InCall VoicePrivacy is disabled");
                 }
-
-                ret = dcCalls;
-                sendMessageResponse(rr.mResult, ret);
             }
-            mRil.processResponseDone(rr, responseInfo, ret);
+
+            Collections.sort(dcCalls);
+
+            if ((num == 0) && mRil.mTestingEmergencyCall.getAndSet(false)) {
+                if (mRil.mEmergencyCallbackModeRegistrant != null) {
+                    mRil.riljLog("responseCurrentCalls: call ended, testing emergency call,"
+                            + " notify ECM Registrants");
+                    mRil.mEmergencyCallbackModeRegistrant.notifyRegistrant();
+                }
+            }
+
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, dcCalls);
+            }
+            mRil.processResponseDone(rr, responseInfo, dcCalls);
         }
     }
 
@@ -1386,12 +1393,10 @@
         RILRequest rr = mRil.processResponse(responseInfo);
 
         if (rr != null) {
-            String ret = null;
             if (responseInfo.error == RadioError.NONE) {
-                ret = str;
-                sendMessageResponse(rr.mResult, ret);
+                sendMessageResponse(rr.mResult, str);
             }
-            mRil.processResponseDone(rr, responseInfo, ret);
+            mRil.processResponseDone(rr, responseInfo, str);
         }
     }
 
@@ -1408,12 +1413,11 @@
         RILRequest rr = ril.processResponse(responseInfo);
 
         if (rr != null) {
-            String[] ret = null;
+            String[] ret = new String[strings.size()];
+            for (int i = 0; i < strings.size(); i++) {
+                ret[i] = strings.get(i);
+            }
             if (responseInfo.error == RadioError.NONE) {
-                ret = new String[strings.size()];
-                for (int i = 0; i < strings.size(); i++) {
-                    ret[i] = strings.get(i);
-                }
                 sendMessageResponse(rr.mResult, ret);
             }
             ril.processResponseDone(rr, responseInfo, ret);
@@ -1425,11 +1429,10 @@
         RILRequest rr = mRil.processResponse(responseInfo);
 
         if (rr != null) {
-            LastCallFailCause ret = null;
+            LastCallFailCause ret = new LastCallFailCause();
+            ret.causeCode = fcInfo.causeCode;
+            ret.vendorCause = fcInfo.vendorCause;
             if (responseInfo.error == RadioError.NONE) {
-                ret = new LastCallFailCause();
-                ret.causeCode = fcInfo.causeCode;
-                ret.vendorCause = fcInfo.vendorCause;
                 sendMessageResponse(rr.mResult, ret);
             }
             mRil.processResponseDone(rr, responseInfo, ret);
@@ -1441,9 +1444,8 @@
         RILRequest rr = mRil.processResponse(responseInfo);
 
         if (rr != null) {
-            SignalStrength ret = null;
+            SignalStrength ret = RIL.convertHalSignalStrength(sigStrength);
             if (responseInfo.error == RadioError.NONE) {
-                ret = RIL.convertHalSignalStrength(sigStrength);
                 sendMessageResponse(rr.mResult, ret);
             }
             mRil.processResponseDone(rr, responseInfo, ret);
@@ -1454,9 +1456,8 @@
         RILRequest rr = mRil.processResponse(responseInfo);
 
         if (rr != null) {
-            SmsResponse ret = null;
+            SmsResponse ret = new SmsResponse(sms.messageRef, sms.ackPDU, sms.errorCode);
             if (responseInfo.error == RadioError.NONE) {
-                ret = new SmsResponse(sms.messageRef, sms.ackPDU, sms.errorCode);
                 sendMessageResponse(rr.mResult, ret);
             }
             mRil.processResponseDone(rr, responseInfo, ret);
@@ -1481,9 +1482,8 @@
         RILRequest rr = mRil.processResponse(responseInfo);
 
         if (rr != null) {
-            IccIoResult ret = null;
+            IccIoResult ret = new IccIoResult(result.sw1, result.sw2, result.simResponse);
             if (responseInfo.error == RadioError.NONE) {
-                ret = new IccIoResult(result.sw1, result.sw2, result.simResponse);
                 sendMessageResponse(rr.mResult, ret);
             }
             mRil.processResponseDone(rr, responseInfo, ret);
@@ -1495,18 +1495,17 @@
                                                  callForwardInfos) {
         RILRequest rr = mRil.processResponse(responseInfo);
         if (rr != null) {
-            CallForwardInfo[] ret = null;
+            CallForwardInfo[] ret = new CallForwardInfo[callForwardInfos.size()];
+            for (int i = 0; i < callForwardInfos.size(); i++) {
+                ret[i] = new CallForwardInfo();
+                ret[i].status = callForwardInfos.get(i).status;
+                ret[i].reason = callForwardInfos.get(i).reason;
+                ret[i].serviceClass = callForwardInfos.get(i).serviceClass;
+                ret[i].toa = callForwardInfos.get(i).toa;
+                ret[i].number = callForwardInfos.get(i).number;
+                ret[i].timeSeconds = callForwardInfos.get(i).timeSeconds;
+            }
             if (responseInfo.error == RadioError.NONE) {
-                ret = new CallForwardInfo[callForwardInfos.size()];
-                for (int i = 0; i < callForwardInfos.size(); i++) {
-                    ret[i] = new CallForwardInfo();
-                    ret[i].status = callForwardInfos.get(i).status;
-                    ret[i].reason = callForwardInfos.get(i).reason;
-                    ret[i].serviceClass = callForwardInfos.get(i).serviceClass;
-                    ret[i].toa = callForwardInfos.get(i).toa;
-                    ret[i].number = callForwardInfos.get(i).number;
-                    ret[i].timeSeconds = callForwardInfos.get(i).timeSeconds;
-                }
                 sendMessageResponse(rr.mResult, ret);
             }
             mRil.processResponseDone(rr, responseInfo, ret);
@@ -1533,14 +1532,13 @@
         RILRequest rr = mRil.processResponse(responseInfo);
 
         if (rr != null) {
-            ArrayList<OperatorInfo> ret = null;
+            ArrayList<OperatorInfo> ret = new ArrayList<OperatorInfo>();
+            for (int i = 0; i < networkInfos.size(); i++) {
+                ret.add(new OperatorInfo(networkInfos.get(i).alphaLong,
+                        networkInfos.get(i).alphaShort, networkInfos.get(i).operatorNumeric,
+                        convertOpertatorInfoToString(networkInfos.get(i).status)));
+            }
             if (responseInfo.error == RadioError.NONE) {
-                ret =  new ArrayList<OperatorInfo>();
-                for (int i = 0; i < networkInfos.size(); i++) {
-                    ret.add(new OperatorInfo(networkInfos.get(i).alphaLong,
-                            networkInfos.get(i).alphaShort, networkInfos.get(i).operatorNumeric,
-                            convertOpertatorInfoToString(networkInfos.get(i).status)));
-                }
                 sendMessageResponse(rr.mResult, ret);
             }
             mRil.processResponseDone(rr, responseInfo, ret);
@@ -1551,8 +1549,12 @@
         RILRequest rr = mRil.processResponse(responseInfo);
 
         if (rr != null) {
-            NetworkScanResult nsr = new NetworkScanResult(0, responseInfo.error, null);
-            sendMessageResponse(rr.mResult, nsr);
+            NetworkScanResult nsr = null;
+            if (responseInfo.error == RadioError.NONE) {
+                nsr = new NetworkScanResult(
+                        NetworkScanResult.SCAN_STATUS_PARTIAL, RadioError.NONE, null);
+                sendMessageResponse(rr.mResult, nsr);
+            }
             mRil.processResponseDone(rr, responseInfo, nsr);
         }
     }
@@ -1562,12 +1564,11 @@
         RILRequest rr = mRil.processResponse(responseInfo);
 
         if (rr != null) {
-            ArrayList<DataCallResponse> dcResponseList = null;
+            ArrayList<DataCallResponse> dcResponseList = new ArrayList<>();
+            for (SetupDataCallResult dcResult : dataCallResultList) {
+                dcResponseList.add(RIL.convertDataCallResult(dcResult));
+            }
             if (responseInfo.error == RadioError.NONE) {
-                dcResponseList = new ArrayList<>();
-                for (SetupDataCallResult dcResult : dataCallResultList) {
-                    dcResponseList.add(RIL.convertDataCallResult(dcResult));
-                }
                 sendMessageResponse(rr.mResult, dcResponseList);
             }
             mRil.processResponseDone(rr, responseInfo, dcResponseList);
@@ -1581,25 +1582,23 @@
         if (rr != null) {
             int rssi;
             String location;
-            ArrayList<NeighboringCellInfo> ret = null;
+            ArrayList<NeighboringCellInfo> ret = new ArrayList<NeighboringCellInfo>();
             NeighboringCellInfo cell;
 
-            if (responseInfo.error == RadioError.NONE) {
-                ret =  new ArrayList<NeighboringCellInfo>();
+            int[] subId = SubscriptionManager.getSubId(mRil.mPhoneId);
+            int radioType =
+                    ((TelephonyManager) mRil.mContext.getSystemService(
+                            Context.TELEPHONY_SERVICE)).getDataNetworkType(subId[0]);
 
-                int[] subId = SubscriptionManager.getSubId(mRil.mPhoneId);
-                int radioType =
-                        ((TelephonyManager) mRil.mContext.getSystemService(
-                                Context.TELEPHONY_SERVICE)).getDataNetworkType(subId[0]);
-
-                if (radioType != TelephonyManager.NETWORK_TYPE_UNKNOWN) {
-                    for (int i = 0; i < cells.size(); i++) {
-                        rssi = cells.get(i).rssi;
-                        location = cells.get(i).cid;
-                        cell = new NeighboringCellInfo(rssi, location, radioType);
-                        ret.add(cell);
-                    }
+            if (radioType != TelephonyManager.NETWORK_TYPE_UNKNOWN) {
+                for (int i = 0; i < cells.size(); i++) {
+                    rssi = cells.get(i).rssi;
+                    location = cells.get(i).cid;
+                    cell = new NeighboringCellInfo(rssi, location, radioType);
+                    ret.add(cell);
                 }
+            }
+            if (responseInfo.error == RadioError.NONE) {
                 sendMessageResponse(rr.mResult, ret);
             }
             mRil.processResponseDone(rr, responseInfo, ret);
@@ -1611,14 +1610,13 @@
         RILRequest rr = mRil.processResponse(responseInfo);
 
         if (rr != null) {
-            ArrayList<SmsBroadcastConfigInfo> ret = null;
+            ArrayList<SmsBroadcastConfigInfo> ret = new ArrayList<SmsBroadcastConfigInfo>();
+            for (int i = 0; i < configs.size(); i++) {
+                ret.add(new SmsBroadcastConfigInfo(configs.get(i).fromServiceId,
+                        configs.get(i).toServiceId, configs.get(i).fromCodeScheme,
+                        configs.get(i).toCodeScheme, configs.get(i).selected));
+            }
             if (responseInfo.error == RadioError.NONE) {
-                ret =  new ArrayList<SmsBroadcastConfigInfo>();
-                for (int i = 0; i < configs.size(); i++) {
-                    ret.add(new SmsBroadcastConfigInfo(configs.get(i).fromServiceId,
-                            configs.get(i).toServiceId, configs.get(i).fromCodeScheme,
-                            configs.get(i).toCodeScheme, configs.get(i).selected));
-                }
                 sendMessageResponse(rr.mResult, ret);
             }
             mRil.processResponseDone(rr, responseInfo, ret);
@@ -1632,41 +1630,41 @@
         if (rr != null) {
             int[] ret = null;
 
-            if (responseInfo.error == RadioError.NONE) {
-                int numServiceCategories = configs.size();
+            int numServiceCategories = configs.size();
 
-                if (numServiceCategories == 0) {
-                    // TODO: The logic of providing default values should
-                    // not be done by this transport layer. And needs to
-                    // be done by the vendor ril or application logic.
-                    int numInts;
-                    numInts = CDMA_BROADCAST_SMS_NO_OF_SERVICE_CATEGORIES
-                            * CDMA_BSI_NO_OF_INTS_STRUCT + 1;
-                    ret = new int[numInts];
+            if (numServiceCategories == 0) {
+                // TODO: The logic of providing default values should
+                // not be done by this transport layer. And needs to
+                // be done by the vendor ril or application logic.
+                int numInts;
+                numInts = CDMA_BROADCAST_SMS_NO_OF_SERVICE_CATEGORIES
+                        * CDMA_BSI_NO_OF_INTS_STRUCT + 1;
+                ret = new int[numInts];
 
-                    // Faking a default record for all possible records.
-                    ret[0] = CDMA_BROADCAST_SMS_NO_OF_SERVICE_CATEGORIES;
+                // Faking a default record for all possible records.
+                ret[0] = CDMA_BROADCAST_SMS_NO_OF_SERVICE_CATEGORIES;
 
-                    // Loop over CDMA_BROADCAST_SMS_NO_OF_SERVICE_CATEGORIES set 'english' as
-                    // default language and selection status to false for all.
-                    for (int i = 1; i < numInts; i += CDMA_BSI_NO_OF_INTS_STRUCT) {
-                        ret[i + 0] = i / CDMA_BSI_NO_OF_INTS_STRUCT;
-                        ret[i + 1] = 1;
-                        ret[i + 2] = 0;
-                    }
-                } else {
-                    int numInts;
-                    numInts = (numServiceCategories * CDMA_BSI_NO_OF_INTS_STRUCT) + 1;
-                    ret = new int[numInts];
-
-                    ret[0] = numServiceCategories;
-                    for (int i = 1, j = 0; j < configs.size();
-                            j++, i = i + CDMA_BSI_NO_OF_INTS_STRUCT) {
-                        ret[i] = configs.get(j).serviceCategory;
-                        ret[i + 1] = configs.get(j).language;
-                        ret[i + 2] = configs.get(j).selected ? 1 : 0;
-                    }
+                // Loop over CDMA_BROADCAST_SMS_NO_OF_SERVICE_CATEGORIES set 'english' as
+                // default language and selection status to false for all.
+                for (int i = 1; i < numInts; i += CDMA_BSI_NO_OF_INTS_STRUCT) {
+                    ret[i + 0] = i / CDMA_BSI_NO_OF_INTS_STRUCT;
+                    ret[i + 1] = 1;
+                    ret[i + 2] = 0;
                 }
+            } else {
+                int numInts;
+                numInts = (numServiceCategories * CDMA_BSI_NO_OF_INTS_STRUCT) + 1;
+                ret = new int[numInts];
+
+                ret[0] = numServiceCategories;
+                for (int i = 1, j = 0; j < configs.size();
+                        j++, i = i + CDMA_BSI_NO_OF_INTS_STRUCT) {
+                    ret[i] = configs.get(j).serviceCategory;
+                    ret[i + 1] = configs.get(j).language;
+                    ret[i + 2] = configs.get(j).selected ? 1 : 0;
+                }
+            }
+            if (responseInfo.error == RadioError.NONE) {
                 sendMessageResponse(rr.mResult, ret);
             }
             mRil.processResponseDone(rr, responseInfo, ret);
@@ -1678,9 +1676,8 @@
         RILRequest rr = mRil.processResponse(responseInfo);
 
         if (rr != null) {
-            ArrayList<CellInfo> ret = null;
+            ArrayList<CellInfo> ret = RIL.convertHalCellInfoList(cellInfo);
             if (responseInfo.error == RadioError.NONE) {
-                ret =  RIL.convertHalCellInfoList(cellInfo);
                 sendMessageResponse(rr.mResult, ret);
             }
             mRil.processResponseDone(rr, responseInfo, ret);
@@ -1719,9 +1716,8 @@
         RILRequest rr = mRil.processResponse(responseInfo);
 
         if (rr != null) {
-            ArrayList<HardwareConfig> ret = null;
+            ArrayList<HardwareConfig> ret = RIL.convertHalHwConfigList(config, mRil);
             if (responseInfo.error == RadioError.NONE) {
-                ret = RIL.convertHalHwConfigList(config, mRil);
                 sendMessageResponse(rr.mResult, ret);
             }
             mRil.processResponseDone(rr, responseInfo, ret);
@@ -1733,11 +1729,13 @@
         RILRequest rr = mRil.processResponse(responseInfo);
 
         if (rr != null) {
-            IccIoResult ret = null;
+            IccIoResult ret = new IccIoResult(
+                    result.sw1,
+                    result.sw2,
+                    (!(result.simResponse).equals(""))
+                            ? android.util.Base64.decode(result.simResponse,
+                            android.util.Base64.DEFAULT) : (byte[]) null);
             if (responseInfo.error == RadioError.NONE) {
-                ret = new IccIoResult(result.sw1, result.sw2, (!(result.simResponse).equals(""))
-                        ? android.util.Base64.decode(result.simResponse,
-                        android.util.Base64.DEFAULT) : (byte[]) null);
                 sendMessageResponse(rr.mResult, ret);
             }
             mRil.processResponseDone(rr, responseInfo, ret);
@@ -1749,9 +1747,8 @@
         RILRequest rr = mRil.processResponse(responseInfo);
 
         if (rr != null) {
-            RadioCapability ret = null;
+            RadioCapability ret = RIL.convertHalRadioCapability(rc, mRil);
             if (responseInfo.error == RadioError.NONE) {
-                ret = RIL.convertHalRadioCapability(rc, mRil);
                 sendMessageResponse(rr.mResult, ret);
             }
             mRil.processResponseDone(rr, responseInfo, ret);
@@ -1762,11 +1759,10 @@
         RILRequest rr = mRil.processResponse(responseInfo);
 
         if (rr != null) {
-            ArrayList<Integer> ret = null;
+            ArrayList<Integer> ret = new ArrayList<Integer>();
+            ret.add(statusInfo.lceStatus);
+            ret.add(Byte.toUnsignedInt(statusInfo.actualIntervalMs));
             if (responseInfo.error == RadioError.NONE) {
-                ret = new ArrayList<Integer>();
-                ret.add(statusInfo.lceStatus);
-                ret.add((int) statusInfo.actualIntervalMs);
                 sendMessageResponse(rr.mResult, ret);
             }
             mRil.processResponseDone(rr, responseInfo, ret);
@@ -1777,9 +1773,8 @@
         RILRequest rr = mRil.processResponse(responseInfo);
 
         if (rr != null) {
-            ArrayList<Integer> ret = null;
+            ArrayList<Integer> ret = RIL.convertHalLceData(lceInfo, mRil);
             if (responseInfo.error == RadioError.NONE) {
-                ret = RIL.convertHalLceData(lceInfo, mRil);
                 sendMessageResponse(rr.mResult, ret);
             }
             mRil.processResponseDone(rr, responseInfo, ret);
@@ -1791,26 +1786,25 @@
         RILRequest rr = mRil.processResponse(responseInfo);
 
         if (rr != null) {
-            List<CarrierIdentifier> ret = null;
-            if (responseInfo.error == RadioError.NONE) {
-                ret = new ArrayList<CarrierIdentifier>();
-                for (int i = 0; i < carriers.allowedCarriers.size(); i++) {
-                    String mcc = carriers.allowedCarriers.get(i).mcc;
-                    String mnc = carriers.allowedCarriers.get(i).mnc;
-                    String spn = null, imsi = null, gid1 = null, gid2 = null;
-                    int matchType = carriers.allowedCarriers.get(i).matchType;
-                    String matchData = carriers.allowedCarriers.get(i).matchData;
-                    if (matchType == CarrierIdentifier.MatchType.SPN) {
-                        spn = matchData;
-                    } else if (matchType == CarrierIdentifier.MatchType.IMSI_PREFIX) {
-                        imsi = matchData;
-                    } else if (matchType == CarrierIdentifier.MatchType.GID1) {
-                        gid1 = matchData;
-                    } else if (matchType == CarrierIdentifier.MatchType.GID2) {
-                        gid2 = matchData;
-                    }
-                    ret.add(new CarrierIdentifier(mcc, mnc, spn, imsi, gid1, gid2));
+            List<CarrierIdentifier> ret = new ArrayList<CarrierIdentifier>();
+            for (int i = 0; i < carriers.allowedCarriers.size(); i++) {
+                String mcc = carriers.allowedCarriers.get(i).mcc;
+                String mnc = carriers.allowedCarriers.get(i).mnc;
+                String spn = null, imsi = null, gid1 = null, gid2 = null;
+                int matchType = carriers.allowedCarriers.get(i).matchType;
+                String matchData = carriers.allowedCarriers.get(i).matchData;
+                if (matchType == CarrierIdentifier.MatchType.SPN) {
+                    spn = matchData;
+                } else if (matchType == CarrierIdentifier.MatchType.IMSI_PREFIX) {
+                    imsi = matchData;
+                } else if (matchType == CarrierIdentifier.MatchType.GID1) {
+                    gid1 = matchData;
+                } else if (matchType == CarrierIdentifier.MatchType.GID2) {
+                    gid2 = matchData;
                 }
+                ret.add(new CarrierIdentifier(mcc, mnc, spn, imsi, gid1, gid2));
+            }
+            if (responseInfo.error == RadioError.NONE) {
                 /* TODO: Handle excluded carriers */
                 sendMessageResponse(rr.mResult, ret);
             }
diff --git a/src/java/com/android/internal/telephony/RetryManager.java b/src/java/com/android/internal/telephony/RetryManager.java
index 684de9a..23c3498 100644
--- a/src/java/com/android/internal/telephony/RetryManager.java
+++ b/src/java/com/android/internal/telephony/RetryManager.java
@@ -27,8 +27,6 @@
 
 import com.android.internal.telephony.dataconnection.ApnSetting;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Random;
 
@@ -112,6 +110,11 @@
     private static final long DEFAULT_INTER_APN_DELAY_FOR_PROVISIONING = 3000;
 
     /**
+     * The default value (in milliseconds) for retrying APN after disconnect
+     */
+    private static final long DEFAULT_APN_RETRY_AFTER_DISCONNECT_DELAY = 10000;
+
+    /**
      * The value indicating no retry is needed
      */
     public static final long NO_RETRY = -1;
@@ -141,6 +144,12 @@
     private long mFailFastInterApnDelay;
 
     /**
+     * The delay (in milliseconds) for APN retrying after disconnect (e.g. Modem suddenly reports
+     * data call lost)
+     */
+    private long mApnRetryAfterDisconnectDelay;
+
+    /**
      * Modem suggested delay for retrying the current APN
      */
     private long mModemSuggestedDelay = NO_SUGGESTED_RETRY_DELAY;
@@ -337,6 +346,9 @@
             mFailFastInterApnDelay = b.getLong(
                     CarrierConfigManager.KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG,
                     DEFAULT_INTER_APN_DELAY_FOR_PROVISIONING);
+            mApnRetryAfterDisconnectDelay = b.getLong(
+                    CarrierConfigManager.KEY_CARRIER_DATA_CALL_APN_RETRY_AFTER_DISCONNECT_LONG,
+                    DEFAULT_APN_RETRY_AFTER_DISCONNECT_DELAY);
 
             // Load all retry patterns for all different APNs.
             String[] allConfigStrings = b.getStringArray(
@@ -645,44 +657,21 @@
     }
 
     /**
-     * Get the delay between APN setting trying. This is the fixed delay used for APN setting trying
-     * within the same round, comparing to the exponential delay used for different rounds.
-     * @param failFastEnabled True if fail fast mode enabled, which a shorter delay will be used
+     * Get the delay in milliseconds for APN retry after disconnect
      * @return The delay in milliseconds
      */
-    public long getInterApnDelay(boolean failFastEnabled) {
-        return (failFastEnabled) ? mFailFastInterApnDelay : mInterApnDelay;
+    public long getRetryAfterDisconnectDelay() {
+        return mApnRetryAfterDisconnectDelay;
     }
 
     public String toString() {
-        return "mApnType=" + mApnType + " mRetryCount=" + mRetryCount +
-                " mMaxRetryCount=" + mMaxRetryCount + " mCurrentApnIndex=" + mCurrentApnIndex +
-                " mSameApnRtryCount=" + mSameApnRetryCount + " mModemSuggestedDelay=" +
-                mModemSuggestedDelay + " mRetryForever=" + mRetryForever +
-                " mConfig={" + mConfig + "}";
-    }
-
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println("  RetryManager");
-        pw.println("***************************************");
-
-        pw.println("    config = " + mConfig);
-        pw.println("    mApnType = " + mApnType);
-        pw.println("    mCurrentApnIndex = " + mCurrentApnIndex);
-        pw.println("    mRetryCount = " + mRetryCount);
-        pw.println("    mMaxRetryCount = " + mMaxRetryCount);
-        pw.println("    mSameApnRetryCount = " + mSameApnRetryCount);
-        pw.println("    mModemSuggestedDelay = " + mModemSuggestedDelay);
-
-        if (mWaitingApns != null) {
-            pw.println("    APN list: ");
-            for (int i = 0; i < mWaitingApns.size(); i++) {
-                pw.println("      [" + i + "]=" + mWaitingApns.get(i));
-            }
-        }
-
-        pw.println("***************************************");
-        pw.flush();
+        if (mConfig == null) return "";
+        return "RetryManager: mApnType=" + mApnType + " mRetryCount=" + mRetryCount
+                + " mMaxRetryCount=" + mMaxRetryCount + " mCurrentApnIndex=" + mCurrentApnIndex
+                + " mSameApnRtryCount=" + mSameApnRetryCount + " mModemSuggestedDelay="
+                + mModemSuggestedDelay + " mRetryForever=" + mRetryForever + " mInterApnDelay="
+                + mInterApnDelay + " mApnRetryAfterDisconnectDelay=" + mApnRetryAfterDisconnectDelay
+                + " mConfig={" + mConfig + "}";
     }
 
     private void log(String s) {
diff --git a/src/java/com/android/internal/telephony/RilWakelockInfo.java b/src/java/com/android/internal/telephony/RilWakelockInfo.java
index 319f2fa..5d9e54b 100644
--- a/src/java/com/android/internal/telephony/RilWakelockInfo.java
+++ b/src/java/com/android/internal/telephony/RilWakelockInfo.java
@@ -18,7 +18,8 @@
 
 import android.annotation.TargetApi;
 import android.os.Build;
-import android.util.Log;
+import android.telephony.Rlog;
+
 import com.android.internal.annotations.VisibleForTesting;
 
 @TargetApi(8)
@@ -59,7 +60,7 @@
             if(Build.IS_DEBUGGABLE) {
                 IllegalArgumentException e = new IllegalArgumentException(
                     "concurrentRequests should always be greater than 0.");
-                Log.wtf(LOG_TAG, e);
+                Rlog.e(LOG_TAG, e.toString());
                 throw e;
             } else {
                 concurrentRequests = 1;
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
index d1e8a0c..2ec5101 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -23,6 +23,8 @@
 import static android.telephony.SmsManager.RESULT_ERROR_NO_SERVICE;
 import static android.telephony.SmsManager.RESULT_ERROR_NULL_PDU;
 import static android.telephony.SmsManager.RESULT_ERROR_RADIO_OFF;
+import static android.telephony.SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED;
+import static android.telephony.SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED;
 
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -74,6 +76,7 @@
 import android.widget.TextView;
 
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
 import com.android.internal.telephony.uicc.UiccCard;
 import com.android.internal.telephony.uicc.UiccController;
@@ -313,7 +316,25 @@
         case EVENT_STOP_SENDING:
         {
             SmsTracker tracker = (SmsTracker) msg.obj;
-            tracker.onFailed(mContext, RESULT_ERROR_LIMIT_EXCEEDED, 0/*errorCode*/);
+            if (msg.arg1 == ConfirmDialogListener.SHORT_CODE_MSG) {
+                if (msg.arg2 == ConfirmDialogListener.NEVER_ALLOW) {
+                    tracker.onFailed(mContext,
+                            RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED, 0/*errorCode*/);
+                    Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - "
+                            + "sending SHORT_CODE_NEVER_ALLOWED error code.");
+                } else {
+                    tracker.onFailed(mContext,
+                            RESULT_ERROR_SHORT_CODE_NOT_ALLOWED, 0/*errorCode*/);
+                    Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - "
+                            + "sending SHORT_CODE_NOT_ALLOWED error code.");
+                }
+            } else if (msg.arg1 == ConfirmDialogListener.RATE_LIMIT) {
+                tracker.onFailed(mContext, RESULT_ERROR_LIMIT_EXCEEDED, 0/*errorCode*/);
+                Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - "
+                        + "sending LIMIT_EXCEEDED error code.");
+            } else {
+                Rlog.e(TAG, "SMSDispatcher: EVENT_STOP_SENDING - unexpected cases.");
+            }
             mPendingTrackerCount--;
             break;
         }
@@ -950,7 +971,8 @@
      *  raw pdu of the status report is in the extended data ("pdu").
      * -param destAddr the destination phone number (for short code confirmation)
      */
-    protected void sendRawPdu(SmsTracker tracker) {
+    @VisibleForTesting
+    public void sendRawPdu(SmsTracker tracker) {
         HashMap map = tracker.getData();
         byte pdu[] = (byte[]) map.get("pdu");
 
@@ -1055,7 +1077,7 @@
 
             // Wait for user confirmation unless the user has set permission to always allow/deny
             int premiumSmsPermission = mUsageMonitor.getPremiumSmsPermission(
-                    tracker.mAppInfo.packageName);
+                    tracker.getAppPackageName());
             if (premiumSmsPermission == SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) {
                 // First time trying to send to premium SMS.
                 premiumSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER;
@@ -1068,7 +1090,10 @@
 
                 case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW:
                     Rlog.w(TAG, "User denied this app from sending to premium SMS");
-                    sendMessage(obtainMessage(EVENT_STOP_SENDING, tracker));
+                    Message msg = obtainMessage(EVENT_STOP_SENDING, tracker);
+                    msg.arg1 = ConfirmDialogListener.SHORT_CODE_MSG;
+                    msg.arg2 = ConfirmDialogListener.NEVER_ALLOW;
+                    sendMessage(msg);
                     return false;   // reject this message
 
                 case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER:
@@ -1127,11 +1152,13 @@
             return;     // queue limit reached; error was returned to caller
         }
 
-        CharSequence appLabel = getAppLabel(tracker.mAppInfo.packageName, tracker.mUserId);
+        CharSequence appLabel = getAppLabel(tracker.getAppPackageName(), tracker.mUserId);
         Resources r = Resources.getSystem();
         Spanned messageText = Html.fromHtml(r.getString(R.string.sms_control_message, appLabel));
 
-        ConfirmDialogListener listener = new ConfirmDialogListener(tracker, null);
+        // Construct ConfirmDialogListenter for Rate Limit handling
+        ConfirmDialogListener listener = new ConfirmDialogListener(tracker, null,
+                ConfirmDialogListener.RATE_LIMIT);
 
         AlertDialog d = new AlertDialog.Builder(mContext)
                 .setTitle(R.string.sms_control_title)
@@ -1163,7 +1190,7 @@
             detailsId = R.string.sms_short_code_details;
         }
 
-        CharSequence appLabel = getAppLabel(tracker.mAppInfo.packageName, tracker.mUserId);
+        CharSequence appLabel = getAppLabel(tracker.getAppPackageName(), tracker.mUserId);
         Resources r = Resources.getSystem();
         Spanned messageText = Html.fromHtml(r.getString(R.string.sms_short_code_confirm_message,
                 appLabel, tracker.mDestAddress));
@@ -1172,8 +1199,10 @@
                 Context.LAYOUT_INFLATER_SERVICE);
         View layout = inflater.inflate(R.layout.sms_short_code_confirmation_dialog, null);
 
+        // Construct ConfirmDialogListenter for short code message sending
         ConfirmDialogListener listener = new ConfirmDialogListener(tracker,
-                (TextView)layout.findViewById(R.id.sms_short_code_remember_undo_instruction));
+                (TextView) layout.findViewById(R.id.sms_short_code_remember_undo_instruction),
+                ConfirmDialogListener.SHORT_CODE_MSG);
 
 
         TextView messageView = (TextView) layout.findViewById(R.id.sms_short_code_confirm_message);
@@ -1374,6 +1403,14 @@
         }
 
         /**
+         * Get the App package name
+         * @return App package name info
+         */
+        public String getAppPackageName() {
+            return mAppInfo != null ? mAppInfo.packageName : null;
+        }
+
+        /**
          * Update the status of this message if we persisted it
          */
         public void updateSentMessageStatus(Context context, int status) {
@@ -1634,10 +1671,15 @@
         private Button mNegativeButton;
         private boolean mRememberChoice;    // default is unchecked
         private final TextView mRememberUndoInstruction;
+        private int mConfirmationType;  // 0 - Short Code Msg Sending; 1 - Rate Limit Exceeded
+        private static final int SHORT_CODE_MSG = 0; // Short Code Msg
+        private static final int RATE_LIMIT = 1; // Rate Limit Exceeded
+        private static final int NEVER_ALLOW = 1; // Never Allow
 
-        ConfirmDialogListener(SmsTracker tracker, TextView textView) {
+        ConfirmDialogListener(SmsTracker tracker, TextView textView, int confirmationType) {
             mTracker = tracker;
             mRememberUndoInstruction = textView;
+            mConfirmationType = confirmationType;
         }
 
         void setPositiveButton(Button button) {
@@ -1670,18 +1712,23 @@
                 EventLog.writeEvent(EventLogTags.EXP_DET_SMS_DENIED_BY_USER,
                                     mTracker.mAppInfo.applicationInfo == null ?
                                     -1 :  mTracker.mAppInfo.applicationInfo.uid);
-                sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker));
+                Message msg = obtainMessage(EVENT_STOP_SENDING, mTracker);
+                msg.arg1 = mConfirmationType;
                 if (mRememberChoice) {
                     newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW;
+                    msg.arg2 = ConfirmDialogListener.NEVER_ALLOW;
                 }
+                sendMessage(msg);
             }
-            setPremiumSmsPermission(mTracker.mAppInfo.packageName, newSmsPermission);
+            setPremiumSmsPermission(mTracker.getAppPackageName(), newSmsPermission);
         }
 
         @Override
         public void onCancel(DialogInterface dialog) {
             Rlog.d(TAG, "dialog dismissed: don't send SMS");
-            sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker));
+            Message msg = obtainMessage(EVENT_STOP_SENDING, mTracker);
+            msg.arg1 = mConfirmationType;
+            sendMessage(msg);
         }
 
         @Override
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index d5f6997..fb8231e 100644
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -86,6 +86,8 @@
 import com.android.internal.telephony.uicc.SIMRecords;
 import com.android.internal.telephony.uicc.UiccCardApplication;
 import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.telephony.util.NotificationChannelController;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.IndentingPrintWriter;
 
 import java.io.FileDescriptor;
@@ -175,7 +177,6 @@
     protected static final int EVENT_POLL_SIGNAL_STRENGTH              = 10;
     protected static final int EVENT_NITZ_TIME                         = 11;
     protected static final int EVENT_SIGNAL_STRENGTH_UPDATE            = 12;
-    protected static final int EVENT_RADIO_AVAILABLE                   = 13;
     protected static final int EVENT_POLL_STATE_NETWORK_SELECTION_MODE = 14;
     protected static final int EVENT_GET_LOC_DONE                      = 15;
     protected static final int EVENT_SIM_RECORDS_LOADED                = 16;
@@ -541,6 +542,7 @@
         mSubscriptionManager = SubscriptionManager.from(phone.getContext());
         mSubscriptionManager
                 .addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
+        mRestrictedState = new RestrictedState();
 
         mCi.registerForImsNetworkStateChanged(this, EVENT_IMS_STATE_CHANGED, null);
 
@@ -586,6 +588,7 @@
 
         mPhone.notifyOtaspChanged(TelephonyManager.OTASP_UNINITIALIZED);
 
+        mCi.setOnRestrictedStateChanged(this, EVENT_RESTRICTED_STATE_CHANGED, null);
         updatePhoneType();
 
         mCSST = new CarrierServiceStateTracker(phone, this);
@@ -624,7 +627,6 @@
         mLastCellInfoListTime = 0;
         mLastCellInfoList = null;
         mSignalStrength = new SignalStrength();
-        mRestrictedState = new RestrictedState();
         mStartedGprsRegCheck = false;
         mReportedGprsNoReg = false;
         mMdn = null;
@@ -649,13 +651,7 @@
 
             mCellLoc = new GsmCellLocation();
             mNewCellLoc = new GsmCellLocation();
-            mCi.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);
-            mCi.setOnRestrictedStateChanged(this, EVENT_RESTRICTED_STATE_CHANGED, null);
         } else {
-            //clear GSM regsitrations first
-            mCi.unregisterForAvailable(this);
-            mCi.unSetOnRestrictedStateChanged(this);
-
             if (mPhone.isPhoneTypeCdmaLte()) {
                 mPhone.registerForSimRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null);
             }
@@ -725,6 +721,7 @@
             try {
                 mPhone.notifySignalStrength();
                 notified = true;
+                mLastSignalStrength = mSignalStrength;
             } catch (NullPointerException ex) {
                 loge("updateSignalStrength() Phone already destroyed: " + ex
                         + "SignalStrength not notified");
@@ -847,12 +844,13 @@
      * @param h handler to notify
      * @param what what code of message when delivered
      * @param obj placed in Message.obj
+     * @param notifyNow notify upon registration if data roaming is off
      */
-    public void registerForDataRoamingOff(Handler h, int what, Object obj) {
+    public void registerForDataRoamingOff(Handler h, int what, Object obj, boolean notifyNow) {
         Registrant r = new Registrant(h, what, obj);
         mDataRoamingOffRegistrants.add(r);
 
-        if (!mSS.getDataRoaming()) {
+        if (notifyNow && !mSS.getDataRoaming()) {
             r.notifyRegistrant();
         }
     }
@@ -1118,12 +1116,7 @@
                 }
                 break;
 
-            //GSM
-            case EVENT_RADIO_AVAILABLE:
-                //this is unnecessary
-                //setPowerStateToDesired();
-                break;
-
+            // GSM
             case EVENT_SIM_READY:
                 // Reset the mPreviousSubId so we treat a SIM power bounce
                 // as a first boot.  See b/19194287
@@ -1658,7 +1651,13 @@
 
                 // Setting SS Roaming (general)
                 if (mIsSubscriptionFromRuim) {
-                    mNewSS.setVoiceRoaming(isRoamingBetweenOperators(mNewSS.getVoiceRoaming(), mNewSS));
+                    boolean isRoamingBetweenOperators = isRoamingBetweenOperators(
+                            mNewSS.getVoiceRoaming(), mNewSS);
+                    if (isRoamingBetweenOperators != mNewSS.getVoiceRoaming()) {
+                        log("isRoamingBetweenOperators=" + isRoamingBetweenOperators
+                                + ". Override CDMA voice roaming to " + isRoamingBetweenOperators);
+                        mNewSS.setVoiceRoaming(isRoamingBetweenOperators);
+                    }
                 }
                 /**
                  * For CDMA, voice and data should have the same roaming status.
@@ -1671,15 +1670,25 @@
                     final boolean isVoiceInService =
                             (mNewSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE);
                     if (isVoiceInService) {
-                        mNewSS.setDataRoaming(mNewSS.getVoiceRoaming());
+                        boolean isVoiceRoaming = mNewSS.getVoiceRoaming();
+                        if (mNewSS.getDataRoaming() != isVoiceRoaming) {
+                            log("Data roaming != Voice roaming. Override data roaming to "
+                                    + isVoiceRoaming);
+                            mNewSS.setDataRoaming(isVoiceRoaming);
+                        }
                     } else {
                         /**
                          * As per VoiceRegStateResult from radio types.hal the TSB58
                          * Roaming Indicator shall be sent if device is registered
                          * on a CDMA or EVDO system.
                          */
-                        mNewSS.setDataRoaming(
-                                !isRoamIndForHomeSystem(Integer.toString(mRoamingIndicator)));
+                        boolean isRoamIndForHomeSystem = isRoamIndForHomeSystem(
+                                Integer.toString(mRoamingIndicator));
+                        if (mNewSS.getDataRoaming() == isRoamIndForHomeSystem) {
+                            log("isRoamIndForHomeSystem=" + isRoamIndForHomeSystem
+                                    + ", override data roaming to " + !isRoamIndForHomeSystem);
+                            mNewSS.setDataRoaming(!isRoamIndForHomeSystem);
+                        }
                     }
                 }
 
@@ -1787,8 +1796,10 @@
             case EVENT_POLL_STATE_REGISTRATION: {
                 VoiceRegStateResult voiceRegStateResult = (VoiceRegStateResult) ar.result;
                 int registrationState = getRegStateFromHalRegState(voiceRegStateResult.regState);
+                int cssIndicator = voiceRegStateResult.cssSupported ? 1 : 0;
 
                 mNewSS.setVoiceRegState(regCodeToServiceState(registrationState));
+                mNewSS.setCssIndicator(cssIndicator);
                 mNewSS.setRilVoiceRadioTechnology(voiceRegStateResult.rat);
 
                 //Denial reason if registrationState = 3
@@ -1814,8 +1825,6 @@
                         mEmergencyOnly = false;
                     }
                 } else {
-                    //init with 0, because it is treated as a boolean
-                    int cssIndicator = voiceRegStateResult.cssSupported ? 1 : 0;
                     int roamingIndicator = voiceRegStateResult.roamingIndicator;
 
                     //Indicates if current system is in PR
@@ -1833,7 +1842,6 @@
                                     && !isRoamIndForHomeSystem(
                                             Integer.toString(roamingIndicator));
                     mNewSS.setVoiceRoaming(cdmaRoaming);
-                    mNewSS.setCssIndicator(cssIndicator);
                     mRoamingIndicator = roamingIndicator;
                     mIsInPrl = (systemIsInPrl == 0) ? false : true;
                     mDefaultRoamingIndicator = defaultRoamingIndicator;
@@ -1884,6 +1892,9 @@
                     mNewReasonDataDenied = dataRegStateResult.reasonDataDenied;
                     mNewMaxDataCalls = dataRegStateResult.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
@@ -1892,7 +1903,11 @@
                     }
                 } else if (mPhone.isPhoneTypeCdma()) {
 
-                    mNewSS.setDataRoaming(regCodeIsRoaming(regState));
+                    boolean isDataRoaming = regCodeIsRoaming(regState);
+                    mNewSS.setDataRoaming(isDataRoaming);
+                    // Save the data roaming state reported by modem registration before resource
+                    // overlay or carrier config possibly overrides it.
+                    mNewSS.setDataRoamingFromRegistration(isDataRoaming);
 
                     if (DBG) {
                         log("handlPollStateResultMessage: cdma setDataRegState=" + dataRegState
@@ -1917,11 +1932,15 @@
                     }
 
                     // voice roaming state in done while handling EVENT_POLL_STATE_REGISTRATION_CDMA
-                    mNewSS.setDataRoaming(regCodeIsRoaming(regState));
+                    boolean isDataRoaming = regCodeIsRoaming(regState);
+                    mNewSS.setDataRoaming(isDataRoaming);
+                    // Save the data roaming state reported by modem registration before resource
+                    // overlay or carrier config possibly overrides it.
+                    mNewSS.setDataRoamingFromRegistration(isDataRoaming);
                     if (DBG) {
-                        log("handlPollStateResultMessage: CdmaLteSST setDataRegState=" + dataRegState
-                                + " regState=" + regState
-                                + " dataRadioTechnology=" + newDataRat);
+                        log("handlPollStateResultMessage: CdmaLteSST setDataRegState="
+                                + dataRegState + " regState=" + regState + " dataRadioTechnology="
+                                + newDataRat);
                     }
                 }
 
@@ -1984,7 +2003,7 @@
             case EVENT_POLL_STATE_NETWORK_SELECTION_MODE: {
                 ints = (int[])ar.result;
                 mNewSS.setIsManualSelection(ints[0] == 1);
-                if ((ints[0] == 1) && (!mPhone.isManualNetSelAllowed())) {
+                if ((ints[0] == 1) && (mPhone.shouldForceAutoNetworkSelect())) {
                         /*
                          * modem is currently in manual selection but manual
                          * selection is not allowed in the current mode so
@@ -2011,8 +2030,9 @@
      */
     private boolean isRoamIndForHomeSystem(String roamInd) {
         // retrieve the carrier-specified list of ERIs for home system
-        String[] homeRoamIndicators = mPhone.getContext().getResources()
+        String[] homeRoamIndicators = Resources.getSystem()
                 .getStringArray(com.android.internal.R.array.config_cdma_home_system);
+        log("isRoamIndForHomeSystem: homeRoamIndicators=" + Arrays.toString(homeRoamIndicators));
 
         if (homeRoamIndicators != null) {
             // searches through the comma-separated list for a match,
@@ -2023,10 +2043,12 @@
                 }
             }
             // no matches found against the list!
+            log("isRoamIndForHomeSystem: No match found against list for roamInd=" + roamInd);
             return false;
         }
 
         // no system property found for the roaming indicators for home system
+        log("isRoamIndForHomeSystem: No list found");
         return false;
     }
 
@@ -2050,14 +2072,15 @@
              * agreements and MVNO's.
              */
             boolean roaming = (mGsmRoaming || mDataRoaming);
-            if (mGsmRoaming && !isOperatorConsideredRoaming(mNewSS) &&
-                    (isSameNamedOperators(mNewSS) || isOperatorConsideredNonRoaming(mNewSS))) {
+
+            if (mGsmRoaming && !isOperatorConsideredRoaming(mNewSS)
+                    && (isSameNamedOperators(mNewSS) || isOperatorConsideredNonRoaming(mNewSS))) {
+                log("updateRoamingState: resource override set non roaming.isSameNamedOperators="
+                        + isSameNamedOperators(mNewSS) + ",isOperatorConsideredNonRoaming="
+                        + isOperatorConsideredNonRoaming(mNewSS));
                 roaming = false;
             }
 
-            // Save the roaming state before carrier config possibly overrides it.
-            mNewSS.setDataRoamingFromRegistration(roaming);
-
             CarrierConfigManager configLoader = (CarrierConfigManager)
                     mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
 
@@ -2087,9 +2110,6 @@
             mNewSS.setVoiceRoaming(roaming);
             mNewSS.setDataRoaming(roaming);
         } else {
-            // Save the roaming state before carrier config possibly overrides it.
-            mNewSS.setDataRoamingFromRegistration(mNewSS.getDataRoaming());
-
             CarrierConfigManager configLoader = (CarrierConfigManager)
                     mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
             if (configLoader != null) {
@@ -2288,7 +2308,7 @@
             mCurDataSpn = dataSpn;
             mCurPlmn = plmn;
         } else {
-            // mOperatorAlphaLong contains the ERI text
+            // mOperatorAlpha contains the ERI text
             String plmn = mSS.getOperatorAlpha();
             boolean showPlmn = false;
 
@@ -2478,16 +2498,13 @@
      * that could support voice and data simultaneously.
      */
     public boolean isConcurrentVoiceAndDataAllowed() {
-        if (mPhone.isPhoneTypeGsm()) {
-            return (mSS.getRilVoiceRadioTechnology() >= ServiceState.RIL_RADIO_TECHNOLOGY_UMTS);
-        } else if (mPhone.isPhoneTypeCdma()) {
-            // Note: it needs to be confirmed which CDMA network types
-            // can support voice and data calls concurrently.
-            // For the time-being, the return value will be false.
-            return false;
+        if (mSS.getCssIndicator() == 1) {
+            // Checking the Concurrent Service Supported flag first for all phone types.
+            return true;
+        } else if (mPhone.isPhoneTypeGsm()) {
+            return (mSS.getRilDataRadioTechnology() >= ServiceState.RIL_RADIO_TECHNOLOGY_UMTS);
         } else {
-            // Using the Conncurrent Service Supported flag for CdmaLte devices.
-            return mSS.getCssIndicator() == 1;
+            return false;
         }
     }
 
@@ -2660,6 +2677,8 @@
 
         boolean hasRejectCauseChanged = mRejectCode != mNewRejectCode;
 
+        boolean hasCssIndicatorChanged = (mSS.getCssIndicator() != mNewSS.getCssIndicator());
+
         boolean has4gHandoff = false;
         boolean hasMultiApnSupport = false;
         boolean hasLostMultiApnSupport = false;
@@ -2705,7 +2724,8 @@
                     + " hasLocationChanged=" + hasLocationChanged
                     + " has4gHandoff = " + has4gHandoff
                     + " hasMultiApnSupport=" + hasMultiApnSupport
-                    + " hasLostMultiApnSupport=" + hasLostMultiApnSupport);
+                    + " hasLostMultiApnSupport=" + hasLostMultiApnSupport
+                    + " hasCssIndicatorChanged=" + hasCssIndicatorChanged);
         }
 
         // Add an event log when connection state changes
@@ -2740,6 +2760,11 @@
                             mNewSS.getRilVoiceRadioTechnology()) + " at cell " + cid);
                 }
             }
+
+            if (hasCssIndicatorChanged) {
+                mPhone.notifyDataConnection(Phone.REASON_CSS_INDICATOR_CHANGED);
+            }
+
             mReasonDataDenied = mNewReasonDataDenied;
             mMaxDataCalls = mNewMaxDataCalls;
             mRejectCode = mNewRejectCode;
@@ -2815,7 +2840,13 @@
                 tm.setNetworkCountryIsoForPhone(mPhone.getPhoneId(), "");
                 mGotCountryCode = false;
                 mNitzUpdatedTime = false;
-            } else {
+            } else if (mSS.getRilDataRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN) {
+                // Update time zone, ISO, and IDD.
+                //
+                // If the device is on IWLAN, modems manufacture a ServiceState with the MCC/MNC of
+                // the SIM as if we were talking to towers. Telephony code then uses that with
+                // mccTable to suggest a timezone. We shouldn't do that if the MCC/MNC is from IWLAN
+
                 String iso = "";
                 String mcc = "";
                 try {
@@ -2830,31 +2861,7 @@
 
                 if (!mNitzUpdatedTime && !mcc.equals("000") && !TextUtils.isEmpty(iso)
                         && getAutoTimeZone()) {
-
-                    // Test both paths if ignore nitz is true
-                    boolean testOneUniqueOffsetPath = SystemProperties.getBoolean(
-                            TelephonyProperties.PROPERTY_IGNORE_NITZ, false)
-                            && ((SystemClock.uptimeMillis() & 1) == 0);
-
-                    List<String> uniqueZoneIds = TimeUtils.getTimeZoneIdsWithUniqueOffsets(iso);
-                    if ((uniqueZoneIds.size() == 1) || testOneUniqueOffsetPath) {
-                        String zoneId = uniqueZoneIds.get(0);
-                        if (DBG) {
-                            log("pollStateDone: no nitz but one TZ for iso-cc=" + iso
-                                    + " with zone.getID=" + zoneId
-                                    + " testOneUniqueOffsetPath=" + testOneUniqueOffsetPath);
-                        }
-                        mTimeZoneLog.log("pollStateDone: set time zone=" + zoneId
-                                + " mcc=" + mcc + " iso=" + iso);
-                        setAndBroadcastNetworkSetTimeZone(zoneId);
-                    } else {
-                        if (DBG) {
-                            log("pollStateDone: there are " + uniqueZoneIds.size()
-                                    + " unique offsets for iso-cc='" + iso
-                                    + " testOneUniqueOffsetPath=" + testOneUniqueOffsetPath
-                                    + "', do nothing");
-                        }
-                    }
+                    updateTimeZoneByNetworkCountryCode(iso);
                 }
 
                 if (!mPhone.isPhoneTypeGsm()) {
@@ -3144,8 +3151,6 @@
                 + mNeedFixZoneAfterNitz + " zone=" + (zone != null ? zone.getID() : "NULL");
         mTimeZoneLog.log(tmpLog);
 
-        mNeedFixZoneAfterNitz = false;
-
         if (zone != null) {
             log("fixTimeZone: zone != null zone.getID=" + zone.getID());
             if (getAutoTimeZone()) {
@@ -3153,10 +3158,13 @@
             } else {
                 log("fixTimeZone: skip changing zone as getAutoTimeZone was false");
             }
-            saveNitzTimeZone(zone.getID());
+            if (mNeedFixZoneAfterNitz) {
+                saveNitzTimeZone(zone.getID());
+            }
         } else {
             log("fixTimeZone: zone == null, do nothing for zone");
         }
+        mNeedFixZoneAfterNitz = false;
     }
 
     /**
@@ -3275,7 +3283,8 @@
     /**
      * Do not set roaming state in case of oprators considered non-roaming.
      *
-     * Can use mcc or mcc+mnc as item of config_operatorConsideredNonRoaming.
+     * Can use mcc or mcc+mnc as item of
+     * {@link CarrierConfigManager#KEY_NON_ROAMING_OPERATOR_STRING_ARRAY}.
      * For example, 302 or 21407. If mcc or mcc+mnc match with operator,
      * don't set roaming state.
      *
@@ -3284,15 +3293,22 @@
      */
     private boolean isOperatorConsideredNonRoaming(ServiceState s) {
         String operatorNumeric = s.getOperatorNumeric();
-        String[] numericArray = mPhone.getContext().getResources().getStringArray(
-                com.android.internal.R.array.config_operatorConsideredNonRoaming);
-
-        if (numericArray.length == 0 || operatorNumeric == null) {
+        final CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
+                .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        String[] numericArray = null;
+        if (configManager != null) {
+            PersistableBundle config = configManager.getConfigForSubId(mPhone.getSubId());
+            if (config != null) {
+                numericArray = config.getStringArray(
+                        CarrierConfigManager.KEY_NON_ROAMING_OPERATOR_STRING_ARRAY);
+            }
+        }
+        if (ArrayUtils.isEmpty(numericArray) || operatorNumeric == null) {
             return false;
         }
 
         for (String numeric : numericArray) {
-            if (operatorNumeric.startsWith(numeric)) {
+            if (!TextUtils.isEmpty(numeric) && operatorNumeric.startsWith(numeric)) {
                 return true;
             }
         }
@@ -3301,15 +3317,22 @@
 
     private boolean isOperatorConsideredRoaming(ServiceState s) {
         String operatorNumeric = s.getOperatorNumeric();
-        String[] numericArray = mPhone.getContext().getResources().getStringArray(
-                com.android.internal.R.array.config_sameNamedOperatorConsideredRoaming);
-
-        if (numericArray.length == 0 || operatorNumeric == null) {
+        final CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
+                .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        String[] numericArray = null;
+        if (configManager != null) {
+            PersistableBundle config = configManager.getConfigForSubId(mPhone.getSubId());
+            if (config != null) {
+                numericArray = config.getStringArray(
+                        CarrierConfigManager.KEY_ROAMING_OPERATOR_STRING_ARRAY);
+            }
+        }
+        if (ArrayUtils.isEmpty(numericArray) || operatorNumeric == null) {
             return false;
         }
 
         for (String numeric : numericArray) {
-            if (operatorNumeric.startsWith(numeric)) {
+            if (!TextUtils.isEmpty(numeric) && operatorNumeric.startsWith(numeric)) {
                 return true;
             }
         }
@@ -3419,7 +3442,7 @@
     public CellLocation getCellLocation(WorkSource workSource) {
         if (((GsmCellLocation)mCellLoc).getLac() >= 0 &&
                 ((GsmCellLocation)mCellLoc).getCid() >= 0) {
-            if (DBG) log("getCellLocation(): X good mCellLoc=" + mCellLoc);
+            if (VDBG) log("getCellLocation(): X good mCellLoc=" + mCellLoc);
             return mCellLoc;
         } else {
             List<CellInfo> result = getAllCellInfo(workSource);
@@ -3444,7 +3467,7 @@
                         cellLocOther.setLacAndCid(cellIdentityGsm.getLac(),
                                 cellIdentityGsm.getCid());
                         cellLocOther.setPsc(cellIdentityGsm.getPsc());
-                        if (DBG) log("getCellLocation(): X ret GSM info=" + cellLocOther);
+                        if (VDBG) log("getCellLocation(): X ret GSM info=" + cellLocOther);
                         return cellLocOther;
                     } else if (ci instanceof CellInfoWcdma) {
                         CellInfoWcdma cellInfoWcdma = (CellInfoWcdma)ci;
@@ -3452,7 +3475,7 @@
                         cellLocOther.setLacAndCid(cellIdentityWcdma.getLac(),
                                 cellIdentityWcdma.getCid());
                         cellLocOther.setPsc(cellIdentityWcdma.getPsc());
-                        if (DBG) log("getCellLocation(): X ret WCDMA info=" + cellLocOther);
+                        if (VDBG) log("getCellLocation(): X ret WCDMA info=" + cellLocOther);
                         return cellLocOther;
                     } else if ((ci instanceof CellInfoLte) &&
                             ((cellLocOther.getLac() < 0) || (cellLocOther.getCid() < 0))) {
@@ -3464,18 +3487,18 @@
                             cellLocOther.setLacAndCid(cellIdentityLte.getTac(),
                                     cellIdentityLte.getCi());
                             cellLocOther.setPsc(0);
-                            if (DBG) {
+                            if (VDBG) {
                                 log("getCellLocation(): possible LTE cellLocOther=" + cellLocOther);
                             }
                         }
                     }
                 }
-                if (DBG) {
+                if (VDBG) {
                     log("getCellLocation(): X ret best answer cellLocOther=" + cellLocOther);
                 }
                 return cellLocOther;
             } else {
-                if (DBG) {
+                if (VDBG) {
                     log("getCellLocation(): X empty mCellLoc and CellInfo mCellLoc=" + mCellLoc);
                 }
                 return mCellLoc;
@@ -3788,6 +3811,12 @@
         mTimeZoneLog.log(tmpLog);
         if (mSavedTimeZone != null) {
             setAndBroadcastNetworkSetTimeZone(mSavedTimeZone);
+        } else {
+            String iso = ((TelephonyManager) mPhone.getContext().getSystemService(
+                    Context.TELEPHONY_SERVICE)).getNetworkCountryIsoForPhone(mPhone.getPhoneId());
+            if (!TextUtils.isEmpty(iso)) {
+                updateTimeZoneByNetworkCountryCode(iso);
+            }
         }
     }
 
@@ -3840,7 +3869,7 @@
                 }
                 notificationId = PS_NOTIFICATION;
                 title = context.getText(com.android.internal.R.string.RestrictedOnDataTitle);
-                details = context.getText(com.android.internal.R.string.RestrictedOnDataContent);
+                details = context.getText(com.android.internal.R.string.RestrictedStateContent);
                 break;
             case PS_DISABLED:
                 notificationId = PS_NOTIFICATION;
@@ -3848,16 +3877,16 @@
             case CS_ENABLED:
                 title = context.getText(com.android.internal.R.string.RestrictedOnAllVoiceTitle);
                 details = context.getText(
-                        com.android.internal.R.string.RestrictedOnAllVoiceContent);
+                        com.android.internal.R.string.RestrictedStateContent);
                 break;
             case CS_NORMAL_ENABLED:
                 title = context.getText(com.android.internal.R.string.RestrictedOnNormalTitle);
-                details = context.getText(com.android.internal.R.string.RestrictedOnNormalContent);
+                details = context.getText(com.android.internal.R.string.RestrictedStateContent);
                 break;
             case CS_EMERGENCY_ENABLED:
                 title = context.getText(com.android.internal.R.string.RestrictedOnEmergencyTitle);
                 details = context.getText(
-                        com.android.internal.R.string.RestrictedOnEmergencyContent);
+                        com.android.internal.R.string.RestrictedStateContent);
                 break;
             case CS_DISABLED:
                 // do nothing and cancel the notification later
@@ -3893,6 +3922,7 @@
                         com.android.internal.R.color.system_notification_accent_color))
                 .setContentTitle(title)
                 .setContentText(details)
+                .setChannel(NotificationChannelController.CHANNEL_ID_ALERT)
                 .build();
 
         NotificationManager notificationManager = (NotificationManager)
@@ -4576,8 +4606,8 @@
         pw.println(" mRestrictedState=" + mRestrictedState);
         pw.println(" mPendingRadioPowerOffAfterDataOff=" + mPendingRadioPowerOffAfterDataOff);
         pw.println(" mPendingRadioPowerOffAfterDataOffTag=" + mPendingRadioPowerOffAfterDataOffTag);
-        pw.println(" mCellLoc=" + mCellLoc);
-        pw.println(" mNewCellLoc=" + mNewCellLoc);
+        pw.println(" mCellLoc=" + Rlog.pii(VDBG, mCellLoc));
+        pw.println(" mNewCellLoc=" + Rlog.pii(VDBG, mNewCellLoc));
         pw.println(" mLastCellInfoListTime=" + mLastCellInfoListTime);
         dumpCellInfoList(pw);
         pw.flush();
@@ -4660,7 +4690,6 @@
         ipw.println(" Radio power Log:");
         ipw.increaseIndent();
         mRadioPowerLog.dump(fd, ipw, args);
-        ipw.decreaseIndent();
 
         ipw.println(" Time Logs:");
         ipw.increaseIndent();
@@ -4936,11 +4965,43 @@
     protected int getCombinedRegState() {
         int regState = mSS.getVoiceRegState();
         int dataRegState = mSS.getDataRegState();
-        if ((regState == ServiceState.STATE_OUT_OF_SERVICE)
+        if ((regState == ServiceState.STATE_OUT_OF_SERVICE
+                || regState == ServiceState.STATE_POWER_OFF)
                 && (dataRegState == ServiceState.STATE_IN_SERVICE)) {
             log("getCombinedRegState: return STATE_IN_SERVICE as Data is in service");
             regState = dataRegState;
         }
         return regState;
     }
+
+    /**
+     * Update time zone by network country code, works on countries which only have one time zone.
+     * @param iso Country code from network MCC
+     */
+    private void updateTimeZoneByNetworkCountryCode(String iso) {
+        // Test both paths if ignore nitz is true
+        boolean testOneUniqueOffsetPath = SystemProperties.getBoolean(
+                TelephonyProperties.PROPERTY_IGNORE_NITZ, false)
+                && ((SystemClock.uptimeMillis() & 1) == 0);
+
+        List<String> uniqueZoneIds = TimeUtils.getTimeZoneIdsWithUniqueOffsets(iso);
+        if ((uniqueZoneIds.size() == 1) || testOneUniqueOffsetPath) {
+            String zoneId = uniqueZoneIds.get(0);
+            if (DBG) {
+                log("updateTimeZoneByNetworkCountryCode: no nitz but one TZ for iso-cc=" + iso
+                        + " with zone.getID=" + zoneId
+                        + " testOneUniqueOffsetPath=" + testOneUniqueOffsetPath);
+            }
+            mTimeZoneLog.log("updateTimeZoneByNetworkCountryCode: set time zone=" + zoneId
+                    + " iso=" + iso);
+            setAndBroadcastNetworkSetTimeZone(zoneId);
+        } else {
+            if (DBG) {
+                log("updateTimeZoneByNetworkCountryCode: there are " + uniqueZoneIds.size()
+                        + " unique offsets for iso-cc='" + iso
+                        + " testOneUniqueOffsetPath=" + testOneUniqueOffsetPath
+                        + "', do nothing");
+            }
+        }
+    }
 }
diff --git a/src/java/com/android/internal/telephony/SettingsObserver.java b/src/java/com/android/internal/telephony/SettingsObserver.java
new file mode 100644
index 0000000..2253c36
--- /dev/null
+++ b/src/java/com/android/internal/telephony/SettingsObserver.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.telephony.Rlog;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * The class to describe settings observer
+ */
+public class SettingsObserver extends ContentObserver {
+    private final Map<Uri, Integer> mUriEventMap;
+    private final Context mContext;
+    private final Handler mHandler;
+    private static final String TAG = "SettingsObserver";
+
+    public SettingsObserver(Context context, Handler handler) {
+        super(null);
+        mUriEventMap = new HashMap<>();
+        mContext = context;
+        mHandler = handler;
+    }
+
+    /**
+     * Start observing a content.
+     * @param uri Content URI
+     * @param what The event to fire if the content changes
+     */
+    public void observe(Uri uri, int what) {
+        mUriEventMap.put(uri, what);
+        final ContentResolver resolver = mContext.getContentResolver();
+        resolver.registerContentObserver(uri, false, this);
+    }
+
+    /**
+     * Stop observing a content.
+     */
+    public void unobserve() {
+        final ContentResolver resolver = mContext.getContentResolver();
+        resolver.unregisterContentObserver(this);
+    }
+
+    @Override
+    public void onChange(boolean selfChange) {
+        Rlog.e(TAG, "Should never be reached.");
+    }
+
+    @Override
+    public void onChange(boolean selfChange, Uri uri) {
+        final Integer what = mUriEventMap.get(uri);
+        if (what != null) {
+            mHandler.obtainMessage(what.intValue()).sendToTarget();
+        } else {
+            Rlog.e(TAG, "No matching event to send for URI=" + uri);
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/SmsNumberUtils.java b/src/java/com/android/internal/telephony/SmsNumberUtils.java
index b468e7c..bf548d6 100644
--- a/src/java/com/android/internal/telephony/SmsNumberUtils.java
+++ b/src/java/com/android/internal/telephony/SmsNumberUtils.java
@@ -16,21 +16,24 @@
 
 package com.android.internal.telephony;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-
 import android.content.Context;
-import android.os.Build;
-import android.text.TextUtils;
 import android.database.Cursor;
 import android.database.SQLException;
+import android.os.Binder;
+import android.os.Build;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneNumberUtils;
-import android.telephony.TelephonyManager;
 import android.telephony.Rlog;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
 
 import com.android.internal.telephony.HbpcdLookup.MccIdd;
 import com.android.internal.telephony.HbpcdLookup.MccLookup;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+
 
  /**
  * This class implements handle the MO SMS target address before sending.
@@ -528,10 +531,11 @@
      *  Filter the destination number if using VZW sim card.
      */
     public static String filterDestAddr(Phone phone, String destAddr) {
-        if (DBG) Rlog.d(TAG, "enter filterDestAddr. destAddr=\"" + destAddr + "\"" );
+        if (DBG) Rlog.d(TAG, "enter filterDestAddr. destAddr=\"" + Rlog.pii(TAG, destAddr) + "\"" );
 
         if (destAddr == null || !PhoneNumberUtils.isGlobalPhoneNumber(destAddr)) {
-            Rlog.w(TAG, "destAddr" + destAddr + " is not a global phone number! Nothing changed.");
+            Rlog.w(TAG, "destAddr" + Rlog.pii(TAG, destAddr) +
+                    " is not a global phone number! Nothing changed.");
             return destAddr;
         }
 
@@ -551,7 +555,8 @@
 
         if (DBG) {
             Rlog.d(TAG, "destAddr is " + ((result != null)?"formatted.":"not formatted."));
-            Rlog.d(TAG, "leave filterDestAddr, new destAddr=\"" + (result != null ? result : destAddr) + "\"" );
+            Rlog.d(TAG, "leave filterDestAddr, new destAddr=\"" + (result != null ? Rlog.pii(TAG,
+                    result) : Rlog.pii(TAG, destAddr)) + "\"");
         }
         return result != null ? result : destAddr;
     }
@@ -597,28 +602,24 @@
     }
 
     private static boolean needToConvert(Phone phone) {
-        boolean bNeedToConvert  = false;
-        String[] listArray = phone.getContext().getResources()
-                .getStringArray(com.android.internal.R.array
-                .config_sms_convert_destination_number_support);
-        if (listArray != null && listArray.length > 0) {
-            for (int i=0; i<listArray.length; i++) {
-                if (!TextUtils.isEmpty(listArray[i])) {
-                    String[] needToConvertArray = listArray[i].split(";");
-                    if (needToConvertArray != null && needToConvertArray.length > 0) {
-                        if (needToConvertArray.length == 1) {
-                            bNeedToConvert = "true".equalsIgnoreCase(needToConvertArray[0]);
-                        } else if (needToConvertArray.length == 2 &&
-                                !TextUtils.isEmpty(needToConvertArray[1]) &&
-                                compareGid1(phone, needToConvertArray[1])) {
-                            bNeedToConvert = "true".equalsIgnoreCase(needToConvertArray[0]);
-                            break;
-                        }
-                    }
+        // Calling package may not have READ_PHONE_STATE which is required for getConfig().
+        // Clear the calling identity so that it is called as self.
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            CarrierConfigManager configManager = (CarrierConfigManager)
+                    phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+            if (configManager != null) {
+                PersistableBundle bundle = configManager.getConfig();
+                if (bundle != null) {
+                    return bundle.getBoolean(CarrierConfigManager
+                            .KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL);
                 }
             }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
-        return bNeedToConvert;
+        // by default this value is false
+        return false;
     }
 
     private static boolean compareGid1(Phone phone, String serviceGid1) {
diff --git a/src/java/com/android/internal/telephony/SmsUsageMonitor.java b/src/java/com/android/internal/telephony/SmsUsageMonitor.java
index 73e9a42..402a5ef 100644
--- a/src/java/com/android/internal/telephony/SmsUsageMonitor.java
+++ b/src/java/com/android/internal/telephony/SmsUsageMonitor.java
@@ -29,8 +29,8 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.telephony.PhoneNumberUtils;
-import android.util.AtomicFile;
 import android.telephony.Rlog;
+import android.util.AtomicFile;
 import android.util.Xml;
 
 import com.android.internal.util.FastXmlSerializer;
@@ -48,10 +48,10 @@
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.regex.Pattern;
 
 /**
@@ -85,7 +85,7 @@
     static final int CATEGORY_STANDARD_SHORT_CODE = 2;
 
     /** Return value from {@link #checkDestination} for possible premium short codes. */
-    static final int CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE = 3;
+    public static final int CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE = 3;
 
     /** Return value from {@link #checkDestination} for premium short codes. */
     static final int CATEGORY_PREMIUM_SHORT_CODE = 4;
diff --git a/src/java/com/android/internal/telephony/SubscriptionController.java b/src/java/com/android/internal/telephony/SubscriptionController.java
index 93a3f8d..ff42b25 100644
--- a/src/java/com/android/internal/telephony/SubscriptionController.java
+++ b/src/java/com/android/internal/telephony/SubscriptionController.java
@@ -38,7 +38,7 @@
 import android.text.TextUtils;
 import android.text.format.Time;
 import android.util.Log;
-import java.util.Objects;
+
 import com.android.internal.telephony.IccCardConstants.State;
 
 import java.io.FileDescriptor;
@@ -46,11 +46,13 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -595,8 +597,6 @@
      */
     @Override
     public int getActiveSubInfoCount(String callingPackage) {
-        if (DBG) logd("[getActiveSubInfoCount]+");
-
         if (!canReadPhoneState(callingPackage, "getActiveSubInfoCount")) {
             return 0;
         }
@@ -607,10 +607,10 @@
             List<SubscriptionInfo> records = getActiveSubscriptionInfoList(
                     mContext.getOpPackageName());
             if (records == null) {
-                if (DBG) logd("[getActiveSubInfoCount] records null");
+                if (VDBG) logd("[getActiveSubInfoCount] records null");
                 return 0;
             }
-            if (DBG) logd("[getActiveSubInfoCount]- count: " + records.size());
+            if (VDBG) logd("[getActiveSubInfoCount]- count: " + records.size());
             return records.size();
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -739,10 +739,11 @@
                     do {
                         int subId = cursor.getInt(cursor.getColumnIndexOrThrow(
                                 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID));
-                        // If sSlotIndexToSubId already has a valid subId for a slotIndex/phoneId,
-                        // do not add another subId for same slotIndex/phoneId.
+                        // If sSlotIndexToSubId already has the same subId for a slotIndex/phoneId,
+                        // do not add it.
                         Integer currentSubId = sSlotIndexToSubId.get(slotIndex);
                         if (currentSubId == null
+                                || currentSubId != subId
                                 || !SubscriptionManager.isValidSubscriptionId(currentSubId)) {
                             // TODO While two subs active, if user deactivats first
                             // one, need to update the default subId with second one.
@@ -790,16 +791,15 @@
             }
 
             // Set Display name after sub id is set above so as to get valid simCarrierName
-            int[] subIds = getSubId(slotIndex);
-            if (subIds == null || subIds.length == 0) {
+            int subId = getSubIdUsingPhoneId(slotIndex);
+            if (!SubscriptionManager.isValidSubscriptionId(subId)) {
                 if (DBG) {
-                    logdl("[addSubInfoRecord]- getSubId failed subIds == null || length == 0 subIds="
-                            + subIds);
+                    logdl("[addSubInfoRecord]- getSubId failed invalid subId = " + subId);
                 }
                 return -1;
             }
             if (setDisplayName) {
-                String simCarrierName = mTelephonyManager.getSimOperatorName(subIds[0]);
+                String simCarrierName = mTelephonyManager.getSimOperatorName(subId);
                 String nameToSet;
 
                 if (!TextUtils.isEmpty(simCarrierName)) {
@@ -812,7 +812,7 @@
                 value.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
                 resolver.update(SubscriptionManager.CONTENT_URI, value,
                         SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID +
-                                "=" + Long.toString(subIds[0]), null);
+                                "=" + Long.toString(subId), null);
 
                 if (DBG) logdl("[addSubInfoRecord] sim name = " + nameToSet);
             }
@@ -839,11 +839,10 @@
     public boolean setPlmnSpn(int slotIndex, boolean showPlmn, String plmn, boolean showSpn,
                               String spn) {
         synchronized (mLock) {
-            int[] subIds = getSubId(slotIndex);
+            int subId = getSubIdUsingPhoneId(slotIndex);
             if (mContext.getPackageManager().resolveContentProvider(
                     SubscriptionManager.CONTENT_URI.getAuthority(), 0) == null ||
-                    subIds == null ||
-                    !SubscriptionManager.isValidSubscriptionId(subIds[0])) {
+                    !SubscriptionManager.isValidSubscriptionId(subId)) {
                 // No place to store this info. Notify registrants of the change anyway as they
                 // might retrieve the SPN/PLMN text from the SST sticky broadcast.
                 // TODO: This can be removed once SubscriptionController is not running on devices
@@ -867,9 +866,7 @@
             } else if (showSpn) {
                 carrierText = spn;
             }
-            for (int i = 0; i < subIds.length; i++) {
-                setCarrierText(carrierText, subIds[i]);
-            }
+            setCarrierText(carrierText, subId);
             return true;
         }
     }
@@ -1347,11 +1344,11 @@
     private void broadcastDefaultSmsSubIdChanged(int subId) {
         // Broadcast an Intent for default sms sub change
         if (DBG) logdl("[broadcastDefaultSmsSubIdChanged] subId=" + subId);
-        Intent intent = new Intent(Intent.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED);
+        Intent intent = new Intent(SubscriptionManager.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED);
         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
                 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
         intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
-        intent.putExtra(Intent.EXTRA_SUBSCRIPTION_INDEX, subId);
+        intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
@@ -1381,7 +1378,8 @@
         // Broadcast an Intent for default voice sub change
         if (DBG) logdl("[broadcastDefaultVoiceSubIdChanged] subId=" + subId);
         Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
-        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
+                | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
         intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
@@ -1464,7 +1462,8 @@
         // Broadcast an Intent for default data sub change
         if (DBG) logdl("[broadcastDefaultDataSubIdChanged] subId=" + subId);
         Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
-        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
+                | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
         intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
@@ -1490,7 +1489,8 @@
 
                 // Broadcast an Intent for default sub change
                 Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED);
-                intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+                intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
+                        | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
                 SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId, subId);
                 if (DBG) {
                     logdl("[setDefaultFallbackSubId] broadcast default subId changed phoneId=" +
@@ -1566,15 +1566,10 @@
         return subIds[0];
     }
 
-    public int[] getSubIdUsingSlotIndex(int slotIndex) {
-        return getSubId(slotIndex);
-    }
-
     public List<SubscriptionInfo> getSubInfoUsingSlotIndexWithCheck(int slotIndex,
                                                                     boolean needCheck,
                                                                     String callingPackage) {
         if (DBG) logd("[getSubInfoUsingSlotIndexWithCheck]+ slotIndex:" + slotIndex);
-
         if (!canReadPhoneState(callingPackage, "getSubInfoUsingSlotIndexWithCheck")) {
             return null;
         }
@@ -1642,7 +1637,7 @@
      */
     @Override
     public int[] getActiveSubIdList() {
-        Set<Entry<Integer, Integer>> simInfoSet = sSlotIndexToSubId.entrySet();
+        Set<Entry<Integer, Integer>> simInfoSet = new HashSet<>(sSlotIndexToSubId.entrySet());
 
         int[] subIdArr = new int[simInfoSet.size()];
         int i = 0;
diff --git a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
index 74b65fe..ad217c2 100644
--- a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
+++ b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
@@ -16,10 +16,8 @@
 
 package com.android.internal.telephony;
 
-import static android.Manifest.permission.READ_PHONE_STATE;
-
-import android.app.ActivityManagerNative;
-import android.app.IUserSwitchObserver;
+import android.app.ActivityManager;
+import android.app.UserSwitchObserver;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.ContentValues;
@@ -34,8 +32,6 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.os.UserManager;
 import android.preference.PreferenceManager;
 import android.provider.Settings;
 import android.telephony.CarrierConfigManager;
@@ -53,10 +49,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 
 /**
  *@hide
@@ -107,8 +100,6 @@
     private static int[] mInsertSimState = new int[PROJECT_SIM_NUM];
     private SubscriptionManager mSubscriptionManager = null;
     private IPackageManager mPackageManager;
-    private UserManager mUserManager;
-    private Map<Integer, Intent> rebroadcastIntentsOnUnlock = new HashMap<>();
 
     // The current foreground user ID.
     private int mCurrentlyActiveUserId;
@@ -121,11 +112,9 @@
         mPhone = phone;
         mSubscriptionManager = SubscriptionManager.from(mContext);
         mPackageManager = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
-        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
 
         IntentFilter intentFilter = new IntentFilter(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
         intentFilter.addAction(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED);
-        intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
         mContext.registerReceiver(sReceiver, intentFilter);
 
         mCarrierServiceBindHelper = new CarrierServiceBindHelper(mContext);
@@ -139,8 +128,7 @@
         // -Whenever we switch to a new user
         mCurrentlyActiveUserId = 0;
         try {
-            ActivityManagerNative.getDefault().registerUserSwitchObserver(
-                    new IUserSwitchObserver.Stub() {
+            ActivityManager.getService().registerUserSwitchObserver(new UserSwitchObserver() {
                 @Override
                 public void onUserSwitching(int newUserId, IRemoteCallback reply)
                         throws RemoteException {
@@ -156,18 +144,8 @@
                         }
                     }
                 }
-
-                @Override
-                public void onUserSwitchComplete(int newUserId) {
-                    // Ignore.
-                }
-
-                @Override
-                public void onForegroundProfileSwitch(int newProfileId) throws RemoteException {
-                    // Ignore.
-                }
             }, LOG_TAG);
-            mCurrentlyActiveUserId = ActivityManagerNative.getDefault().getCurrentUser().id;
+            mCurrentlyActiveUserId = ActivityManager.getService().getCurrentUser().id;
         } catch (RemoteException e) {
             logd("Couldn't get current user ID; guessing it's 0: " + e.getMessage());
         }
@@ -183,22 +161,6 @@
             String action = intent.getAction();
             logd("Action: " + action);
 
-            if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
-                // broadcast pending intents
-                Iterator iterator = rebroadcastIntentsOnUnlock.entrySet().iterator();
-                while (iterator.hasNext()) {
-                    Map.Entry pair = (Map.Entry) iterator.next();
-                    Intent i = (Intent)pair.getValue();
-                    iterator.remove();
-                    logd("Broadcasting intent ACTION_SIM_STATE_CHANGED for mCardIndex: " +
-                            pair.getKey());
-                    ActivityManagerNative.broadcastStickyIntent(i, READ_PHONE_STATE,
-                            UserHandle.USER_ALL);
-                }
-                logd("[Receiver]-");
-                return;
-            }
-
             if (!action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED) &&
                     !action.equals(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED)) {
                 return;
@@ -216,7 +178,6 @@
             logd("simStatus: " + simStatus);
 
             if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
-                rebroadcastIntentsOnUnlock.put(slotIndex, intent);
                 if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(simStatus)) {
                     sendMessage(obtainMessage(EVENT_SIM_ABSENT, slotIndex, -1));
                 } else if (IccCardConstants.INTENT_VALUE_ICC_UNKNOWN.equals(simStatus)) {
@@ -390,111 +351,105 @@
     }
 
     private void handleSimLoaded(int slotId) {
-        logd("handleSimStateLoadedInternal: slotId: " + slotId);
+        logd("handleSimLoaded: slotId: " + slotId);
 
         // The SIM should be loaded at this state, but it is possible in cases such as SIM being
         // removed or a refresh RESET that the IccRecords could be null. The right behavior is to
         // not broadcast the SIM loaded.
         IccRecords records = mPhone[slotId].getIccCard().getIccRecords();
         if (records == null) {  // Possibly a race condition.
-            logd("onRecieve: IccRecords null");
+            logd("handleSimLoaded: IccRecords null");
             return;
         }
         if (records.getIccId() == null) {
-            logd("onRecieve: IccID null");
+            logd("handleSimLoaded: IccID null");
             return;
         }
         mIccId[slotId] = records.getIccId();
 
         if (isAllIccIdQueryDone()) {
             updateSubscriptionInfoByIccId();
-        }
+            int[] subIds = mSubscriptionManager.getActiveSubscriptionIdList();
+            for (int subId : subIds) {
+                TelephonyManager tm = TelephonyManager.getDefault();
 
-        int subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
-        int[] subIds = SubscriptionController.getInstance().getSubId(slotId);
-        if (subIds != null) {   // Why an array?
-            subId = subIds[0];
-        }
+                String operator = tm.getSimOperatorNumeric(subId);
+                slotId = SubscriptionController.getInstance().getPhoneId(subId);
 
-        if (SubscriptionManager.isValidSubscriptionId(subId)) {
-            TelephonyManager tm = TelephonyManager.getDefault();
-
-            String operator = tm.getSimOperatorNumericForPhone(slotId);
-
-            if (!TextUtils.isEmpty(operator)) {
-                if (subId == SubscriptionController.getInstance().getDefaultSubId()) {
-                    MccTable.updateMccMncConfiguration(mContext, operator, false);
-                }
-                SubscriptionController.getInstance().setMccMnc(operator, subId);
-            } else {
-                logd("EVENT_RECORDS_LOADED Operator name is null");
-            }
-
-            String msisdn = tm.getLine1Number(subId);
-            ContentResolver contentResolver = mContext.getContentResolver();
-
-            if (msisdn != null) {
-                ContentValues number = new ContentValues(1);
-                number.put(SubscriptionManager.NUMBER, msisdn);
-                contentResolver.update(SubscriptionManager.CONTENT_URI, number,
-                        SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "="
-                        + Long.toString(subId), null);
-            }
-
-            SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
-            String nameToSet;
-            String simCarrierName = tm.getSimOperatorName(subId);
-            ContentValues name = new ContentValues(1);
-
-            if (subInfo != null && subInfo.getNameSource() !=
-                    SubscriptionManager.NAME_SOURCE_USER_INPUT) {
-                if (!TextUtils.isEmpty(simCarrierName)) {
-                    nameToSet = simCarrierName;
+                if (!TextUtils.isEmpty(operator)) {
+                    if (subId == SubscriptionController.getInstance().getDefaultSubId()) {
+                        MccTable.updateMccMncConfiguration(mContext, operator, false);
+                    }
+                    SubscriptionController.getInstance().setMccMnc(operator, subId);
                 } else {
-                    nameToSet = "CARD " + Integer.toString(slotId + 1);
+                    logd("EVENT_RECORDS_LOADED Operator name is null");
                 }
-                name.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
-                logd("sim name = " + nameToSet);
-                contentResolver.update(SubscriptionManager.CONTENT_URI, name,
-                        SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID
-                        + "=" + Long.toString(subId), null);
+
+                String msisdn = tm.getLine1Number(subId);
+                ContentResolver contentResolver = mContext.getContentResolver();
+
+                if (msisdn != null) {
+                    ContentValues number = new ContentValues(1);
+                    number.put(SubscriptionManager.NUMBER, msisdn);
+                    contentResolver.update(SubscriptionManager.CONTENT_URI, number,
+                            SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "="
+                                    + Long.toString(subId), null);
+                }
+
+                SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
+                String nameToSet;
+                String simCarrierName = tm.getSimOperatorName(subId);
+                ContentValues name = new ContentValues(1);
+
+                if (subInfo != null && subInfo.getNameSource() !=
+                        SubscriptionManager.NAME_SOURCE_USER_INPUT) {
+                    if (!TextUtils.isEmpty(simCarrierName)) {
+                        nameToSet = simCarrierName;
+                    } else {
+                        nameToSet = "CARD " + Integer.toString(slotId + 1);
+                    }
+                    name.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
+                    logd("sim name = " + nameToSet);
+                    contentResolver.update(SubscriptionManager.CONTENT_URI, name,
+                            SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID
+                                    + "=" + Long.toString(subId), null);
+                }
+
+                /* Update preferred network type and network selection mode on SIM change.
+                 * Storing last subId in SharedPreference for now to detect SIM change. */
+                SharedPreferences sp =
+                        PreferenceManager.getDefaultSharedPreferences(mContext);
+                int storedSubId = sp.getInt(CURR_SUBID + slotId, -1);
+
+                if (storedSubId != subId) {
+                    int networkType = RILConstants.PREFERRED_NETWORK_MODE;
+
+                    // Set the modem network mode
+                    mPhone[slotId].setPreferredNetworkType(networkType, null);
+                    Settings.Global.putInt(mPhone[slotId].getContext().getContentResolver(),
+                            Settings.Global.PREFERRED_NETWORK_MODE + subId,
+                            networkType);
+
+                    // Only support automatic selection mode on SIM change.
+                    mPhone[slotId].getNetworkSelectionMode(
+                            obtainMessage(EVENT_GET_NETWORK_SELECTION_MODE_DONE,
+                                    new Integer(slotId)));
+
+                    // Update stored subId
+                    SharedPreferences.Editor editor = sp.edit();
+                    editor.putInt(CURR_SUBID + slotId, subId);
+                    editor.apply();
+                }
+
+                // Update set of enabled carrier apps now that the privilege rules may have changed.
+                CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(),
+                        mPackageManager, TelephonyManager.getDefault(),
+                        mContext.getContentResolver(), mCurrentlyActiveUserId);
+
+                broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_LOADED, null);
+                updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_LOADED);
             }
-
-            /* Update preferred network type and network selection mode on SIM change.
-             * Storing last subId in SharedPreference for now to detect SIM change. */
-            SharedPreferences sp =
-                    PreferenceManager.getDefaultSharedPreferences(mContext);
-            int storedSubId = sp.getInt(CURR_SUBID + slotId, -1);
-
-            if (storedSubId != subId) {
-                int networkType = RILConstants.PREFERRED_NETWORK_MODE;
-
-                // Set the modem network mode
-                mPhone[slotId].setPreferredNetworkType(networkType, null);
-                Settings.Global.putInt(mPhone[slotId].getContext().getContentResolver(),
-                        Settings.Global.PREFERRED_NETWORK_MODE + subId,
-                        networkType);
-
-                // Only support automatic selection mode on SIM change.
-                mPhone[slotId].getNetworkSelectionMode(
-                        obtainMessage(EVENT_GET_NETWORK_SELECTION_MODE_DONE, new Integer(slotId)));
-
-                // Update stored subId
-                SharedPreferences.Editor editor = sp.edit();
-                editor.putInt(CURR_SUBID + slotId, subId);
-                editor.apply();
-            }
-        } else {
-            logd("Invalid subId, could not update ContentResolver");
         }
-
-        // Update set of enabled carrier apps now that the privilege rules may have changed.
-        CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(),
-                mPackageManager, TelephonyManager.getDefault(), mContext.getContentResolver(),
-                mCurrentlyActiveUserId);
-
-        broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_LOADED, null);
-        updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_LOADED);
     }
 
     private void updateCarrierServices(int slotId, String simState) {
@@ -533,8 +488,6 @@
     synchronized private void updateSubscriptionInfoByIccId() {
         logd("updateSubscriptionInfoByIccId:+ Start");
 
-        mSubscriptionManager.clearSubscriptionInfo();
-
         for (int i = 0; i < PROJECT_SIM_NUM; i++) {
             mInsertSimState[i] = SIM_NOT_CHANGE;
         }
@@ -548,6 +501,13 @@
         }
         logd("insertedSimCount = " + insertedSimCount);
 
+        // We only clear the slot-to-sub map when one/some SIM was removed. Note this is a
+        // workaround for some race conditions that the empty map was accessed while we are
+        // rebuilding the map.
+        if (SubscriptionController.getInstance().getActiveSubIdList().length > insertedSimCount) {
+            SubscriptionController.getInstance().clearSubInfo();
+        }
+
         int index = 0;
         for (int i = 0; i < PROJECT_SIM_NUM; i++) {
             if (mInsertSimState[i] == SIM_NOT_INSERT) {
@@ -699,8 +659,7 @@
         SubscriptionManager.putPhoneIdAndSubIdExtra(i, slotId);
         logd("Broadcasting intent ACTION_SIM_STATE_CHANGED " + state + " reason " + reason +
              " for mCardIndex: " + slotId);
-        ActivityManagerNative.broadcastStickyIntent(i, READ_PHONE_STATE, UserHandle.USER_ALL);
-        rebroadcastIntentsOnUnlock.put(slotId, i);
+        IntentBroadcaster.getInstance().broadcastStickyIntent(i, slotId);
     }
 
     public void dispose() {
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
index 1ae1ee6..193d29e 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -133,6 +133,13 @@
         return new ImsExternalCallTracker(imsPhone);
     }
 
+    /**
+     * Create an AppSmsManager for per-app SMS message.
+     */
+    public AppSmsManager makeAppSmsManager(Context context) {
+        return new AppSmsManager(context);
+    }
+
     public DeviceStateMonitor makeDeviceStateMonitor(Phone phone) {
         return new DeviceStateMonitor(phone);
     }
diff --git a/src/java/com/android/internal/telephony/TelephonyTester.java b/src/java/com/android/internal/telephony/TelephonyTester.java
index 3de356f..3608c20 100644
--- a/src/java/com/android/internal/telephony/TelephonyTester.java
+++ b/src/java/com/android/internal/telephony/TelephonyTester.java
@@ -20,11 +20,9 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.SharedPreferences;
 import android.net.Uri;
 import android.os.BadParcelableException;
 import android.os.Build;
-import android.preference.PreferenceManager;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
 
@@ -33,10 +31,10 @@
 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;
 import com.android.internal.telephony.imsphone.ImsPhoneCall;
-import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
 import com.android.internal.telephony.test.TestConferenceEventPackageParser;
 
 import java.io.File;
@@ -83,6 +81,17 @@
     private static final String ACTION_TEST_HANDOVER_FAIL =
             "com.android.internal.telephony.TestHandoverFail";
 
+    /**
+     * Test-only intent used to trigger signalling of a
+     * {@link com.android.internal.telephony.gsm.SuppServiceNotification} to the {@link ImsPhone}.
+     * Use {@link #EXTRA_CODE} to specify the
+     * {@link com.android.internal.telephony.gsm.SuppServiceNotification#code}.
+     */
+    private static final String ACTION_TEST_SUPP_SRVC_NOTIFICATION =
+            "com.android.internal.telephony.TestSuppSrvcNotification";
+
+    private static final String EXTRA_CODE = "code";
+
     private static List<ImsExternalCallState> mImsExternalCallStates = null;
 
     private Phone mPhone;
@@ -111,6 +120,9 @@
                 } else if (action.equals(ACTION_TEST_HANDOVER_FAIL)) {
                     log("handle handover fail test intent");
                     handleHandoverFailedIntent();
+                } else if (action.equals(ACTION_TEST_SUPP_SRVC_NOTIFICATION)) {
+                    log("handle supp service notification test intent");
+                    sendTestSuppServiceNotification(intent);
                 } else {
                     if (DBG) log("onReceive: unknown action=" + action);
                 }
@@ -137,6 +149,7 @@
                 filter.addAction(ACTION_TEST_CONFERENCE_EVENT_PACKAGE);
                 filter.addAction(ACTION_TEST_DIALOG_EVENT_PACKAGE);
                 filter.addAction(ACTION_TEST_HANDOVER_FAIL);
+                filter.addAction(ACTION_TEST_SUPP_SRVC_NOTIFICATION);
                 mImsExternalCallStates = new ArrayList<ImsExternalCallState>();
             }
 
@@ -251,4 +264,18 @@
             mImsExternalCallStates.add(state);
         }
     }
+
+    private void sendTestSuppServiceNotification(Intent intent) {
+        if (intent.hasExtra(EXTRA_CODE)) {
+            int code = intent.getIntExtra(EXTRA_CODE, -1);
+            ImsPhone imsPhone = (ImsPhone) mPhone;
+            if (imsPhone == null) {
+                return;
+            }
+            log("Test supp service notification:" + code);
+            SuppServiceNotification suppServiceNotification = new SuppServiceNotification();
+            suppServiceNotification.code = code;
+            imsPhone.notifySuppSvcNotification(suppServiceNotification);
+        }
+    }
 }
diff --git a/src/java/com/android/internal/telephony/UiccSmsController.java b/src/java/com/android/internal/telephony/UiccSmsController.java
index 6844dd3..e6cb972 100755
--- a/src/java/com/android/internal/telephony/UiccSmsController.java
+++ b/src/java/com/android/internal/telephony/UiccSmsController.java
@@ -42,16 +42,20 @@
 public class UiccSmsController extends ISms.Stub {
     static final String LOG_TAG = "RIL_UiccSmsController";
 
-    protected Phone[] mPhone;
-
-    protected UiccSmsController(Phone[] phone){
-        mPhone = phone;
-
+    protected UiccSmsController() {
         if (ServiceManager.getService("isms") == null) {
             ServiceManager.addService("isms", this);
         }
     }
 
+    private Phone getPhone(int subId) {
+        Phone phone = PhoneFactory.getPhone(SubscriptionManager.getPhoneId(subId));
+        if (phone == null) {
+            phone = PhoneFactory.getDefaultPhone();
+        }
+        return phone;
+    }
+
     @Override
     public boolean
     updateMessageOnIccEfForSubscriber(int subId, String callingPackage, int index, int status,
@@ -320,45 +324,26 @@
     }
 
     /**
-     * get sms interface manager object based on subscription.
-     **/
+     * Get sms interface manager object based on subscription.
+     * @return ICC SMS manager
+     */
     private @Nullable IccSmsInterfaceManager getIccSmsInterfaceManager(int subId) {
-        if (!isActiveSubId(subId)) {
-            Rlog.e(LOG_TAG, "Subscription " + subId + " is inactive.");
-            return null;
-        }
-
-        int phoneId = SubscriptionController.getInstance().getPhoneId(subId) ;
-        //Fixme: for multi-subscription case
-        if (!SubscriptionManager.isValidPhoneId(phoneId)
-                || phoneId == SubscriptionManager.DEFAULT_PHONE_INDEX) {
-            phoneId = 0;
-        }
-
-        try {
-            return (IccSmsInterfaceManager)
-                ((Phone)mPhone[(int)phoneId]).getIccSmsInterfaceManager();
-        } catch (NullPointerException e) {
-            Rlog.e(LOG_TAG, "Exception is :"+e.toString()+" For subscription :"+subId );
-            e.printStackTrace();
-            return null;
-        } catch (ArrayIndexOutOfBoundsException e) {
-            Rlog.e(LOG_TAG, "Exception is :"+e.toString()+" For subscription :"+subId );
-            e.printStackTrace();
-            return null;
-        }
+        return getPhone(subId).getIccSmsInterfaceManager();
     }
 
     /**
-       Gets User preferred SMS subscription */
+     * Get User preferred SMS subscription
+     * @return User preferred SMS subscription
+     */
     @Override
     public int getPreferredSmsSubscription() {
         return SubscriptionController.getInstance().getDefaultSmsSubId();
     }
 
     /**
-     * Get SMS prompt property,  enabled or not
-     **/
+     * Get SMS prompt property enabled or not
+     * @return True if SMS prompt is enabled.
+     */
     @Override
     public boolean isSMSPromptEnabled() {
         return PhoneFactory.isSMSPromptEnabled();
@@ -392,11 +377,9 @@
         }
     }
 
-    /*
-     * @return true if the subId is active.
-     */
-    private boolean isActiveSubId(int subId) {
-        return SubscriptionController.getInstance().isActiveSubId(subId);
+    @Override
+    public String createAppSpecificSmsToken(int subId, String callingPkg, PendingIntent intent) {
+        return getPhone(subId).getAppSmsManager().createAppSpecificSmsToken(callingPkg, intent);
     }
 
     private void sendErrorInPendingIntent(@Nullable PendingIntent intent, int errorCode) {
diff --git a/src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java b/src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java
index 2daf232..1294762 100644
--- a/src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java
+++ b/src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.provider.VoicemailContract;
 import android.telecom.PhoneAccountHandle;
+import android.telephony.PhoneNumberUtils;
 import android.telephony.SmsMessage;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -29,16 +30,37 @@
 import android.util.ArrayMap;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.VisualVoicemailSmsParser.WrappedMessageData;
 
+import java.nio.ByteBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.CharsetDecoder;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.regex.Pattern;
 
+/**
+ * Filters SMS to {@link android.telephony.VisualVoicemailService}, based on the config from {@link
+ * VisualVoicemailSmsFilterSettings}. The SMS is sent to telephony service which will do the actual
+ * dispatching.
+ */
 public class VisualVoicemailSmsFilter {
 
+    /**
+     * Interface to convert subIds so the logic can be replaced in tests.
+     */
+    @VisibleForTesting
+    public interface PhoneAccountHandleConverter {
+
+        /**
+         * Convert the subId to a {@link PhoneAccountHandle}
+         */
+        PhoneAccountHandle fromSubId(int subId);
+    }
+
     private static final String TAG = "VvmSmsFilter";
 
     private static final String TELEPHONY_SERVICE_PACKAGE = "com.android.phone";
@@ -49,9 +71,37 @@
 
     private static Map<String, List<Pattern>> sPatterns;
 
+    private static final PhoneAccountHandleConverter DEFAULT_PHONE_ACCOUNT_HANDLE_CONVERTER =
+            new PhoneAccountHandleConverter() {
+
+                @Override
+                public PhoneAccountHandle fromSubId(int subId) {
+                    if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+                        return null;
+                    }
+                    int phoneId = SubscriptionManager.getPhoneId(subId);
+                    if (phoneId == SubscriptionManager.INVALID_PHONE_INDEX) {
+                        return null;
+                    }
+                    return new PhoneAccountHandle(PSTN_CONNECTION_SERVICE_COMPONENT,
+                            PhoneFactory.getPhone(phoneId).getFullIccSerialNumber());
+                }
+            };
+
+    private static PhoneAccountHandleConverter sPhoneAccountHandleConverter =
+            DEFAULT_PHONE_ACCOUNT_HANDLE_CONVERTER;
+
+    /**
+     * Wrapper to combine multiple PDU into an SMS message
+     */
+    private static class FullMessage {
+        public SmsMessage firstMessage;
+        public String fullMessageBody;
+    }
+
     /**
      * Attempt to parse the incoming SMS as a visual voicemail SMS. If the parsing succeeded, A
-     * {@link VoicemailContract.ACTION_VOICEMAIL_SMS_RECEIVED} intent will be sent to telephony
+     * {@link VoicemailContract#ACTION_VOICEMAIL_SMS_RECEIVED} intent will be sent to telephony
      * service, and the SMS will be dropped.
      *
      * <p>The accepted format for a visual voicemail SMS is a generalization of the OMTP format:
@@ -59,8 +109,8 @@
      * <p>[clientPrefix]:[prefix]:([key]=[value];)*
      *
      * Additionally, if the SMS does not match the format, but matches the regex specified by the
-     * carrier in {@link com.android.internal.R.array.config_vvmSmsFilterRegexes}, the SMS will
-     * still be dropped and a {@link VoicemailContract.ACTION_VOICEMAIL_SMS_RECEIVED} will be sent.
+     * carrier in {@link com.android.internal.R.array#config_vvmSmsFilterRegexes}, the SMS will
+     * still be dropped and a {@link VoicemailContract#ACTION_VOICEMAIL_SMS_RECEIVED} will be sent.
      *
      * @return true if the SMS has been parsed to be a visual voicemail SMS and should be dropped
      */
@@ -69,28 +119,29 @@
         TelephonyManager telephonyManager =
                 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
 
-        VisualVoicemailSmsFilterSettings settings =
-                telephonyManager.getActiveVisualVoicemailSmsFilterSettings(subId);
+        VisualVoicemailSmsFilterSettings settings;
+        settings = telephonyManager.getActiveVisualVoicemailSmsFilterSettings(subId);
+
         if (settings == null) {
             return false;
         }
-        // TODO: filter base on originating number and destination port.
 
-        PhoneAccountHandle phoneAccountHandle = phoneAccountHandleFromSubId(context, subId);
+        PhoneAccountHandle phoneAccountHandle = sPhoneAccountHandleConverter.fromSubId(subId);
+
         if (phoneAccountHandle == null) {
             Log.e(TAG, "Unable to convert subId " + subId + " to PhoneAccountHandle");
             return false;
         }
 
-        String messageBody = getFullMessage(pdus, format);
+        FullMessage fullMessage = getFullMessage(pdus, format);
 
-        if(messageBody == null){
-            // Verizon WAP push SMS is not recognized by android, which has a ascii PDU.
+        if (fullMessage == null) {
+            // Carrier WAP push SMS is not recognized by android, which has a ascii PDU.
             // Attempt to parse it.
             Log.i(TAG, "Unparsable SMS received");
             String asciiMessage = parseAsciiPduMessage(pdus);
             WrappedMessageData messageData = VisualVoicemailSmsParser
-                .parseAlternativeFormat(asciiMessage);
+                    .parseAlternativeFormat(asciiMessage);
             if (messageData != null) {
                 sendVvmSmsBroadcast(context, phoneAccountHandle, messageData, null);
             }
@@ -98,10 +149,34 @@
             // system decide. Usually because it is not parsable it will be dropped.
             return false;
         }
+
+        String messageBody = fullMessage.fullMessageBody;
         String clientPrefix = settings.clientPrefix;
         WrappedMessageData messageData = VisualVoicemailSmsParser
-            .parse(clientPrefix, messageBody);
+                .parse(clientPrefix, messageBody);
         if (messageData != null) {
+            if (settings.destinationPort
+                    == VisualVoicemailSmsFilterSettings.DESTINATION_PORT_DATA_SMS) {
+                if (destPort == -1) {
+                    // Non-data SMS is directed to the port "-1".
+                    Log.i(TAG, "SMS matching VVM format received but is not a DATA SMS");
+                    return false;
+                }
+            } else if (settings.destinationPort
+                    != VisualVoicemailSmsFilterSettings.DESTINATION_PORT_ANY) {
+                if (settings.destinationPort != destPort) {
+                    Log.i(TAG, "SMS matching VVM format received but is not directed to port "
+                            + settings.destinationPort);
+                    return false;
+                }
+            }
+
+            if (!settings.originatingNumbers.isEmpty()
+                    && !isSmsFromNumbers(fullMessage.firstMessage, settings.originatingNumbers)) {
+                Log.i(TAG, "SMS matching VVM format received but is not from originating numbers");
+                return false;
+            }
+
             sendVvmSmsBroadcast(context, phoneAccountHandle, messageData, null);
             return true;
         }
@@ -117,7 +192,7 @@
         for (Pattern pattern : patterns) {
             if (pattern.matcher(messageBody).matches()) {
                 Log.w(TAG, "Incoming SMS matches pattern " + pattern + " but has illegal format, "
-                    + "still dropping as VVM SMS");
+                        + "still dropping as VVM SMS");
                 sendVvmSmsBroadcast(context, phoneAccountHandle, null, messageBody);
                 return true;
             }
@@ -125,6 +200,19 @@
         return false;
     }
 
+    /**
+     * override how subId is converted to PhoneAccountHandle for tests
+     */
+    @VisibleForTesting
+    public static void setPhoneAccountHandleConverterForTest(
+            PhoneAccountHandleConverter converter) {
+        if (converter == null) {
+            sPhoneAccountHandleConverter = DEFAULT_PHONE_ACCOUNT_HANDLE_CONVERTER;
+        } else {
+            sPhoneAccountHandleConverter = converter;
+        }
+    }
+
     private static void buildPatternsMap(Context context) {
         if (sPatterns != null) {
             return;
@@ -132,7 +220,7 @@
         sPatterns = new ArrayMap<>();
         // TODO(twyen): build from CarrierConfig once public API can be updated.
         for (String entry : context.getResources()
-            .getStringArray(com.android.internal.R.array.config_vvmSmsFilterRegexes)) {
+                .getStringArray(com.android.internal.R.array.config_vvmSmsFilterRegexes)) {
             String[] mccMncList = entry.split(";")[0].split(",");
             Pattern pattern = Pattern.compile(entry.split(";")[1]);
 
@@ -146,7 +234,7 @@
     }
 
     private static void sendVvmSmsBroadcast(Context context, PhoneAccountHandle phoneAccountHandle,
-        @Nullable WrappedMessageData messageData, @Nullable String messageBody) {
+            @Nullable WrappedMessageData messageData, @Nullable String messageBody) {
         Log.i(TAG, "VVM SMS received");
         Intent intent = new Intent(VoicemailContract.ACTION_VOICEMAIL_SMS_RECEIVED);
         VisualVoicemailSms.Builder builder = new VisualVoicemailSms.Builder();
@@ -167,21 +255,39 @@
      * @return the message body of the SMS, or {@code null} if it can not be parsed.
      */
     @Nullable
-    private static String getFullMessage(byte[][] pdus, String format) {
+    private static FullMessage getFullMessage(byte[][] pdus, String format) {
+        FullMessage result = new FullMessage();
         StringBuilder builder = new StringBuilder();
+        CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
         for (byte pdu[] : pdus) {
             SmsMessage message = SmsMessage.createFromPdu(pdu, format);
-
             if (message == null) {
                 // The PDU is not recognized by android
                 return null;
             }
+            if (result.firstMessage == null) {
+                result.firstMessage = message;
+            }
             String body = message.getMessageBody();
+            if (body == null && message.getUserData() != null) {
+                // Attempt to interpret the user data as UTF-8. UTF-8 string over data SMS using
+                // 8BIT data coding scheme is our recommended way to send VVM SMS and is used in CTS
+                // Tests. The OMTP visual voicemail specification does not specify the SMS type and
+                // encoding.
+                ByteBuffer byteBuffer = ByteBuffer.wrap(message.getUserData());
+                try {
+                    body = decoder.decode(byteBuffer).toString();
+                } catch (CharacterCodingException e) {
+                    // User data is not decode-able as UTF-8. Ignoring.
+                    return null;
+                }
+            }
             if (body != null) {
                 builder.append(body);
             }
         }
-        return builder.toString();
+        result.fullMessageBody = builder.toString();
+        return result;
     }
 
     private static String parseAsciiPduMessage(byte[][] pdus) {
@@ -192,16 +298,17 @@
         return builder.toString();
     }
 
-    @Nullable
-    private static PhoneAccountHandle phoneAccountHandleFromSubId(Context context, int subId) {
-        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
-            return null;
+    private static boolean isSmsFromNumbers(SmsMessage message, List<String> numbers) {
+        if (message == null) {
+            Log.e(TAG, "Unable to create SmsMessage from PDU, cannot determine originating number");
+            return false;
         }
-        int phoneId = SubscriptionManager.getPhoneId(subId);
-        if (phoneId == SubscriptionManager.INVALID_PHONE_INDEX) {
-            return null;
+
+        for (String number : numbers) {
+            if (PhoneNumberUtils.compare(number, message.getOriginatingAddress())) {
+                return true;
+            }
         }
-        return new PhoneAccountHandle(PSTN_CONNECTION_SERVICE_COMPONENT,
-                PhoneFactory.getPhone(phoneId).getFullIccSerialNumber());
+        return false;
     }
 }
diff --git a/src/java/com/android/internal/telephony/cat/AppInterface.java b/src/java/com/android/internal/telephony/cat/AppInterface.java
index 6c4eacc..c78b7f8 100644
--- a/src/java/com/android/internal/telephony/cat/AppInterface.java
+++ b/src/java/com/android/internal/telephony/cat/AppInterface.java
@@ -30,11 +30,11 @@
      * proactive command, session end, ALPHA during STK CC arrive.
      */
     public static final String CAT_CMD_ACTION =
-                                    "android.intent.action.stk.command";
+                                    "com.android.internal.stk.command";
     public static final String CAT_SESSION_END_ACTION =
-                                    "android.intent.action.stk.session_end";
+                                    "com.android.internal.stk.session_end";
     public static final String CAT_ALPHA_NOTIFY_ACTION =
-                                    "android.intent.action.stk.alpha_notify";
+                                    "com.android.internal.stk.alpha_notify";
 
     //This is used to send ALPHA string from card to STK App.
     public static final String ALPHA_STRING = "alpha_string";
@@ -45,7 +45,7 @@
     public static final String CARD_STATUS = "card_status";
     //Intent's actions are broadcasted by Telephony once IccRefresh occurs.
     public static final String CAT_ICC_STATUS_CHANGE =
-                                    "android.intent.action.stk.icc_status_change";
+                                    "com.android.internal.stk.icc_status_change";
 
     // Permission required by STK command receiver
     public static final String STK_PERMISSION = "android.permission.RECEIVE_STK_COMMANDS";
diff --git a/src/java/com/android/internal/telephony/cat/CatService.java b/src/java/com/android/internal/telephony/cat/CatService.java
index 2f3ff3d..7e70212 100644
--- a/src/java/com/android/internal/telephony/cat/CatService.java
+++ b/src/java/com/android/internal/telephony/cat/CatService.java
@@ -881,8 +881,9 @@
         // This sends an intent with CARD_ABSENT (0 - false) /CARD_PRESENT (1 - true).
         intent.putExtra(AppInterface.CARD_STATUS, cardPresent);
         intent.setComponent(AppInterface.getDefaultSTKApplication());
+        intent.putExtra("SLOT_ID", mSlotId);
         CatLog.d(this, "Sending Card Status: "
-                + cardState + " " + "cardPresent: " + cardPresent);
+                + cardState + " " + "cardPresent: " + cardPresent +  "SLOT_ID: " +  mSlotId);
         mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION);
     }
 
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java b/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
index 7865bc4..3cd804d 100644
--- a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
+++ b/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
@@ -17,7 +17,6 @@
 package com.android.internal.telephony.dataconnection;
 
 import android.app.PendingIntent;
-import android.content.res.Resources;
 import android.net.ConnectivityManager;
 import android.net.NetworkCapabilities;
 import android.net.NetworkConfig;
@@ -536,8 +535,8 @@
         return mConnectionGeneration.get();
     }
 
-    public long getInterApnDelay(boolean failFastEnabled) {
-        return mRetryManager.getInterApnDelay(failFastEnabled || isFastRetryReason());
+    long getRetryAfterDisconnectDelay() {
+        return mRetryManager.getRetryAfterDisconnectDelay();
     }
 
     public static int apnIdForType(int networkType) {
@@ -726,7 +725,7 @@
                 l.dump(fd, pw, args);
             }
             pw.decreaseIndent();
-            pw.println("mRetryManager={" + mRetryManager.toString() + "}");
+            pw.println(mRetryManager);
         }
     }
 }
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java b/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java
index 0ce1991..ce8318d 100644
--- a/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java
+++ b/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java
@@ -24,6 +24,7 @@
 import android.telephony.ServiceState;
 import android.text.TextUtils;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.RILConstants;
@@ -33,6 +34,7 @@
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * This class represents a apn setting for create PDP link
@@ -42,6 +44,7 @@
     static final String LOG_TAG = "ApnSetting";
 
     private static final boolean DBG = false;
+    private static final boolean VDBG = false;
 
     static final String V2_FORMAT_REGEX = "^\\[ApnSettingV2\\]\\s*";
     static final String V3_FORMAT_REGEX = "^\\[ApnSettingV3\\]\\s*";
@@ -544,6 +547,116 @@
                 && mvnoMatchData.equals(other.mvnoMatchData);
     }
 
+    /**
+     * Compare two APN settings
+     *
+     * Note: This method does not compare 'id', 'bearer', 'bearerBitmask'. We only use this for
+     * determining if tearing a data call is needed when conditions change. See
+     * cleanUpConnectionsOnUpdatedApns in DcTracker.
+     *
+     * @param o the other object to compare
+     * @param isDataRoaming True if the device is on data roaming
+     * @return True if the two APN settings are same
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean equals(Object o, boolean isDataRoaming) {
+        if (!(o instanceof ApnSetting)) {
+            return false;
+        }
+
+        ApnSetting other = (ApnSetting) o;
+
+        return carrier.equals(other.carrier)
+                && numeric.equals(other.numeric)
+                && apn.equals(other.apn)
+                && proxy.equals(other.proxy)
+                && mmsc.equals(other.mmsc)
+                && mmsProxy.equals(other.mmsProxy)
+                && TextUtils.equals(mmsPort, other.mmsPort)
+                && port.equals(other.port)
+                && TextUtils.equals(user, other.user)
+                && TextUtils.equals(password, other.password)
+                && authType == other.authType
+                && Arrays.deepEquals(types, other.types)
+                && typesBitmap == other.typesBitmap
+                && (isDataRoaming || protocol.equals(other.protocol))
+                && (!isDataRoaming || roamingProtocol.equals(other.roamingProtocol))
+                && carrierEnabled == other.carrierEnabled
+                && profileId == other.profileId
+                && modemCognitive == other.modemCognitive
+                && maxConns == other.maxConns
+                && waitTime == other.waitTime
+                && maxConnsTime == other.maxConnsTime
+                && mtu == other.mtu
+                && mvnoType.equals(other.mvnoType)
+                && mvnoMatchData.equals(other.mvnoMatchData);
+    }
+
+    /**
+     * Check if neither mention DUN and are substantially similar
+     *
+     * @param other The other APN settings to compare
+     * @return True if two APN settings are similar
+     */
+    public boolean similar(ApnSetting other) {
+        return (!this.canHandleType(PhoneConstants.APN_TYPE_DUN)
+                && !other.canHandleType(PhoneConstants.APN_TYPE_DUN)
+                && Objects.equals(this.apn, other.apn)
+                && !typeSameAny(this, other)
+                && xorEquals(this.proxy, other.proxy)
+                && xorEquals(this.port, other.port)
+                && xorEquals(this.protocol, other.protocol)
+                && xorEquals(this.roamingProtocol, other.roamingProtocol)
+                && this.carrierEnabled == other.carrierEnabled
+                && this.bearerBitmask == other.bearerBitmask
+                && this.profileId == other.profileId
+                && Objects.equals(this.mvnoType, other.mvnoType)
+                && Objects.equals(this.mvnoMatchData, other.mvnoMatchData)
+                && xorEquals(this.mmsc, other.mmsc)
+                && xorEquals(this.mmsProxy, other.mmsProxy)
+                && xorEquals(this.mmsPort, other.mmsPort));
+    }
+
+    // check whether the types of two APN same (even only one type of each APN is same)
+    private boolean typeSameAny(ApnSetting first, ApnSetting second) {
+        if (VDBG) {
+            StringBuilder apnType1 = new StringBuilder(first.apn + ": ");
+            for (int index1 = 0; index1 < first.types.length; index1++) {
+                apnType1.append(first.types[index1]);
+                apnType1.append(",");
+            }
+
+            StringBuilder apnType2 = new StringBuilder(second.apn + ": ");
+            for (int index1 = 0; index1 < second.types.length; index1++) {
+                apnType2.append(second.types[index1]);
+                apnType2.append(",");
+            }
+            Rlog.d(LOG_TAG, "APN1: is " + apnType1);
+            Rlog.d(LOG_TAG, "APN2: is " + apnType2);
+        }
+
+        for (int index1 = 0; index1 < first.types.length; index1++) {
+            for (int index2 = 0; index2 < second.types.length; index2++) {
+                if (first.types[index1].equals(PhoneConstants.APN_TYPE_ALL)
+                        || second.types[index2].equals(PhoneConstants.APN_TYPE_ALL)
+                        || first.types[index1].equals(second.types[index2])) {
+                    if (VDBG) Rlog.d(LOG_TAG, "typeSameAny: return true");
+                    return true;
+                }
+            }
+        }
+
+        if (VDBG) Rlog.d(LOG_TAG, "typeSameAny: return false");
+        return false;
+    }
+
+    // equal or one is not specified
+    private boolean xorEquals(String first, String second) {
+        return (Objects.equals(first, second)
+                || TextUtils.isEmpty(first)
+                || TextUtils.isEmpty(second));
+    }
+
     // Helper function to convert APN string into a 32-bit bitmask.
     private static int getApnBitmask(String apn) {
         switch (apn) {
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
index 8662bd3..6f7a13b 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
@@ -36,9 +36,9 @@
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Pair;
-import android.util.Patterns;
 import android.util.TimeUtils;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.CallTracker;
 import com.android.internal.telephony.CarrierSignalAgent;
 import com.android.internal.telephony.CommandException;
@@ -467,9 +467,18 @@
 
         DataProfile dp = new DataProfile(mApnSetting, cp.mProfileId);
 
-        mPhone.mCi.setupDataCall(cp.mRilRat, dp,
-                mPhone.getServiceState().getDataRoamingFromRegistration(),
-                mPhone.getDataRoamingEnabled(), msg);
+        // We need to use the actual modem roaming state instead of the framework roaming state
+        // here. This flag is only passed down to ril_service for picking the correct protocol (for
+        // old modem backward compatibility).
+        boolean isModemRoaming = mPhone.getServiceState().getDataRoamingFromRegistration();
+
+        // Set this flag to true if the user turns on data roaming. Or if we override the roaming
+        // state in framework, we should set this flag to true as well so the modem will not reject
+        // the data call setup (because the modem actually thinks the device is roaming).
+        boolean allowRoaming = mPhone.getDataRoamingEnabled()
+                || (isModemRoaming && !mPhone.getServiceState().getDataRoaming());
+
+        mPhone.mCi.setupDataCall(cp.mRilRat, dp, isModemRoaming, allowRoaming, msg);
     }
 
     /**
@@ -491,21 +500,12 @@
                 discReason = RILConstants.DEACTIVATE_REASON_PDP_RESET;
             }
         }
-        if (mPhone.mCi.getRadioState().isOn()
-                || (mPhone.getServiceState().getRilDataRadioTechnology()
-                        == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN )) {
-            String str = "tearDownData radio is on, call deactivateDataCall";
-            if (DBG) log(str);
-            if (apnContext != null) apnContext.requestLog(str);
-            mPhone.mCi.deactivateDataCall(mCid, discReason,
-                    obtainMessage(EVENT_DEACTIVATE_DONE, mTag, 0, o));
-        } else {
-            String str = "tearDownData radio is off sendMessage EVENT_DEACTIVATE_DONE immediately";
-            if (DBG) log(str);
-            if (apnContext != null) apnContext.requestLog(str);
-            AsyncResult ar = new AsyncResult(o, null, null);
-            sendMessage(obtainMessage(EVENT_DEACTIVATE_DONE, mTag, 0, ar));
-        }
+
+        String str = "tearDownData. mCid=" + mCid + ", reason=" + discReason;
+        if (DBG) log(str);
+        if (apnContext != null) apnContext.requestLog(str);
+        mPhone.mCi.deactivateDataCall(mCid, discReason,
+                obtainMessage(EVENT_DEACTIVATE_DONE, mTag, 0, o));
     }
 
     private void notifyAllWithEvent(ApnContext alreadySent, int event, String reason) {
@@ -817,7 +817,7 @@
      *
      * This gets set once per connection setup and is based on conditions at that time.
      * We could theoretically have dynamic capabilities but now is not a good time to
-     * experiement with that.
+     * experiment with that.
      *
      * This flag overrides the APN-based restriction capability, restricting the network
      * based on both having a NetworkRequest with restricted AND needing a restricted
@@ -865,6 +865,7 @@
         result.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
 
         if (mApnSetting != null) {
+            ApnSetting securedDunApn = mDct.fetchDunApn();
             for (String type : mApnSetting.types) {
                 if (!mRestrictedNetworkOverride
                         && (mConnectionParams != null && mConnectionParams.mUnmeteredUseOnly)
@@ -881,6 +882,11 @@
                         result.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
                         result.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS);
                         result.addCapability(NetworkCapabilities.NET_CAPABILITY_IA);
+                        // check if this is the DUN apn as well as returned by fetchDunApn().
+                        // If yes, add DUN capability too.
+                        if (mApnSetting.equals(securedDunApn)) {
+                            result.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
+                        }
                         break;
                     }
                     case PhoneConstants.APN_TYPE_DEFAULT: {
@@ -896,7 +902,6 @@
                         break;
                     }
                     case PhoneConstants.APN_TYPE_DUN: {
-                        ApnSetting securedDunApn = mDct.fetchDunApn();
                         if (securedDunApn == null || securedDunApn.equals(mApnSetting)) {
                             result.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
                         }
@@ -976,10 +981,14 @@
         return result;
     }
 
-    private boolean isIpAddress(String address) {
+    /**
+     * @return {@code} true iff. {@code address} is a literal IPv4 or IPv6 address.
+     */
+    @VisibleForTesting
+    public static boolean isIpAddress(String address) {
         if (address == null) return false;
 
-        return Patterns.IP_ADDRESS.matcher(address).matches();
+        return InetAddress.isNumeric(address);
     }
 
     private DataCallResponse.SetupResult setLinkProperties(DataCallResponse response,
@@ -1049,7 +1058,7 @@
             mPhone.getServiceStateTracker().registerForDataRoamingOn(getHandler(),
                     DataConnection.EVENT_DATA_CONNECTION_ROAM_ON, null);
             mPhone.getServiceStateTracker().registerForDataRoamingOff(getHandler(),
-                    DataConnection.EVENT_DATA_CONNECTION_ROAM_OFF, null);
+                    DataConnection.EVENT_DATA_CONNECTION_ROAM_OFF, null, true);
 
             // Add ourselves to the list of data connections
             mDcController.addDc(DataConnection.this);
@@ -1455,8 +1464,8 @@
                                     + " result.isRestartRadioFail=" +
                                     result.mFailCause.isRestartRadioFail(mPhone.getContext(),
                                             mPhone.getSubId())
-                                    + " result.isPermanentFail=" +
-                                    mDct.isPermanentFail(result.mFailCause);
+                                    + " isPermanentFailure=" +
+                                    mDct.isPermanentFailure(result.mFailCause);
                             if (DBG) log(str);
                             if (cp.mApnContext != null) cp.mApnContext.requestLog(str);
 
@@ -2115,4 +2124,3 @@
         pw.flush();
     }
 }
-
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java b/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java
index e949acf..e7afdff 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java
@@ -74,6 +74,16 @@
         return mDataDisallowedReasonSet.contains(reason);
     }
 
+    /**
+     * Check if only one disallowed reason prevent data connection.
+     *
+     * @param reason The given reason to check
+     * @return True if the given reason is the only one that prevents data connection
+     */
+    public boolean containsOnly(DataDisallowedReasonType reason) {
+        return mDataDisallowedReasonSet.size() == 1 && contains(reason);
+    }
+
     boolean contains(DataAllowedReasonType reason) {
         return reason == mDataAllowedReason;
     }
@@ -88,7 +98,7 @@
     }
 
     // Disallowed reasons. There could be multiple reasons if data connection is not allowed.
-    enum DataDisallowedReasonType {
+    public enum DataDisallowedReasonType {
         // Soft failure reasons. Normally the reasons from users or policy settings.
         DATA_DISABLED(false),                   // Data is disabled by the user or policy.
         ROAMING_DISABLED(false),                // Data roaming is disabled by the user.
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcController.java b/src/java/com/android/internal/telephony/dataconnection/DcController.java
index b168756..291d6f5 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcController.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcController.java
@@ -275,7 +275,7 @@
                                             + failCause);
                                 }
                                 mDct.sendRestartRadio();
-                            } else if (mDct.isPermanentFail(failCause)) {
+                            } else if (mDct.isPermanentFailure(failCause)) {
                                 if (DBG) {
                                     log("onDataStateChanged: inactive, add to cleanup list. "
                                             + "failCause=" + failCause);
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcFailCause.java b/src/java/com/android/internal/telephony/dataconnection/DcFailCause.java
index f34076a..b3762e5 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcFailCause.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcFailCause.java
@@ -19,7 +19,9 @@
 import android.content.res.Resources;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
+
 import java.util.HashMap;
+import java.util.HashSet;
 
 /**
  * Returned as the reason for a connection failure as defined
@@ -105,8 +107,8 @@
     // specified in ril.h
     REGISTRATION_FAIL(-1),
     GPRS_REGISTRATION_FAIL(-2),
-    SIGNAL_LOST(-3),
-    PREF_RADIO_TECH_CHANGED(-4),            /* no retry */
+    SIGNAL_LOST(-3),                        /* no retry */
+    PREF_RADIO_TECH_CHANGED(-4),
     RADIO_POWER_OFF(-5),                    /* no retry */
     TETHERED_CALL_ACTIVE(-6),               /* no retry */
     ERROR_UNSPECIFIED(0xFFFF),
@@ -129,6 +131,12 @@
         }
     }
 
+    /**
+     * Map of subId -> set of data call setup permanent failure for the carrier.
+     */
+    private static final HashMap<Integer, HashSet<DcFailCause>> sPermanentFailureCache =
+            new HashMap<>();
+
     DcFailCause(int errorCode) {
         mErrorCode = errorCode;
     }
@@ -160,16 +168,62 @@
         return false;
     }
 
-    public boolean isPermanentFail() {
-        return (this == OPERATOR_BARRED) || (this == MISSING_UNKNOWN_APN) ||
-                (this == UNKNOWN_PDP_ADDRESS_TYPE) || (this == USER_AUTHENTICATION) ||
-                (this == ACTIVATION_REJECT_GGSN) || (this == SERVICE_OPTION_NOT_SUPPORTED) ||
-                (this == SERVICE_OPTION_NOT_SUBSCRIBED) || (this == NSAPI_IN_USE) ||
-                (this == ONLY_IPV4_ALLOWED) || (this == ONLY_IPV6_ALLOWED) ||
-                (this == PROTOCOL_ERRORS) ||
-                (this == RADIO_POWER_OFF) || (this == TETHERED_CALL_ACTIVE) ||
-                (this == RADIO_NOT_AVAILABLE) || (this == UNACCEPTABLE_NETWORK_PARAMETER) ||
-                (this == SIGNAL_LOST);
+    public boolean isPermanentFailure(Context context, int subId) {
+
+        synchronized (sPermanentFailureCache) {
+
+            HashSet<DcFailCause> permanentFailureSet = sPermanentFailureCache.get(subId);
+
+            // In case of cache miss, we need to look up the settings from carrier config.
+            if (permanentFailureSet == null) {
+                // Retrieve the permanent failure from carrier config
+                CarrierConfigManager configManager = (CarrierConfigManager)
+                        context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+                if (configManager != null) {
+                    PersistableBundle b = configManager.getConfigForSubId(subId);
+                    if (b != null) {
+                        String[] permanentFailureStrings = b.getStringArray(CarrierConfigManager.
+                                KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS);
+
+                        if (permanentFailureStrings != null) {
+                            permanentFailureSet = new HashSet<>();
+                            for (String failure : permanentFailureStrings) {
+                                permanentFailureSet.add(DcFailCause.valueOf(failure));
+                            }
+                        }
+                    }
+                }
+
+                // If we are not able to find the configuration from carrier config, use the default
+                // ones.
+                if (permanentFailureSet == null) {
+                    permanentFailureSet = new HashSet<DcFailCause>() {
+                        {
+                            add(OPERATOR_BARRED);
+                            add(MISSING_UNKNOWN_APN);
+                            add(UNKNOWN_PDP_ADDRESS_TYPE);
+                            add(USER_AUTHENTICATION);
+                            add(ACTIVATION_REJECT_GGSN);
+                            add(SERVICE_OPTION_NOT_SUPPORTED);
+                            add(SERVICE_OPTION_NOT_SUBSCRIBED);
+                            add(NSAPI_IN_USE);
+                            add(ONLY_IPV4_ALLOWED);
+                            add(ONLY_IPV6_ALLOWED);
+                            add(PROTOCOL_ERRORS);
+                            add(RADIO_POWER_OFF);
+                            add(TETHERED_CALL_ACTIVE);
+                            add(RADIO_NOT_AVAILABLE);
+                            add(UNACCEPTABLE_NETWORK_PARAMETER);
+                            add(SIGNAL_LOST);
+                        }
+                    };
+                }
+
+                sPermanentFailureCache.put(subId, permanentFailureSet);
+            }
+
+            return permanentFailureSet.contains(this);
+        }
     }
 
     public boolean isEventLoggable() {
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
index f00bfe6..7a95438 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -84,6 +84,7 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.RILConstants;
+import com.android.internal.telephony.SettingsObserver;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.dataconnection.DataConnectionReasons.DataAllowedReasonType;
 import com.android.internal.telephony.dataconnection.DataConnectionReasons.DataDisallowedReasonType;
@@ -100,7 +101,6 @@
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.Map.Entry;
-import java.util.Objects;
 import java.util.PriorityQueue;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
@@ -149,8 +149,6 @@
     private static final boolean DATA_STALL_SUSPECTED = true;
     private static final boolean DATA_STALL_NOT_SUSPECTED = false;
 
-    private String RADIO_RESET_PROPERTY = "gsm.radioreset";
-
     private static final String INTENT_RECONNECT_ALARM =
             "com.android.internal.telephony.data-reconnect";
     private static final String INTENT_RECONNECT_ALARM_EXTRA_TYPE = "reconnect_alarm_extra_type";
@@ -263,12 +261,8 @@
                             + " mIsWifiConnected=" + mIsWifiConnected);
                 }
             } else if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
-                CarrierConfigManager configMgr = (CarrierConfigManager)
-                        mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
-                if (configMgr != null) {
-                    PersistableBundle cfg = configMgr.getConfigForSubId(mPhone.getSubId());
-                    if (cfg != null) mAllowUserEditTetherApn =
-                            cfg.getBoolean(CarrierConfigManager.KEY_EDITABLE_TETHER_APN_BOOL);
+                if (mIccRecords.get() != null && mIccRecords.get().getRecordsLoaded()) {
+                    setDefaultDataRoamingEnabled();
                 }
             } else {
                 if (DBG) log("onReceive: Unknown action=" + action);
@@ -321,46 +315,6 @@
                 }
             };
 
-    private static class SettingsObserver extends ContentObserver {
-        final private HashMap<Uri, Integer> mUriEventMap;
-        final private Context mContext;
-        final private Handler mHandler;
-        final private static String TAG = "DcTracker.SettingsObserver";
-
-        SettingsObserver(Context context, Handler handler) {
-            super(null);
-            mUriEventMap = new HashMap<Uri, Integer>();
-            mContext = context;
-            mHandler = handler;
-        }
-
-        void observe(Uri uri, int what) {
-            mUriEventMap.put(uri, what);
-            final ContentResolver resolver = mContext.getContentResolver();
-            resolver.registerContentObserver(uri, false, this);
-        }
-
-        void unobserve() {
-            final ContentResolver resolver = mContext.getContentResolver();
-            resolver.unregisterContentObserver(this);
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            Rlog.e(TAG, "Should never be reached.");
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            final Integer what = mUriEventMap.get(uri);
-            if (what != null) {
-                mHandler.obtainMessage(what.intValue()).sendToTarget();
-            } else {
-                Rlog.e(TAG, "No matching event to send for URI=" + uri);
-            }
-        }
-    }
-
     private final SettingsObserver mSettingsObserver;
 
     private void registerSettingsObserver() {
@@ -578,11 +532,13 @@
     private boolean mMeteredApnDisabled = false;
 
     /**
-     * Whether carrier allow user edited tether APN. Updated by carrier config
-     * KEY_EDITABLE_TETHER_APN_BOOL
-     * If true, APN with dun type from database will be used, see fetchDunApn for details.
+     * int to remember whether has setDataProfiles and with roaming or not.
+     * 0: default, has never set data profile
+     * 1: has set data profile with home protocol
+     * 2: has set data profile with roaming protocol
+     * This is not needed once RIL command is updated to support both home and roaming protocol.
      */
-    private boolean mAllowUserEditTetherApn = false;
+    private int mSetDataProfileStatus = 0;
 
     /**
      * Handles changes to the APN db.
@@ -652,7 +608,6 @@
         filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
         filter.addAction(INTENT_DATA_STALL_ALARM);
         filter.addAction(INTENT_PROVISIONING_APN_ALARM);
-        filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
 
         // TODO - redundent with update call below?
         mDataEnabledSettings.setUserDataEnabled(getDataEnabled());
@@ -716,7 +671,7 @@
         mPhone.getServiceStateTracker().registerForDataRoamingOn(this,
                 DctConstants.EVENT_ROAMING_ON, null);
         mPhone.getServiceStateTracker().registerForDataRoamingOff(this,
-                DctConstants.EVENT_ROAMING_OFF, null);
+                DctConstants.EVENT_ROAMING_OFF, null, true);
         mPhone.getServiceStateTracker().registerForPsRestrictedEnabled(this,
                 DctConstants.EVENT_PS_RESTRICT_ENABLED, null);
         mPhone.getServiceStateTracker().registerForPsRestrictedDisabled(this,
@@ -895,11 +850,12 @@
                     if (dcac != null) {
                         final NetworkCapabilities netCaps = dcac.getNetworkCapabilitiesSync();
                         if (netCaps != null && !netCaps.hasCapability(NetworkCapabilities
-                                .NET_CAPABILITY_NOT_RESTRICTED)) {
+                                .NET_CAPABILITY_NOT_RESTRICTED) && !netCaps.hasCapability(
+                                NetworkCapabilities.NET_CAPABILITY_NOT_METERED)) {
                             if (DBG) {
-                                log("Tearing down restricted net:" + apnContext);
+                                log("Tearing down restricted metered net:" + apnContext);
                             }
-                            // Tearing down the restricted data call (metered or unmetered) when
+                            // Tearing down the restricted metered data call when
                             // conditions change. This will allow reestablishing a new unrestricted
                             // data connection.
                             apnContext.setReason(Phone.REASON_DATA_ENABLED);
@@ -1466,8 +1422,6 @@
         }
 
         for (ApnContext apnContext : mPrioritySortedApnContexts) {
-            ArrayList<ApnSetting> waitingApns = null;
-
             if (VDBG) log("setupDataOnConnectableApns: apnContext " + apnContext);
 
             if (apnContext.getState() == DctConstants.State.FAILED
@@ -1478,27 +1432,12 @@
                         mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
                     // RetryFailures.ONLY_ON_CHANGE - check if voice concurrency has changed
                     apnContext.releaseDataConnection(reason);
-                } else {
-                    // RetryFailures.ONLY_ON_CHANGE - check if the apns have changed
-                    int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
-                    ArrayList<ApnSetting> originalApns = apnContext.getWaitingApns();
-                    if (originalApns != null && originalApns.isEmpty() == false) {
-                        waitingApns = buildWaitingApns(apnContext.getApnType(), radioTech);
-                        if (originalApns.size() != waitingApns.size() ||
-                                originalApns.containsAll(waitingApns) == false) {
-                            apnContext.releaseDataConnection(reason);
-                        } else {
-                            continue;
-                        }
-                    } else {
-                        continue;
-                    }
                 }
             }
             if (apnContext.isConnectable()) {
                 log("isConnectable() call trySetupData");
                 apnContext.setReason(reason);
-                trySetupData(apnContext, waitingApns);
+                trySetupData(apnContext);
             }
         }
     }
@@ -1510,10 +1449,6 @@
     }
 
     private boolean trySetupData(ApnContext apnContext) {
-        return trySetupData(apnContext, null);
-    }
-
-    private boolean trySetupData(ApnContext apnContext, ArrayList<ApnSetting> waitingApns) {
 
         if (mPhone.getSimulatedRadioControl() != null) {
             // Assume data is connected on the simulator
@@ -1542,9 +1477,8 @@
             apnContext.setConcurrentVoiceAndDataAllowed(mPhone.getServiceStateTracker()
                     .isConcurrentVoiceAndDataAllowed());
             if (apnContext.getState() == DctConstants.State.IDLE) {
-                if (waitingApns == null) {
-                    waitingApns = buildWaitingApns(apnContext.getApnType(), radioTech);
-                }
+                ArrayList<ApnSetting> waitingApns =
+                        buildWaitingApns(apnContext.getApnType(), radioTech);
                 if (waitingApns.isEmpty()) {
                     notifyNoData(DcFailCause.MISSING_UNKNOWN_APN, apnContext);
                     notifyOffApnsOfAvailability(apnContext.getReason());
@@ -1781,7 +1715,12 @@
         apnContext.requestLog(str);
     }
 
-    ApnSetting fetchDunApn() {
+    /**
+     * Fetch dun apn
+     * @return ApnSetting to be used for dun
+     */
+    @VisibleForTesting
+    public ApnSetting fetchDunApn() {
         if (SystemProperties.getBoolean("net.tethering.noprovisioning", false)) {
             log("fetchDunApn: net.tethering.noprovisioning=true ret: null");
             return null;
@@ -1792,32 +1731,39 @@
         ArrayList<ApnSetting> dunCandidates = new ArrayList<ApnSetting>();
         ApnSetting retDunSetting = null;
 
-        // Places to look for tether APN in order: TETHER_DUN_APN setting, APN database if
-        // carrier allows it, and config_tether_apndata resource.
+        // Places to look for tether APN in order: TETHER_DUN_APN setting (to be deprecated soon),
+        // APN database, and config_tether_apndata resource (to be deprecated soon).
         String apnData = Settings.Global.getString(mResolver, Settings.Global.TETHER_DUN_APN);
         if (!TextUtils.isEmpty(apnData)) {
             dunCandidates.addAll(ApnSetting.arrayFromString(apnData));
             if (VDBG) log("fetchDunApn: dunCandidates from Setting: " + dunCandidates);
-        } else if (mAllowUserEditTetherApn) {
-            for (ApnSetting apn : mAllApnSettings) {
-                if (apn.canHandleType(PhoneConstants.APN_TYPE_DUN)) {
-                    dunCandidates.add(apn);
-                }
-            }
-            if (VDBG) log("fetchDunApn: dunCandidates from database: " + dunCandidates);
         }
-        // If TETHER_DUN_APN isn't set or
-        // mAllowUserEditTetherApn is true but APN database doesn't have dun APN,
+
+        // todo: remove this and config_tether_apndata after APNs are moved from overlay to apns xml
+        // If TETHER_DUN_APN isn't set or APN database doesn't have dun APN,
         // try the resource as last resort.
         if (dunCandidates.isEmpty()) {
             String[] apnArrayData = mPhone.getContext().getResources()
                 .getStringArray(R.array.config_tether_apndata);
-            for (String apnString : apnArrayData) {
-                ApnSetting apn = ApnSetting.fromString(apnString);
-                // apn may be null if apnString isn't valid or has error parsing
-                if (apn != null) dunCandidates.add(apn);
+            if (!ArrayUtils.isEmpty(apnArrayData)) {
+                for (String apnString : apnArrayData) {
+                    ApnSetting apn = ApnSetting.fromString(apnString);
+                    // apn may be null if apnString isn't valid or has error parsing
+                    if (apn != null) dunCandidates.add(apn);
+                }
+                if (VDBG) log("fetchDunApn: dunCandidates from resource: " + dunCandidates);
             }
-            if (VDBG) log("fetchDunApn: dunCandidates from resource: " + dunCandidates);
+        }
+
+        if (dunCandidates.isEmpty()) {
+            if (!ArrayUtils.isEmpty(mAllApnSettings)) {
+                for (ApnSetting apn : mAllApnSettings) {
+                    if (apn.canHandleType(PhoneConstants.APN_TYPE_DUN)) {
+                        dunCandidates.add(apn);
+                    }
+                }
+                if (VDBG) log("fetchDunApn: dunCandidates from database: " + dunCandidates);
+            }
         }
 
         for (ApnSetting dunSetting : dunCandidates) {
@@ -1891,8 +1837,8 @@
         return result;
     }
 
-    boolean isPermanentFail(DcFailCause dcFailCause) {
-        return (dcFailCause.isPermanentFail() &&
+    boolean isPermanentFailure(DcFailCause dcFailCause) {
+        return (dcFailCause.isPermanentFailure(mPhone.getContext(), mPhone.getSubId()) &&
                 (mAttached.get() == false || dcFailCause != DcFailCause.SIGNAL_LOST));
     }
 
@@ -2157,7 +2103,7 @@
             if (DBG) log("setInitialAttachApn: X selected Apn=" + initialAttachApnSetting);
 
             mPhone.mCi.setInitialAttachApn(new DataProfile(initialAttachApnSetting),
-                    mPhone.getServiceState().getDataRoaming(), null);
+                    mPhone.getServiceState().getDataRoamingFromRegistration(), null);
         }
     }
 
@@ -2179,7 +2125,7 @@
         if (DBG) log("onApnChanged: createAllApnList and cleanUpAllConnections");
         createAllApnList();
         setInitialAttachApn();
-        cleanUpConnectionsOnUpdatedApns(!isDisconnected);
+        cleanUpConnectionsOnUpdatedApns(!isDisconnected, Phone.REASON_APN_CHANGED);
 
         // FIXME: See bug 17426028 maybe no conditional is needed.
         if (mPhone.getSubId() == SubscriptionManager.getDefaultDataSubscriptionId()) {
@@ -2230,7 +2176,7 @@
     private boolean isOnlySingleDcAllowed(int rilRadioTech) {
         // Default single dc rats with no knowledge of carrier
         int[] singleDcRats = null;
-        // get the carrier specific value, if it exists, from CarrierConfigManager
+        // get the carrier specific value, if it exists, from CarrierConfigManager.
         // generally configManager and bundle should not be null, but if they are it should be okay
         // to leave singleDcRats null as well
         CarrierConfigManager configManager = (CarrierConfigManager)
@@ -2330,7 +2276,7 @@
     private void notifyNoData(DcFailCause lastFailCauseCode,
                               ApnContext apnContext) {
         if (DBG) log( "notifyNoData: type=" + apnContext.getApnType());
-        if (isPermanentFail(lastFailCauseCode)
+        if (isPermanentFailure(lastFailCauseCode)
             && (!apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DEFAULT))) {
             mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnContext.getApnType());
         }
@@ -2680,9 +2626,9 @@
     }
 
     /**
-     * Modify {@link android.provider.Settings.Global#DATA_ROAMING} value.
+     * Modify {@link android.provider.Settings.Global#DATA_ROAMING} value for user modification only
      */
-    public void setDataRoamingEnabled(boolean enabled) {
+    public void setDataRoamingEnabledByUser(boolean enabled) {
         final int phoneSubId = mPhone.getSubId();
         if (getDataRoamingEnabled() != enabled) {
             int roaming = enabled ? 1 : 0;
@@ -2690,6 +2636,7 @@
             // For single SIM phones, this is a per phone property.
             if (TelephonyManager.getDefault().getSimCount() == 1) {
                 Settings.Global.putInt(mResolver, Settings.Global.DATA_ROAMING, roaming);
+                setDataRoamingFromUserAction(true);
             } else {
                 Settings.Global.putInt(mResolver, Settings.Global.DATA_ROAMING +
                          phoneSubId, roaming);
@@ -2698,12 +2645,12 @@
             mSubscriptionManager.setDataRoaming(roaming, phoneSubId);
             // will trigger handleDataOnRoamingChange() through observer
             if (DBG) {
-                log("setDataRoamingEnabled: set phoneSubId=" + phoneSubId
+                log("setDataRoamingEnabledByUser: set phoneSubId=" + phoneSubId
                         + " isRoaming=" + enabled);
             }
         } else {
             if (DBG) {
-                log("setDataRoamingEnabled: unchanged phoneSubId=" + phoneSubId
+                log("setDataRoamingEnabledByUser: unchanged phoneSubId=" + phoneSubId
                         + " isRoaming=" + enabled);
              }
         }
@@ -2713,21 +2660,21 @@
      * Return current {@link android.provider.Settings.Global#DATA_ROAMING} value.
      */
     public boolean getDataRoamingEnabled() {
-        boolean isDataRoamingEnabled = "true".equalsIgnoreCase(SystemProperties.get(
-                "ro.com.android.dataroaming", "false"));
+        boolean isDataRoamingEnabled;
         final int phoneSubId = mPhone.getSubId();
 
         try {
             // For single SIM phones, this is a per phone property.
             if (TelephonyManager.getDefault().getSimCount() == 1) {
                 isDataRoamingEnabled = Settings.Global.getInt(mResolver,
-                        Settings.Global.DATA_ROAMING, isDataRoamingEnabled ? 1 : 0) != 0;
+                        Settings.Global.DATA_ROAMING, getDefaultDataRoamingEnabled() ? 1 : 0) != 0;
             } else {
                 isDataRoamingEnabled = TelephonyManager.getIntWithSubId(mResolver,
                         Settings.Global.DATA_ROAMING, phoneSubId) != 0;
             }
         } catch (SettingNotFoundException snfe) {
             if (DBG) log("getDataRoamingEnabled: SettingNofFoundException snfe=" + snfe);
+            isDataRoamingEnabled = getDefaultDataRoamingEnabled();
         }
         if (VDBG) {
             log("getDataRoamingEnabled: phoneSubId=" + phoneSubId
@@ -2736,6 +2683,70 @@
         return isDataRoamingEnabled;
     }
 
+    /**
+     * get default values for {@link Settings.Global#DATA_ROAMING}
+     * return {@code true} if either
+     * {@link CarrierConfigManager#KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL} or
+     * system property ro.com.android.dataroaming is set to true. otherwise return {@code false}
+     */
+    private boolean getDefaultDataRoamingEnabled() {
+        final CarrierConfigManager configMgr = (CarrierConfigManager)
+                mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        boolean isDataRoamingEnabled = "true".equalsIgnoreCase(SystemProperties.get(
+                "ro.com.android.dataroaming", "false"));
+        isDataRoamingEnabled |= configMgr.getConfigForSubId(mPhone.getSubId()).getBoolean(
+                CarrierConfigManager.KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL);
+        return isDataRoamingEnabled;
+    }
+
+    /**
+     * Set default value for {@link android.provider.Settings.Global#DATA_ROAMING}
+     * if the setting is not from user actions. default value is based on carrier config and system
+     * properties.
+     */
+    private void setDefaultDataRoamingEnabled() {
+        // For single SIM phones, this is a per phone property.
+        String setting = Settings.Global.DATA_ROAMING;
+        boolean useCarrierSpecificDefault = false;
+        if (TelephonyManager.getDefault().getSimCount() != 1) {
+            setting = setting + mPhone.getSubId();
+            try {
+                Settings.Global.getInt(mResolver, setting);
+            } catch (SettingNotFoundException ex) {
+                // For msim, update to carrier default if uninitialized.
+                useCarrierSpecificDefault = true;
+            }
+        } else if (!isDataRoamingFromUserAction()) {
+            // for single sim device, update to carrier default if user action is not set
+            useCarrierSpecificDefault = true;
+        }
+        if (useCarrierSpecificDefault) {
+            boolean defaultVal = getDefaultDataRoamingEnabled();
+            log("setDefaultDataRoamingEnabled: " + setting + "default value: " + defaultVal);
+            Settings.Global.putInt(mResolver, setting, defaultVal ? 1 : 0);
+            mSubscriptionManager.setDataRoaming(defaultVal ? 1 : 0, mPhone.getSubId());
+        }
+    }
+
+    private boolean isDataRoamingFromUserAction() {
+        final SharedPreferences sp = PreferenceManager
+                .getDefaultSharedPreferences(mPhone.getContext());
+        // since we don't want to unset user preference from system update, pass true as the default
+        // value if shared pref does not exist and set shared pref to false explicitly from factory
+        // reset.
+        if (!sp.contains(Phone.DATA_ROAMING_IS_USER_SETTING_KEY)
+                && Settings.Global.getInt(mResolver, Settings.Global.DEVICE_PROVISIONED, 0) == 0) {
+            sp.edit().putBoolean(Phone.DATA_ROAMING_IS_USER_SETTING_KEY, false).commit();
+        }
+        return sp.getBoolean(Phone.DATA_ROAMING_IS_USER_SETTING_KEY, true);
+    }
+
+    private void setDataRoamingFromUserAction(boolean isUserAction) {
+        final SharedPreferences.Editor sp = PreferenceManager
+                .getDefaultSharedPreferences(mPhone.getContext()).edit();
+        sp.putBoolean(Phone.DATA_ROAMING_IS_USER_SETTING_KEY, isUserAction).commit();
+    }
+
     // When the data roaming status changes from roaming to non-roaming.
     private void onDataRoamingOff() {
         if (DBG) log("onDataRoamingOff");
@@ -3032,7 +3043,7 @@
 
             // If the data call failure cause is a permanent failure, we mark the APN as permanent
             // failed.
-            if (isPermanentFail(cause)) {
+            if (isPermanentFailure(cause)) {
                 log("cause = " + cause + ", mark apn as permanent failed. apn = " + apn);
                 apnContext.markApnPermanentFailed(apn);
             }
@@ -3165,7 +3176,7 @@
             // we're not tying up the RIL command channel.
             // This also helps in any external dependency to turn off the context.
             if (DBG) log("onDisconnectDone: attached, ready and retry after disconnect");
-            long delay = apnContext.getInterApnDelay(mFailFast);
+            long delay = apnContext.getRetryAfterDisconnectDelay();
             if (delay > 0) {
                 // Data connection is in IDLE state, so when we reconnect later, we'll rebuild
                 // the waiting APN list, which will also reset/reconfigure the retry manager.
@@ -3290,7 +3301,6 @@
         if (DBG) log("setDataProfilesAsNeeded");
         if (mAllApnSettings != null && !mAllApnSettings.isEmpty()) {
             ArrayList<DataProfile> dps = new ArrayList<DataProfile>();
-            boolean isRoaming = mPhone.getServiceState().getDataRoaming();
             for (ApnSetting apn : mAllApnSettings) {
                 if (apn.modemCognitive) {
                     DataProfile dp = new DataProfile(apn);
@@ -3301,7 +3311,7 @@
             }
             if (dps.size() > 0) {
                 mPhone.mCi.setDataProfile(dps.toArray(new DataProfile[0]),
-                        mPhone.getServiceState().getDataRoaming(), null);
+                        mPhone.getServiceState().getDataRoamingFromRegistration(), null);
             }
         }
     }
@@ -3368,7 +3378,7 @@
             int j = i + 1;
             while (j < mAllApnSettings.size()) {
                 second = mAllApnSettings.get(j);
-                if (apnsSimilar(first, second)) {
+                if (first.similar(second)) {
                     ApnSetting newApn = mergeApns(first, second);
                     mAllApnSettings.set(i, newApn);
                     first = newApn;
@@ -3381,64 +3391,6 @@
         }
     }
 
-    //check whether the types of two APN same (even only one type of each APN is same)
-    private boolean apnTypeSameAny(ApnSetting first, ApnSetting second) {
-        if(VDBG) {
-            StringBuilder apnType1 = new StringBuilder(first.apn + ": ");
-            for(int index1 = 0; index1 < first.types.length; index1++) {
-                apnType1.append(first.types[index1]);
-                apnType1.append(",");
-            }
-
-            StringBuilder apnType2 = new StringBuilder(second.apn + ": ");
-            for(int index1 = 0; index1 < second.types.length; index1++) {
-                apnType2.append(second.types[index1]);
-                apnType2.append(",");
-            }
-            log("APN1: is " + apnType1);
-            log("APN2: is " + apnType2);
-        }
-
-        for(int index1 = 0; index1 < first.types.length; index1++) {
-            for(int index2 = 0; index2 < second.types.length; index2++) {
-                if(first.types[index1].equals(PhoneConstants.APN_TYPE_ALL) ||
-                        second.types[index2].equals(PhoneConstants.APN_TYPE_ALL) ||
-                        first.types[index1].equals(second.types[index2])) {
-                    if(VDBG)log("apnTypeSameAny: return true");
-                    return true;
-                }
-            }
-        }
-
-        if(VDBG)log("apnTypeSameAny: return false");
-        return false;
-    }
-
-    // Check if neither mention DUN and are substantially similar
-    private boolean apnsSimilar(ApnSetting first, ApnSetting second) {
-        return (first.canHandleType(PhoneConstants.APN_TYPE_DUN) == false &&
-                second.canHandleType(PhoneConstants.APN_TYPE_DUN) == false &&
-                Objects.equals(first.apn, second.apn) &&
-                !apnTypeSameAny(first, second) &&
-                xorEquals(first.proxy, second.proxy) &&
-                xorEquals(first.port, second.port) &&
-                first.carrierEnabled == second.carrierEnabled &&
-                first.bearerBitmask == second.bearerBitmask &&
-                first.profileId == second.profileId &&
-                Objects.equals(first.mvnoType, second.mvnoType) &&
-                Objects.equals(first.mvnoMatchData, second.mvnoMatchData) &&
-                xorEquals(first.mmsc, second.mmsc) &&
-                xorEquals(first.mmsProxy, second.mmsProxy) &&
-                xorEquals(first.mmsPort, second.mmsPort));
-    }
-
-    // equal or one is not specified
-    private boolean xorEquals(String first, String second) {
-        return (Objects.equals(first, second) ||
-                TextUtils.isEmpty(first) ||
-                TextUtils.isEmpty(second));
-    }
-
     private ApnSetting mergeApns(ApnSetting dest, ApnSetting src) {
         int id = dest.id;
         ArrayList<String> resultTypes = new ArrayList<String>();
@@ -3767,6 +3719,15 @@
                 break;
 
             case DctConstants.EVENT_DATA_RAT_CHANGED:
+                if (mPhone.getServiceState().getRilDataRadioTechnology()
+                        == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) {
+                    // unknown rat is an exception for data rat change. It's only received when out
+                    // of service and is not applicable for apn bearer bitmask. We should bypass the
+                    // check of waiting apn list and keep the data connection on, and no need to
+                    // setup a new one.
+                    break;
+                }
+                cleanUpConnectionsOnUpdatedApns(false, Phone.REASON_NW_TYPE_CHANGED);
                 //May new Network allow setupData, so try it here
                 setupDataOnConnectableApns(Phone.REASON_NW_TYPE_CHANGED,
                         RetryFailures.ONLY_ON_CHANGE);
@@ -4368,39 +4329,48 @@
         }
     }
 
-    private void cleanUpConnectionsOnUpdatedApns(boolean tearDown) {
+    private boolean containsAllApns(ArrayList<ApnSetting> oldApnList,
+                                    ArrayList<ApnSetting> newApnList) {
+        for (ApnSetting newApnSetting : newApnList) {
+            boolean canHandle = false;
+            for (ApnSetting oldApnSetting : oldApnList) {
+                // Make sure at least one of the APN from old list can cover the new APN
+                if (oldApnSetting.equals(newApnSetting,
+                        mPhone.getServiceState().getDataRoamingFromRegistration())) {
+                    canHandle = true;
+                    break;
+                }
+            }
+            if (!canHandle) return false;
+        }
+        return true;
+    }
+
+    private void cleanUpConnectionsOnUpdatedApns(boolean tearDown, String reason) {
         if (DBG) log("cleanUpConnectionsOnUpdatedApns: tearDown=" + tearDown);
-        if (mAllApnSettings.isEmpty()) {
+        if (mAllApnSettings != null && mAllApnSettings.isEmpty()) {
             cleanUpAllConnections(tearDown, Phone.REASON_APN_CHANGED);
         } else {
             for (ApnContext apnContext : mApnContexts.values()) {
-                if (VDBG) log("cleanUpConnectionsOnUpdatedApns for "+ apnContext);
-
-                boolean cleanUpApn = true;
                 ArrayList<ApnSetting> currentWaitingApns = apnContext.getWaitingApns();
-
-                if ((currentWaitingApns != null) && (!apnContext.isDisconnected())) {
-                    int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
-                    ArrayList<ApnSetting> waitingApns = buildWaitingApns(
-                            apnContext.getApnType(), radioTech);
-                    if (VDBG) log("new waitingApns:" + waitingApns);
-                    if (waitingApns.size() == currentWaitingApns.size()) {
-                        cleanUpApn = false;
-                        for (int i = 0; i < waitingApns.size(); i++) {
-                            if (!currentWaitingApns.get(i).equals(waitingApns.get(i))) {
-                                if (VDBG) log("new waiting apn is different at " + i);
-                                cleanUpApn = true;
-                                apnContext.setWaitingApns(waitingApns);
-                                break;
-                            }
-                        }
+                ArrayList<ApnSetting> waitingApns = buildWaitingApns(
+                        apnContext.getApnType(),
+                        mPhone.getServiceState().getRilDataRadioTechnology());
+                if (VDBG) log("new waitingApns:" + waitingApns);
+                if ((currentWaitingApns != null)
+                        && ((waitingApns.size() != currentWaitingApns.size())
+                        // Check if the existing waiting APN list can cover the newly built APN
+                        // list. If yes, then we don't need to tear down the existing data call.
+                        // TODO: We probably need to rebuild APN list when roaming status changes.
+                        || !containsAllApns(currentWaitingApns, waitingApns))) {
+                    if (VDBG) log("new waiting apn is different for " + apnContext);
+                    apnContext.setWaitingApns(waitingApns);
+                    if (!apnContext.isDisconnected()) {
+                        if (VDBG) log("cleanUpConnectionsOnUpdatedApns for " + apnContext);
+                        apnContext.setReason(reason);
+                        cleanUpConnection(true, apnContext);
                     }
                 }
-
-                if (cleanUpApn) {
-                    apnContext.setReason(Phone.REASON_APN_CHANGED);
-                    cleanUpConnection(true, apnContext);
-                }
             }
         }
 
@@ -4586,13 +4556,11 @@
         public static final int CLEANUP                 = 1;
         public static final int REREGISTER              = 2;
         public static final int RADIO_RESTART           = 3;
-        public static final int RADIO_RESTART_WITH_PROP = 4;
 
         private static boolean isAggressiveRecovery(int value) {
             return ((value == RecoveryAction.CLEANUP) ||
                     (value == RecoveryAction.REREGISTER) ||
-                    (value == RecoveryAction.RADIO_RESTART) ||
-                    (value == RecoveryAction.RADIO_RESTART_WITH_PROP));
+                    (value == RecoveryAction.RADIO_RESTART));
         }
     }
 
@@ -4638,23 +4606,6 @@
                 EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RADIO_RESTART,
                         mSentSinceLastRecv);
                 if (DBG) log("restarting radio");
-                putRecoveryAction(RecoveryAction.RADIO_RESTART_WITH_PROP);
-                restartRadio();
-                break;
-            case RecoveryAction.RADIO_RESTART_WITH_PROP:
-                // This is in case radio restart has not recovered the data.
-                // It will set an additional "gsm.radioreset" property to tell
-                // RIL or system to take further action.
-                // The implementation of hard reset recovery action is up to OEM product.
-                // Once RADIO_RESET property is consumed, it is expected to set back
-                // to false by RIL.
-                EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RADIO_RESTART_WITH_PROP, -1);
-                if (DBG) log("restarting radio with gsm.radioreset to true");
-                SystemProperties.set(RADIO_RESET_PROPERTY, "true");
-                // give 1 sec so property change can be notified.
-                try {
-                    Thread.sleep(1000);
-                } catch (InterruptedException e) {}
                 restartRadio();
                 putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST);
                 break;
@@ -4775,7 +4726,7 @@
             intent.putExtra(DATA_STALL_ALARM_TAG_EXTRA, mDataStallAlarmTag);
             mDataStallAlarmIntent = PendingIntent.getBroadcast(mPhone.getContext(), 0, intent,
                     PendingIntent.FLAG_UPDATE_CURRENT);
-            mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+            mAlarmManager.set(AlarmManager.ELAPSED_REALTIME,
                     SystemClock.elapsedRealtime() + delayInMs, mDataStallAlarmIntent);
         } else {
             if (VDBG_STALL) {
diff --git a/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java b/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
index 355629a..ad325ea 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
@@ -791,14 +791,14 @@
     processCode() throws CallStateException {
         try {
             if (isShortCode()) {
-                Rlog.d(LOG_TAG, "isShortCode");
+                Rlog.d(LOG_TAG, "processCode: isShortCode");
                 // These just get treated as USSD.
                 sendUssd(mDialingNumber);
             } else if (mDialingNumber != null) {
                 // We should have no dialing numbers here
                 throw new RuntimeException ("Invalid or Unsupported MMI Code");
             } else if (mSc != null && mSc.equals(SC_CLIP)) {
-                Rlog.d(LOG_TAG, "is CLIP");
+                Rlog.d(LOG_TAG, "processCode: is CLIP");
                 if (isInterrogate()) {
                     mPhone.mCi.queryCLIP(
                             obtainMessage(EVENT_QUERY_COMPLETE, this));
@@ -806,7 +806,7 @@
                     throw new RuntimeException ("Invalid or Unsupported MMI Code");
                 }
             } else if (mSc != null && mSc.equals(SC_CLIR)) {
-                Rlog.d(LOG_TAG, "is CLIR");
+                Rlog.d(LOG_TAG, "processCode: is CLIR");
                 if (isActivate()) {
                     mPhone.mCi.setCLIR(CommandsInterface.CLIR_INVOCATION,
                         obtainMessage(EVENT_SET_COMPLETE, this));
@@ -820,7 +820,7 @@
                     throw new RuntimeException ("Invalid or Unsupported MMI Code");
                 }
             } else if (isServiceCodeCallForwarding(mSc)) {
-                Rlog.d(LOG_TAG, "is CF");
+                Rlog.d(LOG_TAG, "processCode: is CF");
 
                 String dialingNumber = mSia;
                 int serviceClass = siToServiceClass(mSib);
@@ -866,7 +866,7 @@
                         ((cfAction == CommandsInterface.CF_ACTION_ENABLE) ||
                                 (cfAction == CommandsInterface.CF_ACTION_REGISTRATION)) ? 1 : 0;
 
-                    Rlog.d(LOG_TAG, "is CF setCallForward");
+                    Rlog.d(LOG_TAG, "processCode: is CF setCallForward");
                     mPhone.mCi.setCallForward(cfAction, reason, serviceClass,
                             dialingNumber, time, obtainMessage(
                                     EVENT_SET_CFF_COMPLETE,
@@ -957,7 +957,8 @@
                         // Sim is puk-locked
                         handlePasswordError(com.android.internal.R.string.needPuk);
                     } else if (mUiccApplication != null) {
-                        Rlog.d(LOG_TAG, "process mmi service code using UiccApp sc=" + mSc);
+                        Rlog.d(LOG_TAG,
+                                "processCode: process mmi service code using UiccApp sc=" + mSc);
 
                         // We have an app and the pre-checks are OK
                         if (mSc.equals(SC_PIN)) {
@@ -984,11 +985,13 @@
             } else if (mPoundString != null) {
                 sendUssd(mPoundString);
             } else {
+                Rlog.d(LOG_TAG, "processCode: Invalid or Unsupported MMI Code");
                 throw new RuntimeException ("Invalid or Unsupported MMI Code");
             }
         } catch (RuntimeException exc) {
             mState = State.FAILED;
             mMessage = mContext.getText(com.android.internal.R.string.mmiError);
+            Rlog.d(LOG_TAG, "processCode: RuntimeException=" + exc);
             mPhone.onMMIDone(this);
         }
     }
@@ -1014,7 +1017,8 @@
     public void
     onUssdFinished(String ussdMessage, boolean isUssdRequest) {
         if (mState == State.PENDING) {
-            if (ussdMessage == null) {
+            if (TextUtils.isEmpty(ussdMessage)) {
+                Rlog.d(LOG_TAG, "onUssdFinished: no network provided message; using default.");
                 mMessage = mContext.getText(com.android.internal.R.string.mmiComplete);
             } else {
                 mMessage = ussdMessage;
@@ -1024,7 +1028,7 @@
             if (!isUssdRequest) {
                 mState = State.COMPLETE;
             }
-
+            Rlog.d(LOG_TAG, "onUssdFinished: ussdMessage=" + ussdMessage);
             mPhone.onMMIDone(this);
         }
     }
@@ -1040,7 +1044,7 @@
         if (mState == State.PENDING) {
             mState = State.FAILED;
             mMessage = mContext.getText(com.android.internal.R.string.mmiError);
-
+            Rlog.d(LOG_TAG, "onUssdFinishedError");
             mPhone.onMMIDone(this);
         }
     }
@@ -1059,7 +1063,7 @@
         if (mState == State.PENDING) {
             mState = State.COMPLETE;
             mMessage = null;
-
+            Rlog.d(LOG_TAG, "onUssdRelease");
             mPhone.onMMIDone(this);
         }
     }
@@ -1301,6 +1305,7 @@
         }
 
         mMessage = sb;
+        Rlog.d(LOG_TAG, "onSetComplete mmi=" + this);
         mPhone.onMMIDone(this);
     }
 
@@ -1382,6 +1387,7 @@
         }
 
         mMessage = sb;
+        Rlog.d(LOG_TAG, "onGetClirComplete: mmi=" + this);
         mPhone.onMMIDone(this);
     }
 
@@ -1534,6 +1540,7 @@
         }
 
         mMessage = sb;
+        Rlog.d(LOG_TAG, "onQueryCfComplete: mmi=" + this);
         mPhone.onMMIDone(this);
 
     }
@@ -1571,6 +1578,7 @@
         }
 
         mMessage = sb;
+        Rlog.d(LOG_TAG, "onQueryComplete: mmi=" + this);
         mPhone.onMMIDone(this);
     }
 
@@ -1629,8 +1637,11 @@
         if (mSib != null) sb.append(" sib=" + Rlog.pii(LOG_TAG, mSib));
         if (mSic != null) sb.append(" sic=" + Rlog.pii(LOG_TAG, mSic));
         if (mPoundString != null) sb.append(" poundString=" + Rlog.pii(LOG_TAG, mPoundString));
-        if (mDialingNumber != null) sb.append(" dialingNumber=" + mDialingNumber);
-        if (mPwd != null) sb.append(" pwd=" + mPwd);
+        if (mDialingNumber != null) {
+            sb.append(" dialingNumber=" + Rlog.pii(LOG_TAG, mDialingNumber));
+        }
+        if (mPwd != null) sb.append(" pwd=" + Rlog.pii(LOG_TAG, mPwd));
+        if (mCallbackReceiver != null) sb.append(" hasReceiver");
         sb.append("}");
         return sb.toString();
     }
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index 563252a..9b86712 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -16,14 +16,36 @@
 
 package com.android.internal.telephony.imsphone;
 
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAIC;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAICr;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOC;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOIC;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOICxH;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_ALL;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MO;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MT;
+import static com.android.internal.telephony.CommandsInterface.CF_ACTION_DISABLE;
+import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE;
+import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ERASURE;
+import static com.android.internal.telephony.CommandsInterface.CF_ACTION_REGISTRATION;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL_CONDITIONAL;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_BUSY;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_NOT_REACHABLE;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_NO_REPLY;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
+
 import android.app.Activity;
-import android.app.ActivityManagerNative;
+import android.app.ActivityManager;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.net.NetworkStats;
 import android.net.Uri;
 import android.os.AsyncResult;
 import android.os.Bundle;
@@ -31,14 +53,12 @@
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
 import android.os.Registrant;
 import android.os.RegistrantList;
 import android.os.ResultReceiver;
-import android.os.PowerManager.WakeLock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
-
-import android.provider.Telephony;
 import android.telecom.VideoProfile;
 import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneNumberUtils;
@@ -55,33 +75,9 @@
 import com.android.ims.ImsEcbmStateListener;
 import com.android.ims.ImsException;
 import com.android.ims.ImsManager;
-import com.android.ims.ImsMultiEndpoint;
 import com.android.ims.ImsReasonInfo;
 import com.android.ims.ImsSsInfo;
 import com.android.ims.ImsUtInterface;
-
-import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOC;
-import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOIC;
-import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOICxH;
-import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAIC;
-import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAICr;
-import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_ALL;
-import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MO;
-import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MT;
-
-import static com.android.internal.telephony.CommandsInterface.CF_ACTION_DISABLE;
-import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE;
-import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ERASURE;
-import static com.android.internal.telephony.CommandsInterface.CF_ACTION_REGISTRATION;
-import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL;
-import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL_CONDITIONAL;
-import static com.android.internal.telephony.CommandsInterface.CF_REASON_NO_REPLY;
-import static com.android.internal.telephony.CommandsInterface.CF_REASON_NOT_REACHABLE;
-import static com.android.internal.telephony.CommandsInterface.CF_REASON_BUSY;
-import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
-import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
-import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE;
-
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.CallForwardInfo;
@@ -101,10 +97,13 @@
 import com.android.internal.telephony.UUSInfo;
 import com.android.internal.telephony.gsm.SuppServiceNotification;
 import com.android.internal.telephony.uicc.IccRecords;
+import com.android.internal.telephony.util.NotificationChannelController;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
+import java.util.Deque;
 import java.util.List;
 
 /**
@@ -136,7 +135,6 @@
     ImsPhoneCallTracker mCT;
     ImsExternalCallTracker mExternalCallTracker;
     private ArrayList <ImsPhoneMmiCode> mPendingMMIs = new ArrayList<ImsPhoneMmiCode>();
-
     private ServiceState mSS = new ServiceState();
 
     // To redial silently through GSM or CDMA when dialing through IMS fails
@@ -226,7 +224,10 @@
                     .registerForDataRegStateOrRatChanged(this,
                             EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED, null);
         }
-        updateDataServiceState();
+        // Sets the Voice reg state to STATE_OUT_OF_SERVICE and also queries the data service
+        // state. We don't ever need the voice reg state to be anything other than in or out of
+        // service.
+        setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
 
         mDefaultPhone.registerForServiceStateChanged(this, EVENT_SERVICE_STATE_CHANGED, null);
         // Force initial roaming state update later, on EVENT_CARRIER_CONFIG_CHANGED.
@@ -344,6 +345,11 @@
         return mCT.mRingingCall;
     }
 
+    @Override
+    public boolean isImsAvailable() {
+        return mCT.isImsServiceReady();
+    }
+
     private boolean handleCallDeflectionIncallSupplementaryService(
             String dialString) {
         if (dialString.length() > 1) {
@@ -370,15 +376,42 @@
         return true;
     }
 
+    private void sendUssdResponse(String ussdRequest, CharSequence message, int returnCode,
+                                   ResultReceiver wrappedCallback) {
+        UssdResponse response = new UssdResponse(ussdRequest, message);
+        Bundle returnData = new Bundle();
+        returnData.putParcelable(TelephonyManager.USSD_RESPONSE, response);
+        wrappedCallback.send(returnCode, returnData);
+
+    }
+
     @Override
-    public boolean handleUssdRequest(String ussdRequest, ResultReceiver wrappedCallback) {
-        try {
-           dialInternal(ussdRequest, VideoProfile.STATE_AUDIO_ONLY, null, wrappedCallback);
-           return true;
-        } catch (Exception e) {
-           Rlog.d(LOG_TAG, "exception" + e);
-           return false;
+    public boolean handleUssdRequest(String ussdRequest, ResultReceiver wrappedCallback)
+            throws CallStateException {
+        if (mPendingMMIs.size() > 0) {
+            // There are MMI codes in progress; fail attempt now.
+            Rlog.i(LOG_TAG, "handleUssdRequest: queue full: " + Rlog.pii(LOG_TAG, ussdRequest));
+            sendUssdResponse(ussdRequest, null, TelephonyManager.USSD_RETURN_FAILURE,
+                    wrappedCallback );
+            return true;
         }
+        try {
+            dialInternal(ussdRequest, VideoProfile.STATE_AUDIO_ONLY, null, wrappedCallback);
+        } catch (CallStateException cse) {
+            if (CS_FALLBACK.equals(cse.getMessage())) {
+                throw cse;
+            } else {
+                Rlog.w(LOG_TAG, "Could not execute USSD " + cse);
+                sendUssdResponse(ussdRequest, null, TelephonyManager.USSD_RETURN_FAILURE,
+                        wrappedCallback);
+            }
+        } catch (Exception e) {
+            Rlog.w(LOG_TAG, "Could not execute USSD " + e);
+            sendUssdResponse(ussdRequest, null, TelephonyManager.USSD_RETURN_FAILURE,
+                    wrappedCallback);
+            return false;
+        }
+        return true;
     }
 
     private boolean handleCallWaitingIncallSupplementaryService(
@@ -593,9 +626,9 @@
         // Only look at the Network portion for mmi
         String networkPortion = PhoneNumberUtils.extractNetworkPortionAlt(newDialString);
         ImsPhoneMmiCode mmi =
-                ImsPhoneMmiCode.newFromDialString(networkPortion, this);
+                ImsPhoneMmiCode.newFromDialString(networkPortion, this, wrappedCallback);
         if (DBG) Rlog.d(LOG_TAG,
-                "dialing w/ mmi '" + mmi + "'...");
+                "dialInternal: dialing w/ mmi '" + mmi + "'...");
 
         if (mmi == null) {
             return mCT.dial(dialString, videoState, intentExtras);
@@ -604,11 +637,26 @@
         } else if (!mmi.isSupportedOverImsPhone()) {
             // If the mmi is not supported by IMS service,
             // try to initiate dialing with default phone
+            // Note: This code is never reached; there is a bug in isSupportedOverImsPhone which
+            // causes it to return true even though the "processCode" method ultimately throws the
+            // exception.
+            Rlog.i(LOG_TAG, "dialInternal: USSD not supported by IMS; fallback to CS.");
             throw new CallStateException(CS_FALLBACK);
         } else {
             mPendingMMIs.add(mmi);
             mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
-            mmi.processCode();
+
+            try {
+                mmi.processCode();
+            } catch (CallStateException cse) {
+                if (CS_FALLBACK.equals(cse.getMessage())) {
+                    Rlog.i(LOG_TAG, "dialInternal: fallback to GSM required.");
+                    // Make sure we remove from the list of pending MMIs since it will handover to
+                    // GSM.
+                    mPendingMMIs.remove(mmi);
+                    throw cse;
+                }
+            }
 
             return null;
         }
@@ -656,6 +704,11 @@
     }
 
     @Override
+    public void setTTYMode(int ttyMode, Message onComplete) {
+        mCT.setTtyMode(ttyMode);
+    }
+
+    @Override
     public void setUiTTYMode(int uiTtyMode, Message onComplete) {
         mCT.setUiTTYMode(uiTtyMode, onComplete);
     }
@@ -1032,18 +1085,17 @@
             } else {
                 found.onUssdFinished(ussdMessage, isUssdRequest);
             }
-        } else { // pending USSD not found
-            // The network may initiate its own USSD request
+        } else if (!isUssdError && ussdMessage != null) {
+                // pending USSD not found
+                // The network may initiate its own USSD request
 
-            // ignore everything that isnt a Notify or a Request
-            // also, discard if there is no message to present
-            if (!isUssdError && ussdMessage != null) {
+                // ignore everything that isnt a Notify or a Request
+                // also, discard if there is no message to present
                 ImsPhoneMmiCode mmi;
                 mmi = ImsPhoneMmiCode.newNetworkInitiatedUssd(ussdMessage,
                         isUssdRequest,
                         this);
                 onNetworkInitiatedUssd(mmi);
-            }
         }
     }
 
@@ -1058,16 +1110,16 @@
          * The exception is cancellation of an incoming USSD-REQUEST, which is
          * not on the list.
          */
+        Rlog.d(LOG_TAG, "onMMIDone: mmi=" + mmi);
         if (mPendingMMIs.remove(mmi) || mmi.isUssdRequest()) {
             ResultReceiver receiverCallback = mmi.getUssdCallbackReceiver();
             if (receiverCallback != null) {
-                UssdResponse response = new UssdResponse(mmi.getDialString(), mmi.getMessage());
-                Bundle returnData = new Bundle();
-                returnData.putParcelable(TelephonyManager.USSD_RESPONSE, response);
                 int returnCode = (mmi.getState() ==  MmiCode.State.COMPLETE) ?
                         TelephonyManager.USSD_RETURN_SUCCESS : TelephonyManager.USSD_RETURN_FAILURE;
-                receiverCallback.send(returnCode, returnData);
+                sendUssdResponse(mmi.getDialString(), mmi.getMessage(), returnCode,
+                        receiverCallback );
             } else {
+                Rlog.v(LOG_TAG, "onMMIDone: notifyRegistrants");
                 mMmiCompleteRegistrants.notifyRegistrants(
                     new AsyncResult(null, mmi, null));
             }
@@ -1338,8 +1390,8 @@
         Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
         intent.putExtra(PhoneConstants.PHONE_IN_ECM_STATE, isInEcm());
         SubscriptionManager.putPhoneIdAndSubIdExtra(intent, getPhoneId());
-        ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);
-        if (DBG) Rlog.d(LOG_TAG, "sendEmergencyCallbackModeChange");
+        ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL);
+        if (DBG) Rlog.d(LOG_TAG, "sendEmergencyCallbackModeChange: isInEcm=" + isInEcm());
     }
 
     @Override
@@ -1502,14 +1554,15 @@
                                 PendingIntent.FLAG_UPDATE_CURRENT
                         );
 
-                final Notification notification =
-                        new Notification.Builder(mContext)
+                final Notification notification = new Notification.Builder(mContext)
                                 .setSmallIcon(android.R.drawable.stat_sys_warning)
                                 .setContentTitle(title)
                                 .setContentText(messageNotification)
                                 .setAutoCancel(true)
                                 .setContentIntent(resultPendingIntent)
-                                .setStyle(new Notification.BigTextStyle().bigText(messageNotification))
+                                .setStyle(new Notification.BigTextStyle()
+                                .bigText(messageNotification))
+                                .setChannelId(NotificationChannelController.CHANNEL_ID_WFC)
                                 .build();
                 final String notificationTag = "wifi_calling";
                 final int notificationId = 1;
@@ -1529,95 +1582,103 @@
     public void processDisconnectReason(ImsReasonInfo imsReasonInfo) {
         if (imsReasonInfo.mCode == imsReasonInfo.CODE_REGISTRATION_ERROR
                 && imsReasonInfo.mExtraMessage != null) {
+            // Suppress WFC Registration notifications if WFC is not enabled by the user.
+            if (ImsManager.isWfcEnabledByUser(mContext)) {
+                processWfcDisconnectForNotification(imsReasonInfo);
+            }
+        }
+    }
 
-            CarrierConfigManager configManager =
-                    (CarrierConfigManager)mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-            if (configManager == null) {
-                Rlog.e(LOG_TAG, "processDisconnectReason: CarrierConfigManager is not ready");
-                return;
-            }
-            PersistableBundle pb = configManager.getConfigForSubId(getSubId());
-            if (pb == null) {
-                Rlog.e(LOG_TAG, "processDisconnectReason: no config for subId " + getSubId());
-                return;
-            }
-            final String[] wfcOperatorErrorCodes =
-                    pb.getStringArray(
-                            CarrierConfigManager.KEY_WFC_OPERATOR_ERROR_CODES_STRING_ARRAY);
-            if (wfcOperatorErrorCodes == null) {
-                // no operator-specific error codes
-                return;
+    // Processes an IMS disconnect cause for possible WFC registration errors and optionally
+    // disable WFC.
+    private void processWfcDisconnectForNotification(ImsReasonInfo imsReasonInfo) {
+        CarrierConfigManager configManager =
+                (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        if (configManager == null) {
+            Rlog.e(LOG_TAG, "processDisconnectReason: CarrierConfigManager is not ready");
+            return;
+        }
+        PersistableBundle pb = configManager.getConfigForSubId(getSubId());
+        if (pb == null) {
+            Rlog.e(LOG_TAG, "processDisconnectReason: no config for subId " + getSubId());
+            return;
+        }
+        final String[] wfcOperatorErrorCodes =
+                pb.getStringArray(
+                        CarrierConfigManager.KEY_WFC_OPERATOR_ERROR_CODES_STRING_ARRAY);
+        if (wfcOperatorErrorCodes == null) {
+            // no operator-specific error codes
+            return;
+        }
+
+        final String[] wfcOperatorErrorAlertMessages =
+                mContext.getResources().getStringArray(
+                        com.android.internal.R.array.wfcOperatorErrorAlertMessages);
+        final String[] wfcOperatorErrorNotificationMessages =
+                mContext.getResources().getStringArray(
+                        com.android.internal.R.array.wfcOperatorErrorNotificationMessages);
+
+        for (int i = 0; i < wfcOperatorErrorCodes.length; i++) {
+            String[] codes = wfcOperatorErrorCodes[i].split("\\|");
+            if (codes.length != 2) {
+                Rlog.e(LOG_TAG, "Invalid carrier config: " + wfcOperatorErrorCodes[i]);
+                continue;
             }
 
-            final String[] wfcOperatorErrorAlertMessages =
-                    mContext.getResources().getStringArray(
-                            com.android.internal.R.array.wfcOperatorErrorAlertMessages);
-            final String[] wfcOperatorErrorNotificationMessages =
-                    mContext.getResources().getStringArray(
-                            com.android.internal.R.array.wfcOperatorErrorNotificationMessages);
-
-            for (int i = 0; i < wfcOperatorErrorCodes.length; i++) {
-                String[] codes = wfcOperatorErrorCodes[i].split("\\|");
-                if (codes.length != 2) {
-                    Rlog.e(LOG_TAG, "Invalid carrier config: " + wfcOperatorErrorCodes[i]);
-                    continue;
-                }
-
-                // Match error code.
-                if (!imsReasonInfo.mExtraMessage.startsWith(
-                        codes[0])) {
-                    continue;
-                }
-                // If there is no delimiter at the end of error code string
-                // then we need to verify that we are not matching partial code.
-                // EXAMPLE: "REG9" must not match "REG99".
-                // NOTE: Error code must not be empty.
-                int codeStringLength = codes[0].length();
-                char lastChar = codes[0].charAt(codeStringLength - 1);
-                if (Character.isLetterOrDigit(lastChar)) {
-                    if (imsReasonInfo.mExtraMessage.length() > codeStringLength) {
-                        char nextChar = imsReasonInfo.mExtraMessage.charAt(codeStringLength);
-                        if (Character.isLetterOrDigit(nextChar)) {
-                            continue;
-                        }
+            // Match error code.
+            if (!imsReasonInfo.mExtraMessage.startsWith(
+                    codes[0])) {
+                continue;
+            }
+            // If there is no delimiter at the end of error code string
+            // then we need to verify that we are not matching partial code.
+            // EXAMPLE: "REG9" must not match "REG99".
+            // NOTE: Error code must not be empty.
+            int codeStringLength = codes[0].length();
+            char lastChar = codes[0].charAt(codeStringLength - 1);
+            if (Character.isLetterOrDigit(lastChar)) {
+                if (imsReasonInfo.mExtraMessage.length() > codeStringLength) {
+                    char nextChar = imsReasonInfo.mExtraMessage.charAt(codeStringLength);
+                    if (Character.isLetterOrDigit(nextChar)) {
+                        continue;
                     }
                 }
-
-                final CharSequence title = mContext.getText(
-                        com.android.internal.R.string.wfcRegErrorTitle);
-
-                int idx = Integer.parseInt(codes[1]);
-                if (idx < 0 ||
-                        idx >= wfcOperatorErrorAlertMessages.length ||
-                        idx >= wfcOperatorErrorNotificationMessages.length) {
-                    Rlog.e(LOG_TAG, "Invalid index: " + wfcOperatorErrorCodes[i]);
-                    continue;
-                }
-                CharSequence messageAlert = imsReasonInfo.mExtraMessage;
-                CharSequence messageNotification = imsReasonInfo.mExtraMessage;
-                if (!wfcOperatorErrorAlertMessages[idx].isEmpty()) {
-                    messageAlert = wfcOperatorErrorAlertMessages[idx];
-                }
-                if (!wfcOperatorErrorNotificationMessages[idx].isEmpty()) {
-                    messageNotification = wfcOperatorErrorNotificationMessages[idx];
-                }
-
-                // UX requirement is to disable WFC in case of "permanent" registration failures.
-                ImsManager.setWfcSetting(mContext, false);
-
-                // If WfcSettings are active then alert will be shown
-                // otherwise notification will be added.
-                Intent intent = new Intent(ImsManager.ACTION_IMS_REGISTRATION_ERROR);
-                intent.putExtra(EXTRA_KEY_ALERT_TITLE, title);
-                intent.putExtra(EXTRA_KEY_ALERT_MESSAGE, messageAlert);
-                intent.putExtra(EXTRA_KEY_NOTIFICATION_MESSAGE, messageNotification);
-                mContext.sendOrderedBroadcast(intent, null, mResultReceiver,
-                        null, Activity.RESULT_OK, null, null);
-
-                // We can only match a single error code
-                // so should break the loop after a successful match.
-                break;
             }
+
+            final CharSequence title = mContext.getText(
+                    com.android.internal.R.string.wfcRegErrorTitle);
+
+            int idx = Integer.parseInt(codes[1]);
+            if (idx < 0
+                    || idx >= wfcOperatorErrorAlertMessages.length
+                    || idx >= wfcOperatorErrorNotificationMessages.length) {
+                Rlog.e(LOG_TAG, "Invalid index: " + wfcOperatorErrorCodes[i]);
+                continue;
+            }
+            CharSequence messageAlert = imsReasonInfo.mExtraMessage;
+            CharSequence messageNotification = imsReasonInfo.mExtraMessage;
+            if (!wfcOperatorErrorAlertMessages[idx].isEmpty()) {
+                messageAlert = wfcOperatorErrorAlertMessages[idx];
+            }
+            if (!wfcOperatorErrorNotificationMessages[idx].isEmpty()) {
+                messageNotification = wfcOperatorErrorNotificationMessages[idx];
+            }
+
+            // UX requirement is to disable WFC in case of "permanent" registration failures.
+            ImsManager.setWfcSetting(mContext, false);
+
+            // If WfcSettings are active then alert will be shown
+            // otherwise notification will be added.
+            Intent intent = new Intent(ImsManager.ACTION_IMS_REGISTRATION_ERROR);
+            intent.putExtra(EXTRA_KEY_ALERT_TITLE, title);
+            intent.putExtra(EXTRA_KEY_ALERT_MESSAGE, messageAlert);
+            intent.putExtra(EXTRA_KEY_NOTIFICATION_MESSAGE, messageNotification);
+            mContext.sendOrderedBroadcast(intent, null, mResultReceiver,
+                    null, Activity.RESULT_OK, null, null);
+
+            // We can only match a single error code
+            // so should break the loop after a successful match.
+            break;
         }
     }
 
@@ -1642,8 +1703,8 @@
     }
 
     @Override
-    public long getVtDataUsage() {
-        return mCT.getVtDataUsage();
+    public NetworkStats getVtDataUsage(boolean perUidStats) {
+        return mCT.getVtDataUsage(perUidStats);
     }
 
     private void updateRoamingState(boolean newRoaming) {
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
index 8228cca..ed24d22 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
@@ -26,6 +26,7 @@
 import android.os.WorkSource;
 import android.telephony.CellInfo;
 import android.telephony.CellLocation;
+import android.telephony.NetworkScanRequest;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
@@ -427,7 +428,7 @@
     }
 
     @Override
-    public void startNetworkScan(Message response) {
+    public void startNetworkScan(NetworkScanRequest nsr, Message response) {
     }
 
     @Override
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
index a011f757..52636f5 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
@@ -267,6 +267,7 @@
             long conferenceConnectTime = imsPhoneConnection.getConferenceConnectTime();
             if (conferenceConnectTime > 0) {
                 imsPhoneConnection.setConnectTime(conferenceConnectTime);
+                imsPhoneConnection.setConnectTimeReal(imsPhoneConnection.getConnectTimeReal());
             } else {
                 if (DBG) {
                     Rlog.d(LOG_TAG, "merge: conference connect time is 0");
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index f5a95dc..9b4aced 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -22,8 +22,10 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
+import android.net.NetworkStats;
 import android.net.Uri;
 import android.os.AsyncResult;
 import android.os.Bundle;
@@ -33,15 +35,17 @@
 import android.os.Registrant;
 import android.os.RegistrantList;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.preference.PreferenceManager;
 import android.provider.Settings;
 import android.telecom.ConferenceParticipant;
+import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
 import android.telephony.CarrierConfigManager;
 import android.telephony.DisconnectCause;
-import android.telephony.PreciseDisconnectCause;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.PreciseDisconnectCause;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
@@ -49,10 +53,10 @@
 import android.telephony.ims.ImsServiceProxy;
 import android.telephony.ims.feature.ImsFeature;
 import android.text.TextUtils;
-import android.util.SparseIntArray;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Pair;
+import android.util.SparseIntArray;
 
 import com.android.ims.ImsCall;
 import com.android.ims.ImsCallProfile;
@@ -71,6 +75,7 @@
 import com.android.ims.internal.ImsVideoCallProviderWrapper;
 import com.android.ims.internal.VideoPauseTracker;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.SomeArgs;
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.CallStateException;
 import com.android.internal.telephony.CallTracker;
@@ -80,12 +85,13 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyProperties;
-import com.android.internal.telephony.TelephonyProto.ImsConnectionState;
-import com.android.internal.telephony.TelephonyProto.TelephonyCallSession;
-import com.android.internal.telephony.TelephonyProto.TelephonyCallSession.Event.ImsCommand;
 import com.android.internal.telephony.dataconnection.DataEnabledSettings;
 import com.android.internal.telephony.gsm.SuppServiceNotification;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
+import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.ImsCommand;
+import com.android.server.net.NetworkStatsService;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -93,6 +99,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.regex.Pattern;
 
 /**
@@ -106,6 +113,10 @@
         void onPhoneStateChanged(PhoneConstants.State oldState, PhoneConstants.State newState);
     }
 
+    public interface SharedPreferenceProxy {
+        SharedPreferences getDefaultSharedPreferences(Context context);
+    }
+
     private static final boolean DBG = true;
 
     // When true, dumps the state of ImsPhoneCallTracker after changes to foreground and background
@@ -121,6 +132,7 @@
             "UTLTE", "UTWiFi"};
 
     private TelephonyMetrics mMetrics;
+    private boolean mCarrierConfigLoaded = false;
 
     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
@@ -205,6 +217,10 @@
                     log("onReceive : Updating mAllowEmergencyVideoCalls = " +
                             mAllowEmergencyVideoCalls);
                 }
+                mCarrierConfigLoaded  = true;
+            } else if (TelecomManager.ACTION_CHANGE_DEFAULT_DIALER.equals(intent.getAction())) {
+                mDefaultDialerUid.set(getPackageUid(context, intent.getStringExtra(
+                        TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME)));
             }
         }
     };
@@ -222,6 +238,7 @@
     private static final int EVENT_DATA_ENABLED_CHANGED = 23;
     private static final int EVENT_GET_IMS_SERVICE = 24;
     private static final int EVENT_CHECK_FOR_WIFI_HANDOVER = 25;
+    private static final int EVENT_ON_FEATURE_CAPABILITY_CHANGED = 26;
 
     private static final int TIMEOUT_HANGUP_PENDINGMO = 500;
 
@@ -249,7 +266,11 @@
     // Hold aggregated video call data usage for each video call since boot.
     // The ImsCall's call id is the key of the map.
     private final HashMap<Integer, Long> mVtDataUsageMap = new HashMap<>();
-    private volatile long mTotalVtDataUsage = 0;
+
+    private volatile NetworkStats mVtDataUsageSnapshot = null;
+    private volatile NetworkStats mVtDataUsageUidSnapshot = null;
+
+    private final AtomicInteger mDefaultDialerUid = new AtomicInteger(NetworkStats.UID_ALL);
 
     private ImsPhoneConnection mPendingMO;
     private int mClirMode = CommandsInterface.CLIR_DEFAULT;
@@ -283,6 +304,7 @@
     private ImsCall mCallExpectedToResume = null;
     private boolean mAllowEmergencyVideoCalls = false;
     private boolean mIgnoreDataEnabledChangedForVideoCalls = false;
+    private boolean mIsViLteDataMetered = false;
 
     /**
      * Listeners to changes in the phone state.  Intended for use by other interested IMS components
@@ -335,6 +357,8 @@
                 PreciseDisconnectCause.LOCAL_IMS_SERVICE_DOWN);
         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL,
                 PreciseDisconnectCause.LOCAL_NO_PENDING_CALL);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE,
+                PreciseDisconnectCause.NORMAL);
         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_POWER_OFF,
                 PreciseDisconnectCause.LOCAL_POWER_OFF);
         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_LOW_BATTERY,
@@ -569,6 +593,7 @@
      */
     private Map<Pair<Integer, String>, Integer> mImsReasonCodeMap = new ArrayMap<>();
 
+
     /**
      * TODO: Remove this code; it is a workaround.
      * When {@code true}, forces {@link ImsManager#updateImsServiceConfig(Context, int, boolean)} to
@@ -580,6 +605,13 @@
      */
     private boolean mShouldUpdateImsConfigOnDisconnect = false;
 
+    /**
+     * Default implementation for retrieving shared preferences; uses the actual PreferencesManager.
+     */
+    private SharedPreferenceProxy mSharedPreferenceProxy = (Context context) -> {
+        return PreferenceManager.getDefaultSharedPreferences(context);
+    };
+
     // Callback fires when ImsManager MMTel Feature changes state
     private ImsServiceProxy.INotifyStatusChanged mNotifyStatusChangedCallback = () -> {
         try {
@@ -637,6 +669,7 @@
         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);
         cacheCarrierConfiguration(mPhone.getSubId());
 
@@ -644,11 +677,47 @@
                 this, EVENT_DATA_ENABLED_CHANGED, null);
 
         mImsServiceRetryCount = 0;
+
+        final TelecomManager telecomManager =
+                (TelecomManager) mPhone.getContext().getSystemService(Context.TELECOM_SERVICE);
+        mDefaultDialerUid.set(
+                getPackageUid(mPhone.getContext(), telecomManager.getDefaultDialerPackage()));
+
+        long currentTime = SystemClock.elapsedRealtime();
+        mVtDataUsageSnapshot = new NetworkStats(currentTime, 1);
+        mVtDataUsageUidSnapshot = new NetworkStats(currentTime, 1);
+
         // Send a message to connect to the Ims Service and open a connection through
         // getImsService().
         sendEmptyMessage(EVENT_GET_IMS_SERVICE);
     }
 
+    /**
+     * Test-only method used to mock out access to the shared preferences through the
+     * {@link PreferenceManager}.
+     * @param sharedPreferenceProxy
+     */
+    @VisibleForTesting
+    public void setSharedPreferenceProxy(SharedPreferenceProxy sharedPreferenceProxy) {
+        mSharedPreferenceProxy = sharedPreferenceProxy;
+    }
+
+    private int getPackageUid(Context context, String pkg) {
+        if (pkg == null) {
+            return NetworkStats.UID_ALL;
+        }
+
+        // Initialize to UID_ALL so at least it can be counted to overall data usage if
+        // the dialer's package uid is not available.
+        int uid = NetworkStats.UID_ALL;
+        try {
+            uid = context.getPackageManager().getPackageUid(pkg, 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            loge("Cannot find package uid. pkg = " + pkg);
+        }
+        return uid;
+    }
+
     private PendingIntent createIncomingCallPendingIntent() {
         Intent intent = new Intent(ImsManager.ACTION_IMS_INCOMING_CALL);
         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -692,10 +761,16 @@
             multiEndpoint.setExternalCallStateListener(
                     mPhone.getExternalCallTracker().getExternalCallStateListener());
         }
+
+        if (mCarrierConfigLoaded) {
+            ImsManager.updateImsServiceConfig(mPhone.getContext(),
+                    mPhone.getPhoneId(), true);
+        }
     }
 
     private void stopListeningForCalls() {
         try {
+            resetImsCapabilities();
             // Only close on valid session.
             if (mImsManager != null && mServiceId > 0) {
                 mImsManager.close(mServiceId);
@@ -751,8 +826,16 @@
 
     public Connection dial(String dialString, int videoState, Bundle intentExtras) throws
             CallStateException {
-        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mPhone.getContext());
-        int oirMode = sp.getInt(Phone.CLIR_KEY, CommandsInterface.CLIR_DEFAULT);
+        int oirMode;
+        if (mSharedPreferenceProxy != null && mPhone.getDefaultPhone() != null) {
+            SharedPreferences sp = mSharedPreferenceProxy.getDefaultSharedPreferences(
+                    mPhone.getContext());
+            oirMode = sp.getInt(Phone.CLIR_KEY + mPhone.getDefaultPhone().getPhoneId(),
+                    CommandsInterface.CLIR_DEFAULT);
+        } else {
+            loge("dial; could not get default CLIR mode.");
+            oirMode = CommandsInterface.CLIR_DEFAULT;
+        }
         return dial(dialString, oirMode, videoState, intentExtras);
     }
 
@@ -859,6 +942,14 @@
         return mPendingMO;
     }
 
+    boolean isImsServiceReady() {
+        if (mImsManager == null) {
+            return false;
+        }
+
+        return mImsManager.isServiceAvailable();
+    }
+
     /**
      * Caches frequently used carrier configuration items locally.
      *
@@ -894,9 +985,11 @@
         mSupportDowngradeVtToAudio = carrierConfig.getBoolean(
                 CarrierConfigManager.KEY_SUPPORT_DOWNGRADE_VT_TO_AUDIO_BOOL);
         mNotifyHandoverVideoFromWifiToLTE = carrierConfig.getBoolean(
-                CarrierConfigManager.KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL);
+                CarrierConfigManager.KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL);
         mIgnoreDataEnabledChangedForVideoCalls = carrierConfig.getBoolean(
                 CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS);
+        mIsViLteDataMetered = carrierConfig.getBoolean(
+                CarrierConfigManager.KEY_VILTE_DATA_IS_METERED_BOOL);
         mSupportPauseVideo = carrierConfig.getBoolean(
                 CarrierConfigManager.KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL);
 
@@ -1117,7 +1210,7 @@
 
             // Swap the ImsCalls pointed to by the foreground and background ImsPhoneCalls.
             // If hold or resume later fails, we will swap them back.
-            boolean switchingWithWaitingCall = mBackgroundCall.getImsCall() == null &&
+            boolean switchingWithWaitingCall = !mBackgroundCall.getState().isAlive() &&
                     mRingingCall != null &&
                     mRingingCall.getState() == ImsPhoneCall.State.WAITING;
 
@@ -1186,6 +1279,13 @@
         ImsPhoneConnection foregroundConnection = mForegroundCall.getFirstConnection();
         if (foregroundConnection != null) {
             foregroundConnection.setConferenceConnectTime(conferenceConnectTime);
+            foregroundConnection.onConnectionEvent(android.telecom.Connection.EVENT_MERGE_START,
+                    null);
+        }
+        ImsPhoneConnection backgroundConnection = findConnection(bgImsCall);
+        if (backgroundConnection != null) {
+            backgroundConnection.onConnectionEvent(android.telecom.Connection.EVENT_MERGE_START,
+                    null);
         }
 
         try {
@@ -1218,19 +1318,16 @@
             && !mForegroundCall.isFull();
     }
 
-    public boolean
-    canDial() {
+    public boolean canDial() {
         boolean ret;
-        int serviceState = mPhone.getServiceState().getState();
         String disableCall = SystemProperties.get(
                 TelephonyProperties.PROPERTY_DISABLE_CALL, "false");
 
-        ret = (serviceState != ServiceState.STATE_POWER_OFF)
-            && mPendingMO == null
-            && !mRingingCall.isRinging()
-            && !disableCall.equals("true")
-            && (!mForegroundCall.getState().isAlive()
-                    || !mBackgroundCall.getState().isAlive());
+        ret = mPendingMO == null
+                && !mRingingCall.isRinging()
+                && !disableCall.equals("true")
+                && (!mForegroundCall.getState().isAlive()
+                        || !mBackgroundCall.getState().isAlive());
 
         return ret;
     }
@@ -1327,7 +1424,27 @@
     }
 
     //***** Called from ImsPhone
+    /**
+     * Set the TTY mode. This is the actual tty mode (varies depending on peripheral status)
+     */
+    public void setTtyMode(int ttyMode) {
+        if (mImsManager == null) {
+            Log.w(LOG_TAG, "ImsManager is null when setting TTY mode");
+            return;
+        }
 
+        try {
+            mImsManager.setTtyMode(ttyMode);
+        } catch (ImsException e) {
+            loge("setTtyMode : " + e);
+            retryGetImsService();
+        }
+    }
+
+    /**
+     * Sets the UI TTY mode. This is the preferred TTY mode that the user sets in the call
+     * settings screen.
+     */
     public void setUiTTYMode(int uiTtyMode, Message onComplete) {
         if (mImsManager == null) {
             mPhone.sendErrorResponse(onComplete, getImsManagerIsNullException());
@@ -1337,7 +1454,7 @@
         try {
             mImsManager.setUiTTYMode(mPhone.getContext(), uiTtyMode, onComplete);
         } catch (ImsException e) {
-            loge("setTTYMode : " + e);
+            loge("setUITTYMode : " + e);
             mPhone.sendErrorResponse(onComplete, e);
             retryGetImsService();
         }
@@ -1703,10 +1820,17 @@
         return code;
     }
 
-    private int getDisconnectCauseFromReasonInfo(ImsReasonInfo reasonInfo) {
+    /**
+     * Maps an {@link ImsReasonInfo} reason code to a {@link DisconnectCause} cause code.
+     * The {@link Call.State} provided is the state of the call prior to disconnection.
+     * @param reasonInfo the {@link ImsReasonInfo} for the disconnection.
+     * @param callState The {@link Call.State} prior to disconnection.
+     * @return The {@link DisconnectCause} code.
+     */
+    @VisibleForTesting
+    public int getDisconnectCauseFromReasonInfo(ImsReasonInfo reasonInfo, Call.State callState) {
         int cause = DisconnectCause.ERROR_UNSPECIFIED;
 
-        //int type = reasonInfo.getReasonType();
         int code = maybeRemapReasonCode(reasonInfo);
         switch (code) {
             case ImsReasonInfo.CODE_SIP_BAD_ADDRESS:
@@ -1719,6 +1843,9 @@
             case ImsReasonInfo.CODE_USER_TERMINATED:
                 return DisconnectCause.LOCAL;
 
+            case ImsReasonInfo.CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE:
+                return DisconnectCause.IMS_MERGED_SUCCESSFULLY;
+
             case ImsReasonInfo.CODE_LOCAL_CALL_DECLINE:
             case ImsReasonInfo.CODE_REMOTE_CALL_DECLINE:
                 // If the call has been declined locally (on this device), or on remotely (on
@@ -1759,10 +1886,18 @@
             case ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE:
                 return DisconnectCause.TIMED_OUT;
 
-            case ImsReasonInfo.CODE_LOCAL_LOW_BATTERY:
             case ImsReasonInfo.CODE_LOCAL_POWER_OFF:
                 return DisconnectCause.POWER_OFF;
 
+            case ImsReasonInfo.CODE_LOCAL_LOW_BATTERY:
+            case ImsReasonInfo.CODE_LOW_BATTERY: {
+                if (callState == Call.State.DIALING) {
+                    return DisconnectCause.DIAL_LOW_BATTERY;
+                } else {
+                    return DisconnectCause.LOW_BATTERY;
+                }
+            }
+
             case ImsReasonInfo.CODE_FDN_BLOCKED:
                 return DisconnectCause.FDN_BLOCKED;
 
@@ -1786,6 +1921,16 @@
 
             case ImsReasonInfo.CODE_WIFI_LOST:
                 return DisconnectCause.WIFI_LOST;
+
+            case ImsReasonInfo.CODE_ACCESS_CLASS_BLOCKED:
+                return DisconnectCause.IMS_ACCESS_BLOCKED;
+
+            case ImsReasonInfo.CODE_EMERGENCY_TEMP_FAILURE:
+                return DisconnectCause.EMERGENCY_TEMP_FAILURE;
+
+            case ImsReasonInfo.CODE_EMERGENCY_PERM_FAILURE:
+                return DisconnectCause.EMERGENCY_PERM_FAILURE;
+
             default:
         }
 
@@ -1907,8 +2052,18 @@
                     return;
                 } else {
                     mPendingMO = null;
-                    int cause = getDisconnectCauseFromReasonInfo(reasonInfo);
                     ImsPhoneConnection conn = findConnection(imsCall);
+                    Call.State callState;
+                    if (conn != null) {
+                        callState = conn.getState();
+                    } else {
+                        // Need to fall back in case connection is null; it shouldn't be, but a sane
+                        // fallback is to assume we're dialing.  This state is only used to
+                        // determine which disconnect string to show in the case of a low battery
+                        // disconnect.
+                        callState = Call.State.DIALING;
+                    }
+                    int cause = getDisconnectCauseFromReasonInfo(reasonInfo, callState);
 
                     if(conn != null) {
                         conn.setPreciseDisconnectCause(
@@ -1926,8 +2081,18 @@
         public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) {
             if (DBG) log("onCallTerminated reasonCode=" + reasonInfo.getCode());
 
-            int cause = getDisconnectCauseFromReasonInfo(reasonInfo);
             ImsPhoneConnection conn = findConnection(imsCall);
+            Call.State callState;
+            if (conn != null) {
+                callState = conn.getState();
+            } else {
+                // Connection shouldn't be null, but if it is, we can assume the call was active.
+                // This call state is only used for determining which disconnect message to show in
+                // the case of the device's battery being low resulting in a call drop.
+                callState = Call.State.ACTIVE;
+            }
+            int cause = getDisconnectCauseFromReasonInfo(reasonInfo, callState);
+
             if (DBG) log("cause = " + cause + " conn = " + conn);
 
             if (conn != null) {
@@ -2179,10 +2344,21 @@
                     mPhone.stopOnHoldTone(conn);
                     mOnHoldToneStarted = false;
                 }
-
                 conn.onConnectionEvent(android.telecom.Connection.EVENT_CALL_REMOTELY_UNHELD, null);
             }
 
+            boolean useVideoPauseWorkaround = mPhone.getContext().getResources().getBoolean(
+                    com.android.internal.R.bool.config_useVideoPauseWorkaround);
+            if (useVideoPauseWorkaround && mSupportPauseVideo &&
+                    VideoProfile.isVideo(conn.getVideoState())) {
+                // If we are using the video pause workaround, the vendor IMS code has issues
+                // with video pause signalling.  In this case, when a call is remotely
+                // held, the modem does not reliably change the video state of the call to be
+                // paused.
+                // As a workaround, we will turn on that bit now.
+                conn.changeToUnPausedState();
+            }
+
             SuppServiceNotification supp = new SuppServiceNotification();
             // Type of notification: 0 = MO; 1 = MT
             // Refer SuppServiceNotification class documentation.
@@ -2204,8 +2380,19 @@
                     mOnHoldToneStarted = true;
                     mOnHoldToneId = System.identityHashCode(conn);
                 }
-
                 conn.onConnectionEvent(android.telecom.Connection.EVENT_CALL_REMOTELY_HELD, null);
+
+                boolean useVideoPauseWorkaround = mPhone.getContext().getResources().getBoolean(
+                        com.android.internal.R.bool.config_useVideoPauseWorkaround);
+                if (useVideoPauseWorkaround && mSupportPauseVideo &&
+                        VideoProfile.isVideo(conn.getVideoState())) {
+                    // If we are using the video pause workaround, the vendor IMS code has issues
+                    // with video pause signalling.  In this case, when a call is remotely
+                    // held, the modem does not reliably change the video state of the call to be
+                    // paused.
+                    // As a workaround, we will turn on that bit now.
+                    conn.changeToPausedState();
+                }
             }
 
             SuppServiceNotification supp = new SuppServiceNotification();
@@ -2292,6 +2479,7 @@
             ImsPhoneConnection conn = findConnection(call);
             if (conn != null) {
                 conn.onConferenceMergeFailed();
+                conn.onConnectionEvent(android.telecom.Connection.EVENT_MERGE_COMPLETE, null);
             }
         }
 
@@ -2322,7 +2510,7 @@
             ImsReasonInfo reasonInfo) {
             if (DBG) {
                 log("onCallHandover ::  srcAccessTech=" + srcAccessTech + ", targetAccessTech=" +
-                    targetAccessTech + ", reasonInfo=" + reasonInfo);
+                        targetAccessTech + ", reasonInfo=" + reasonInfo);
             }
 
             // Only consider it a valid handover to WIFI if the source radio tech is known.
@@ -2334,19 +2522,28 @@
                 removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER);
             }
 
-            // Only consider it a handover from WIFI if the source and target radio tech is known.
-            boolean isHandoverFromWifi = srcAccessTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
-                    && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN
-                    && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
-            if (mNotifyHandoverVideoFromWifiToLTE && isHandoverFromWifi && imsCall.isVideoCall()) {
-                log("onCallHandover :: notifying of WIFI to LTE handover.");
-                ImsPhoneConnection conn = findConnection(imsCall);
-                if (conn != null) {
-                    conn.onConnectionEvent(
-                            TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE, null);
-                } else {
-                    loge("onCallHandover :: failed to notify of handover; connection is null.");
+            ImsPhoneConnection conn = findConnection(imsCall);
+            if (conn != null) {
+                // Only consider it a handover from WIFI if the source and target radio tech is known.
+                boolean isHandoverFromWifi =
+                        srcAccessTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+                                && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN
+                                && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
+                if (isHandoverFromWifi && imsCall.isVideoCall()) {
+                    if (mNotifyHandoverVideoFromWifiToLTE && mIsDataEnabled) {
+                        log("onCallHandover :: notifying of WIFI to LTE handover.");
+                        conn.onConnectionEvent(
+                                TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE, null);
+                    }
+
+                    if (!mIsDataEnabled && mIsViLteDataMetered) {
+                        // Call was downgraded from WIFI to LTE and data is metered; downgrade the
+                        // call now.
+                        downgradeVideoCall(ImsReasonInfo.CODE_WIFI_LOST, conn);
+                    }
                 }
+            } else {
+                loge("onCallHandover :: connection null.");
             }
 
             mMetrics.writeOnImsCallHandoverEvent(mPhone.getPhoneId(),
@@ -2498,8 +2695,8 @@
     private ImsConnectionStateListener mImsConnectionStateListener =
         new ImsConnectionStateListener() {
         @Override
-        public void onImsConnected() {
-            if (DBG) log("onImsConnected");
+        public void onImsConnected(int imsRadioTech) {
+            if (DBG) log("onImsConnected imsRadioTech=" + imsRadioTech);
             mPhone.setServiceState(ServiceState.STATE_IN_SERVICE);
             mPhone.setImsRegistered(true);
             mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
@@ -2509,6 +2706,7 @@
         @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);
@@ -2517,8 +2715,8 @@
         }
 
         @Override
-        public void onImsProgressing() {
-            if (DBG) log("onImsProgressing");
+        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(),
@@ -2545,59 +2743,14 @@
         @Override
         public void onFeatureCapabilityChanged(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("onFeatureCapabilityChanged: ");
-                }
-                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 ");
-                        }
-
-                        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 ");
-                        }
-
-                        mImsFeatureEnabled[i] = false;
-                    } else {
-                        // Feature has unknown state; it is not its own value or -1.
-                        if (DBG) {
-                            loge("onFeatureCapabilityChanged(" + i + ", " + mImsFeatureStrings[i]
-                                    + "): unexpectedValue=" + enabledFeatures[i]);
-                        }
-                    }
-                }
-                if (DBG) {
-                    log(sb.toString());
-                }
-                if (tmpIsVideoCallEnabled != isVideoCallEnabled()) {
-                    mPhone.notifyForVideoCapabilityChanged(isVideoCallEnabled());
-                }
-
-                if (DBG) log("onFeatureCapabilityChanged: isVolteEnabled=" + isVolteEnabled()
-                            + ", isVideoCallEnabled=" + isVideoCallEnabled()
-                            + ", isVowifiEnabled=" + isVowifiEnabled()
-                            + ", isUtEnabled=" + isUtEnabled());
-
-                mPhone.onFeatureCapabilityChanged();
-
-                mMetrics.writeOnImsCapabilities(
-                        mPhone.getPhoneId(), mImsFeatureEnabled);
-            }
+            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
@@ -2746,13 +2899,9 @@
                 ImsCall call = (ImsCall) ar.userObj;
                 Long usage = (long) ar.result;
                 log("VT data usage update. usage = " + usage + ", imsCall = " + call);
-
-                Long oldUsage = 0L;
-                if (mVtDataUsageMap.containsKey(call.uniqueId)) {
-                    oldUsage = mVtDataUsageMap.get(call.uniqueId);
+                if (usage > 0) {
+                    updateVtDataUsage(call, usage);
                 }
-                mTotalVtDataUsage += (usage - oldUsage);
-                mVtDataUsageMap.put(call.uniqueId, usage);
                 break;
             case EVENT_DATA_ENABLED_CHANGED:
                 ar = (AsyncResult) msg.obj;
@@ -2781,9 +2930,76 @@
                     }
                 }
                 break;
+            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);
+                } finally {
+                    args.recycle();
+                }
+                break;
+            }
         }
     }
 
+    /**
+     * Update video call data usage
+     *
+     * @param call The IMS call
+     * @param dataUsage The aggregated data usage for the call
+     */
+    private void updateVtDataUsage(ImsCall call, long dataUsage) {
+        long oldUsage = 0L;
+        if (mVtDataUsageMap.containsKey(call.uniqueId)) {
+            oldUsage = mVtDataUsageMap.get(call.uniqueId);
+        }
+
+        long delta = dataUsage - oldUsage;
+        mVtDataUsageMap.put(call.uniqueId, dataUsage);
+
+        log("updateVtDataUsage: call=" + call + ", delta=" + delta);
+
+        long currentTime = SystemClock.elapsedRealtime();
+        int isRoaming = mPhone.getServiceState().getDataRoaming() ? 1 : 0;
+
+        // Create the snapshot of total video call data usage.
+        NetworkStats vtDataUsageSnapshot = new NetworkStats(currentTime, 1);
+        vtDataUsageSnapshot.combineAllValues(mVtDataUsageSnapshot);
+        // Since the modem only reports the total vt data usage rather than rx/tx separately,
+        // the only thing we can do here is splitting the usage into half rx and half tx.
+        // Uid -1 indicates this is for the overall device data usage.
+        vtDataUsageSnapshot.combineValues(new NetworkStats.Entry(
+                NetworkStatsService.VT_INTERFACE, -1, NetworkStats.SET_FOREGROUND,
+                NetworkStats.TAG_NONE, 1, isRoaming, delta / 2, 0, delta / 2, 0, 0));
+        mVtDataUsageSnapshot = vtDataUsageSnapshot;
+
+        // Create the snapshot of video call data usage per dialer. combineValues will create
+        // a separate entry if uid is different from the previous snapshot.
+        NetworkStats vtDataUsageUidSnapshot = new NetworkStats(currentTime, 1);
+        vtDataUsageUidSnapshot.combineAllValues(mVtDataUsageUidSnapshot);
+
+        // The dialer uid might not be initialized correctly during boot up due to telecom service
+        // not ready or its default dialer cache not ready. So we double check again here to see if
+        // default dialer uid is really not available.
+        if (mDefaultDialerUid.get() == NetworkStats.UID_ALL) {
+            final TelecomManager telecomManager =
+                    (TelecomManager) mPhone.getContext().getSystemService(Context.TELECOM_SERVICE);
+            mDefaultDialerUid.set(
+                    getPackageUid(mPhone.getContext(), telecomManager.getDefaultDialerPackage()));
+        }
+
+        // Since the modem only reports the total vt data usage rather than rx/tx separately,
+        // the only thing we can do here is splitting the usage into half rx and half tx.
+        vtDataUsageUidSnapshot.combineValues(new NetworkStats.Entry(
+                NetworkStatsService.VT_INTERFACE, mDefaultDialerUid.get(),
+                NetworkStats.SET_FOREGROUND, NetworkStats.TAG_NONE, 1, isRoaming, delta / 2,
+                0, delta / 2, 0, 0));
+        mVtDataUsageUidSnapshot = vtDataUsageUidSnapshot;
+    }
+
     @Override
     protected void log(String msg) {
         Rlog.d(LOG_TAG, "[ImsPhoneCallTracker] " + msg);
@@ -2839,10 +3055,9 @@
             pw.println(" " + mImsFeatureStrings[i] + ": "
                     + ((mImsFeatureEnabled[i]) ? "enabled" : "disabled"));
         }
-        pw.println(" mTotalVtDataUsage=" + mTotalVtDataUsage);
-        for (Map.Entry<Integer, Long> entry : mVtDataUsageMap.entrySet()) {
-            pw.println("    id=" + entry.getKey() + " ,usage=" + entry.getValue());
-        }
+        pw.println(" mDefaultDialerUid=" + mDefaultDialerUid.get());
+        pw.println(" mVtDataUsageSnapshot=" + mVtDataUsageSnapshot);
+        pw.println(" mVtDataUsageUidSnapshot=" + mVtDataUsageUidSnapshot);
 
         pw.flush();
         pw.println("++++++++++++++++++++++++++++++++");
@@ -2935,8 +3150,15 @@
         IImsVideoCallProvider imsVideoCallProvider =
                 imsCall.getCallSession().getVideoCallProvider();
         if (imsVideoCallProvider != null) {
+            // TODO: Remove this when we can better formalize the format of session modify requests.
+            boolean useVideoPauseWorkaround = mPhone.getContext().getResources().getBoolean(
+                    com.android.internal.R.bool.config_useVideoPauseWorkaround);
+
             ImsVideoCallProviderWrapper imsVideoCallProviderWrapper =
                     new ImsVideoCallProviderWrapper(imsVideoCallProvider);
+            if (useVideoPauseWorkaround) {
+                imsVideoCallProviderWrapper.setUseVideoPauseWorkaround(useVideoPauseWorkaround);
+            }
             conn.setVideoProvider(imsVideoCallProviderWrapper);
             imsVideoCallProviderWrapper.registerForDataUsageUpdate
                     (this, EVENT_VT_DATA_USAGE_UPDATE, imsCall);
@@ -3082,11 +3304,13 @@
         return isActiveCallVideo && isActiveCallOnWifi && isIncomingCallAudio && !isVoWifiEnabled;
     }
 
-    /** Get aggregated video call data usage since boot.
+    /**
+     * Get aggregated video call data usage since boot.
      *
-     * @return data usage in bytes
+     * @param perUidStats True if requesting data usage per uid, otherwise overall usage.
+     * @return Snapshot of video call data usage
      */
-    public long getVtDataUsage() {
+    public NetworkStats getVtDataUsage(boolean perUidStats) {
 
         // If there is an ongoing VT call, request the latest VT usage from the modem. The latest
         // usage will return asynchronously so it won't be counted in this round, but it will be
@@ -3100,7 +3324,7 @@
             }
         }
 
-        return mTotalVtDataUsage;
+        return perUidStats ? mVtDataUsageUidSnapshot : mVtDataUsageSnapshot;
     }
 
     public void registerPhoneStateListener(PhoneStateListener listener) {
@@ -3154,27 +3378,46 @@
         ImsManager.getInstance(mPhone.getContext(), mPhone.getPhoneId()).setDataEnabled(enabled);
         mIsDataEnabled = enabled;
 
-        if (mIgnoreDataEnabledChangedForVideoCalls) {
-            log("Ignore data " + ((enabled) ? "enabled" : "disabled") + " due to carrier policy.");
+        if (!mIsViLteDataMetered) {
+            log("Ignore data " + ((enabled) ? "enabled" : "disabled") + " - carrier policy "
+                    + "indicates that data is not metered for ViLTE calls.");
             return;
         }
 
-        if (mIgnoreDataEnabledChangedForVideoCalls) {
-            log("Ignore data " + ((enabled) ? "enabled" : "disabled") + " due to carrier policy.");
-            return;
+        // Inform connections that data has been disabled to ensure we turn off video capability
+        // if this is an LTE call.
+        for (ImsPhoneConnection conn : mConnections) {
+            conn.handleDataEnabledChange(enabled);
         }
 
+        int reasonCode;
+        if (reason == DataEnabledSettings.REASON_POLICY_DATA_ENABLED) {
+            reasonCode = ImsReasonInfo.CODE_DATA_LIMIT_REACHED;
+        } else if (reason == DataEnabledSettings.REASON_USER_DATA_ENABLED) {
+            reasonCode = ImsReasonInfo.CODE_DATA_DISABLED;
+        } else {
+            // Unexpected code, default to data disabled.
+            reasonCode = ImsReasonInfo.CODE_DATA_DISABLED;
+        }
+
+        // Potentially send connection events so the InCall UI knows that video calls are being
+        // downgraded due to data being enabled/disabled.
+        maybeNotifyDataDisabled(enabled, reasonCode);
+        // Handle video state changes required as a result of data being enabled/disabled.
+        handleDataEnabledChange(enabled, reasonCode);
+
+        // We do not want to update the ImsConfig for REASON_REGISTERED, since it can happen before
+        // the carrier config has loaded and will deregister IMS.
+        if (!mShouldUpdateImsConfigOnDisconnect
+                && reason != DataEnabledSettings.REASON_REGISTERED) {
+            // This will call into updateVideoCallFeatureValue and eventually all clients will be
+            // asynchronously notified that the availability of VT over LTE has changed.
+            ImsManager.updateImsServiceConfig(mPhone.getContext(), mPhone.getPhoneId(), true);
+        }
+    }
+
+    private void maybeNotifyDataDisabled(boolean enabled, int reasonCode) {
         if (!enabled) {
-            int reasonCode;
-            if (reason == DataEnabledSettings.REASON_POLICY_DATA_ENABLED) {
-                reasonCode = ImsReasonInfo.CODE_DATA_LIMIT_REACHED;
-            } else if (reason == DataEnabledSettings.REASON_USER_DATA_ENABLED) {
-                reasonCode = ImsReasonInfo.CODE_DATA_DISABLED;
-            } else {
-                // Unexpected code, default to data disabled.
-                reasonCode = ImsReasonInfo.CODE_DATA_DISABLED;
-            }
-
             // If data is disabled while there are ongoing VT calls which are not taking place over
             // wifi, then they should be disconnected to prevent the user from incurring further
             // data charges.
@@ -3194,27 +3437,38 @@
                             conn.onConnectionEvent(
                                     TelephonyManager.EVENT_DOWNGRADE_DATA_LIMIT_REACHED, null);
                         }
-                        modifyVideoCall(imsCall, VideoProfile.STATE_AUDIO_ONLY);
-                    } else if (mSupportPauseVideo) {
-                        // The carrier supports video pause signalling, so pause the video.
-                        mShouldUpdateImsConfigOnDisconnect = true;
-                        conn.pauseVideo(VideoPauseTracker.SOURCE_DATA_ENABLED);
-                    } else {
-                        // At this point the only choice we have is to terminate the call.
-                        try {
-                            imsCall.terminate(ImsReasonInfo.CODE_USER_TERMINATED, reasonCode);
-                        } catch (ImsException ie) {
-                            loge("Couldn't terminate call " + imsCall);
-                        }
                     }
                 }
             }
+        }
+    }
+
+    /**
+     * Handles changes to the enabled state of mobile data.
+     * When data is disabled, handles auto-downgrade of video calls over LTE.
+     * When data is enabled, handled resuming of video calls paused when data was disabled.
+     * @param enabled {@code true} if mobile data is enabled, {@code false} if mobile data is
+     *                            disabled.
+     * @param reasonCode The {@link ImsReasonInfo} code for the data enabled state change.
+     */
+    private void handleDataEnabledChange(boolean enabled, int reasonCode) {
+        if (!enabled) {
+            // If data is disabled while there are ongoing VT calls which are not taking place over
+            // wifi, then they should be disconnected to prevent the user from incurring further
+            // data charges.
+            for (ImsPhoneConnection conn : mConnections) {
+                ImsCall imsCall = conn.getImsCall();
+                if (imsCall != null && imsCall.isVideoCall() && !imsCall.isWifiCall()) {
+                    log("handleDataEnabledChange - downgrading " + conn);
+                    downgradeVideoCall(reasonCode, conn);
+                }
+            }
         } else if (mSupportPauseVideo) {
             // Data was re-enabled, so un-pause previously paused video calls.
             for (ImsPhoneConnection conn : mConnections) {
                 // If video is paused, check to see if there are any pending pauses due to enabled
                 // state of data changing.
-                log("onDataEnabledChanged - resuming " + conn);
+                log("handleDataEnabledChange - resuming " + conn);
                 if (VideoProfile.isPaused(conn.getVideoState()) &&
                         conn.wasVideoPausedFromSource(VideoPauseTracker.SOURCE_DATA_ENABLED)) {
                     // The data enabled state was a cause of a pending pause, so potentially
@@ -3224,11 +3478,47 @@
             }
             mShouldUpdateImsConfigOnDisconnect = false;
         }
+    }
 
-        if (!mShouldUpdateImsConfigOnDisconnect) {
-            // This will call into updateVideoCallFeatureValue and eventually all clients will be
-            // asynchronously notified that the availability of VT over LTE has changed.
-            ImsManager.updateImsServiceConfig(mPhone.getContext(), mPhone.getPhoneId(), true);
+    /**
+     * Handles downgrading a video call.  The behavior depends on carrier capabilities; we will
+     * attempt to take one of the following actions (in order of precedence):
+     * 1. If supported by the carrier, the call will be downgraded to an audio-only call.
+     * 2. If the carrier supports video pause signalling, the video will be paused.
+     * 3. The call will be disconnected.
+     * @param reasonCode The {@link ImsReasonInfo} reason code for the downgrade.
+     * @param conn The {@link ImsPhoneConnection} to downgrade.
+     */
+    private void downgradeVideoCall(int reasonCode, ImsPhoneConnection conn) {
+        ImsCall imsCall = conn.getImsCall();
+        if (imsCall != null) {
+            if (conn.hasCapabilities(
+                    Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_LOCAL |
+                            Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE)) {
+
+                // If the carrier supports downgrading to voice, then we can simply issue a
+                // downgrade to voice instead of terminating the call.
+                modifyVideoCall(imsCall, VideoProfile.STATE_AUDIO_ONLY);
+            } else if (mSupportPauseVideo && reasonCode != ImsReasonInfo.CODE_WIFI_LOST) {
+                // The carrier supports video pause signalling, so pause the video if we didn't just
+                // lose wifi; in that case just disconnect.
+                mShouldUpdateImsConfigOnDisconnect = true;
+                conn.pauseVideo(VideoPauseTracker.SOURCE_DATA_ENABLED);
+            } else {
+                // At this point the only choice we have is to terminate the call.
+                try {
+                    imsCall.terminate(ImsReasonInfo.CODE_USER_TERMINATED, reasonCode);
+                } catch (ImsException ie) {
+                    loge("Couldn't terminate call " + imsCall);
+                }
+            }
+        }
+    }
+
+    private void resetImsCapabilities() {
+        log("Resetting Capabilities...");
+        for (int i = 0; i < mImsFeatureEnabled.length; i++) {
+            mImsFeatureEnabled[i] = false;
         }
     }
 
@@ -3253,4 +3543,71 @@
     public boolean isCarrierDowngradeOfVtCallSupported() {
         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 ");
+                    }
+
+                    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 ");
+                    }
+
+                    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 (DBG) {
+                log(sb.toString());
+            }
+
+            if (DBG) log("handleFeatureCapabilityChanged: isVolteEnabled=" + isVolteEnabled()
+                    + ", isVideoCallEnabled=" + isVideoCallEnabled()
+                    + ", isVowifiEnabled=" + isVowifiEnabled()
+                    + ", isUtEnabled=" + isUtEnabled());
+
+            mPhone.onFeatureCapabilityChanged();
+
+            mMetrics.writeOnImsCapabilities(
+                    mPhone.getPhoneId(), mImsFeatureEnabled);
+        }
+    }
 }
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
index 0f6c68d..60a50b9 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
@@ -20,6 +20,8 @@
 import android.os.Handler;
 import android.os.Message;
 import android.service.carrier.CarrierIdentifier;
+import android.telephony.ImsiEncryptionInfo;
+import android.telephony.NetworkScanRequest;
 
 import com.android.internal.telephony.BaseCommands;
 import com.android.internal.telephony.CommandsInterface;
@@ -29,7 +31,6 @@
 import com.android.internal.telephony.dataconnection.DataProfile;
 import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
 
-import java.security.PublicKey;
 import java.util.List;
 
 /**
@@ -334,7 +335,7 @@
     }
 
     @Override
-    public void startNetworkScan(Message response) {
+    public void startNetworkScan(NetworkScanRequest nsr, Message response) {
     }
 
     @Override
@@ -624,7 +625,7 @@
     }
 
     @Override
-    public void setCarrierInfoForImsiEncryption(PublicKey publicKey, String keyIdentifier,
+    public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo,
                                                 Message result) {
     }
 
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
index 31e1f78..0ad81d2 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
@@ -107,6 +107,14 @@
     private ImsRttTextHandler mRttTextHandler;
     private android.telecom.Connection.RttTextStream mRttTextStream;
 
+    /**
+     * Used as an override to determine whether video is locally available for this call.
+     * This allows video availability to be overridden in the case that the modem says video is
+     * currently available, but mobile data is off and the carrier is metering data for video
+     * calls.
+     */
+    private boolean mIsVideoEnabled = true;
+
     //***** Event Constants
     private static final int EVENT_DTMF_DONE = 1;
     private static final int EVENT_PAUSE_DONE = 2;
@@ -242,11 +250,15 @@
         return (a == null) ? (b == null) : (b != null && a.startsWith (b));
     }
 
-    private static int applyLocalCallCapabilities(ImsCallProfile localProfile, int capabilities) {
-        Rlog.w(LOG_TAG, "applyLocalCallCapabilities - localProfile = "+localProfile);
+    private int applyLocalCallCapabilities(ImsCallProfile localProfile, int capabilities) {
+        Rlog.i(LOG_TAG, "applyLocalCallCapabilities - localProfile = " + localProfile);
         capabilities = removeCapability(capabilities,
                 Connection.Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL);
 
+        if (!mIsVideoEnabled) {
+            Rlog.i(LOG_TAG, "applyLocalCallCapabilities - disabling video (overidden)");
+            return capabilities;
+        }
         switch (localProfile.mCallType) {
             case ImsCallProfile.CALL_TYPE_VT:
                 // Fall-through
@@ -828,7 +840,7 @@
                     }
 
                     if (!mShouldIgnoreVideoStateChanges) {
-                        setVideoState(newVideoState);
+                        updateVideoState(newVideoState);
                         changed = true;
                     } else {
                         Rlog.d(LOG_TAG, "updateMediaCapabilities - ignoring video state change " +
@@ -891,6 +903,13 @@
         return changed;
     }
 
+    private void updateVideoState(int newVideoState) {
+        if (mImsVideoCallProviderWrapper != null) {
+            mImsVideoCallProviderWrapper.onVideoStateChanged(newVideoState);
+        }
+        setVideoState(newVideoState);
+    }
+
     public void sendRttModifyRequest(android.telecom.Connection.RttTextStream textStream) {
         getImsCall().sendRttModifyRequest();
         setCurrentRttTextStream(textStream);
@@ -1172,4 +1191,31 @@
 
         return mImsVideoCallProviderWrapper.wasVideoPausedFromSource(source);
     }
+
+    public void changeToPausedState() {
+        int newVideoState = getVideoState() | VideoProfile.STATE_PAUSED;
+        Rlog.i(LOG_TAG, "ImsPhoneConnection: changeToPausedState - setting paused bit; "
+                + "newVideoState=" + VideoProfile.videoStateToString(newVideoState));
+        updateVideoState(newVideoState);
+        mShouldIgnoreVideoStateChanges = true;
+    }
+
+    public void changeToUnPausedState() {
+        int newVideoState = getVideoState() & ~VideoProfile.STATE_PAUSED;
+        Rlog.i(LOG_TAG, "ImsPhoneConnection: changeToUnPausedState - unsetting paused bit; "
+                + "newVideoState=" + VideoProfile.videoStateToString(newVideoState));
+        updateVideoState(newVideoState);
+        mShouldIgnoreVideoStateChanges = false;
+    }
+
+    public void handleDataEnabledChange(boolean isDataEnabled) {
+        mIsVideoEnabled = isDataEnabled;
+        Rlog.i(LOG_TAG, "handleDataEnabledChange: isDataEnabled=" + isDataEnabled
+                + "; updating local video availability.");
+        updateMediaCapabilities(getImsCall());
+        if (mImsVideoCallProviderWrapper != null) {
+            mImsVideoCallProviderWrapper.setIsVideoEnabled(
+                    hasCapabilities(Connection.Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL));
+        }
+    }
 }
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
index 2387fb1..b1013b1 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
@@ -756,14 +756,14 @@
     processCode () throws CallStateException {
         try {
             if (isShortCode()) {
-                Rlog.d(LOG_TAG, "isShortCode");
+                Rlog.d(LOG_TAG, "processCode: isShortCode");
 
                 // These just get treated as USSD.
-                Rlog.d(LOG_TAG, "Sending short code '"
+                Rlog.d(LOG_TAG, "processCode: Sending short code '"
                        + mDialingNumber + "' over CS pipe.");
                 throw new CallStateException(Phone.CS_FALLBACK);
             } else if (isServiceCodeCallForwarding(mSc)) {
-                Rlog.d(LOG_TAG, "is CF");
+                Rlog.d(LOG_TAG, "processCode: is CF");
 
                 String dialingNumber = mSia;
                 int reason = scToCallForwardReason(mSc);
@@ -806,7 +806,7 @@
                         ((cfAction == CommandsInterface.CF_ACTION_ENABLE) ||
                                 (cfAction == CommandsInterface.CF_ACTION_REGISTRATION)) ? 1 : 0;
 
-                    Rlog.d(LOG_TAG, "is CF setCallForward");
+                    Rlog.d(LOG_TAG, "processCode: is CF setCallForward");
                     mPhone.setCallForwardingOption(cfAction, reason,
                             dialingNumber, serviceClass, time, obtainMessage(
                                     EVENT_SET_CFF_COMPLETE,
@@ -839,21 +839,21 @@
                         mPhone.mCT.getUtInterface().updateCLIR(CommandsInterface.CLIR_INVOCATION,
                             obtainMessage(EVENT_SET_COMPLETE, this));
                     } catch (ImsException e) {
-                        Rlog.d(LOG_TAG, "Could not get UT handle for updateCLIR.");
+                        Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCLIR.");
                     }
                 } else if (isDeactivate()) {
                     try {
                         mPhone.mCT.getUtInterface().updateCLIR(CommandsInterface.CLIR_SUPPRESSION,
                             obtainMessage(EVENT_SET_COMPLETE, this));
                     } catch (ImsException e) {
-                        Rlog.d(LOG_TAG, "Could not get UT handle for updateCLIR.");
+                        Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCLIR.");
                     }
                 } else if (isInterrogate()) {
                     try {
                         mPhone.mCT.getUtInterface()
                             .queryCLIR(obtainMessage(EVENT_GET_CLIR_COMPLETE, this));
                     } catch (ImsException e) {
-                        Rlog.d(LOG_TAG, "Could not get UT handle for queryCLIR.");
+                        Rlog.d(LOG_TAG, "processCode: Could not get UT handle for queryCLIR.");
                     }
                 } else {
                     throw new RuntimeException ("Invalid or Unsupported MMI Code");
@@ -865,14 +865,14 @@
                         mPhone.mCT.getUtInterface()
                             .queryCLIP(obtainMessage(EVENT_SUPP_SVC_QUERY_COMPLETE, this));
                     } catch (ImsException e) {
-                        Rlog.d(LOG_TAG, "Could not get UT handle for queryCLIP.");
+                        Rlog.d(LOG_TAG, "processCode: Could not get UT handle for queryCLIP.");
                     }
                 } else if (isActivate() || isDeactivate()) {
                     try {
                         mPhone.mCT.getUtInterface().updateCLIP(isActivate(),
                                 obtainMessage(EVENT_SET_COMPLETE, this));
                     } catch (ImsException e) {
-                        Rlog.d(LOG_TAG, "Could not get UT handle for updateCLIP.");
+                        Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCLIP.");
                     }
                 } else {
                     throw new RuntimeException ("Invalid or Unsupported MMI Code");
@@ -884,14 +884,14 @@
                         mPhone.mCT.getUtInterface()
                             .queryCOLP(obtainMessage(EVENT_SUPP_SVC_QUERY_COMPLETE, this));
                     } catch (ImsException e) {
-                        Rlog.d(LOG_TAG, "Could not get UT handle for queryCOLP.");
+                        Rlog.d(LOG_TAG, "processCode: Could not get UT handle for queryCOLP.");
                     }
                 } else if (isActivate() || isDeactivate()) {
                     try {
                         mPhone.mCT.getUtInterface().updateCOLP(isActivate(),
                                  obtainMessage(EVENT_SET_COMPLETE, this));
                      } catch (ImsException e) {
-                         Rlog.d(LOG_TAG, "Could not get UT handle for updateCOLP.");
+                         Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCOLP.");
                      }
                 } else {
                     throw new RuntimeException ("Invalid or Unsupported MMI Code");
@@ -903,21 +903,21 @@
                         mPhone.mCT.getUtInterface().updateCOLR(NUM_PRESENTATION_RESTRICTED,
                                 obtainMessage(EVENT_SET_COMPLETE, this));
                     } catch (ImsException e) {
-                        Rlog.d(LOG_TAG, "Could not get UT handle for updateCOLR.");
+                        Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCOLR.");
                     }
                 } else if (isDeactivate()) {
                     try {
                         mPhone.mCT.getUtInterface().updateCOLR(NUM_PRESENTATION_ALLOWED,
                                 obtainMessage(EVENT_SET_COMPLETE, this));
                     } catch (ImsException e) {
-                        Rlog.d(LOG_TAG, "Could not get UT handle for updateCOLR.");
+                        Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCOLR.");
                     }
                 } else if (isInterrogate()) {
                     try {
                         mPhone.mCT.getUtInterface()
                             .queryCOLR(obtainMessage(EVENT_SUPP_SVC_QUERY_COMPLETE, this));
                     } catch (ImsException e) {
-                        Rlog.d(LOG_TAG, "Could not get UT handle for queryCOLR.");
+                        Rlog.d(LOG_TAG, "processCode: Could not get UT handle for queryCOLR.");
                     }
                 } else {
                     throw new RuntimeException ("Invalid or Unsupported MMI Code");
@@ -933,7 +933,7 @@
                     }
                  // TODO: isRegister() case needs to be handled.
                 } catch (ImsException e) {
-                    Rlog.d(LOG_TAG, "Could not get UT handle for ICB.");
+                    Rlog.d(LOG_TAG, "processCode: Could not get UT handle for ICB.");
                 }
             } else if (mSc != null && mSc.equals(SC_BAICa)) {
                 int callAction =0;
@@ -956,7 +956,7 @@
                                 null);
                     }
                 } catch (ImsException e) {
-                    Rlog.d(LOG_TAG, "Could not get UT handle for ICBa.");
+                    Rlog.d(LOG_TAG, "processCode: Could not get UT handle for ICBa.");
                 }
             } else if (mSc != null && mSc.equals(SC_WAIT)) {
                 // sia = basic service group
@@ -971,15 +971,17 @@
                     throw new RuntimeException ("Invalid or Unsupported MMI Code");
                 }
             } else if (mPoundString != null) {
-                Rlog.d(LOG_TAG, "Sending pound string '"
+                Rlog.d(LOG_TAG, "processCode: Sending pound string '"
                        + mDialingNumber + "' over CS pipe.");
                 throw new CallStateException(Phone.CS_FALLBACK);
             } else {
+                Rlog.d(LOG_TAG, "processCode: invalid or unsupported MMI");
                 throw new RuntimeException ("Invalid or Unsupported MMI Code");
             }
         } catch (RuntimeException exc) {
             mState = State.FAILED;
             mMessage = mContext.getText(com.android.internal.R.string.mmiError);
+            Rlog.d(LOG_TAG, "processCode: RuntimeException = " + exc);
             mPhone.onMMIDone(this);
         }
     }
@@ -996,9 +998,11 @@
     void
     onUssdFinished(String ussdMessage, boolean isUssdRequest) {
         if (mState == State.PENDING) {
-            if (ussdMessage == null) {
+            if (TextUtils.isEmpty(ussdMessage)) {
                 mMessage = mContext.getText(com.android.internal.R.string.mmiComplete);
+                Rlog.v(LOG_TAG, "onUssdFinished: no message; using: " + mMessage);
             } else {
+                Rlog.v(LOG_TAG, "onUssdFinished: message: " + ussdMessage);
                 mMessage = ussdMessage;
             }
             mIsUssdRequest = isUssdRequest;
@@ -1006,7 +1010,6 @@
             if (!isUssdRequest) {
                 mState = State.COMPLETE;
             }
-
             mPhone.onMMIDone(this);
         }
     }
@@ -1022,7 +1025,7 @@
         if (mState == State.PENDING) {
             mState = State.FAILED;
             mMessage = mContext.getText(com.android.internal.R.string.mmiError);
-
+            Rlog.d(LOG_TAG, "onUssdFinishedError: mmi=" + this);
             mPhone.onMMIDone(this);
         }
     }
@@ -1140,7 +1143,7 @@
                                obtainMessage(EVENT_SET_COMPLETE,this),
                                icbNum);
         } catch (ImsException e) {
-            Rlog.d(LOG_TAG, "Could not get UT handle for updating ICB.");
+            Rlog.d(LOG_TAG, "processIcbMmiCodeForUpdate:Could not get UT handle for updating ICB.");
         }
     }
 
@@ -1230,6 +1233,7 @@
         }
 
         mMessage = sb;
+        Rlog.d(LOG_TAG, "onSetComplete: mmi=" + this);
         mPhone.onMMIDone(this);
     }
 
@@ -1384,6 +1388,7 @@
         }
 
         mMessage = sb;
+        Rlog.d(LOG_TAG, "onQueryCfComplete: mmi=" + this);
         mPhone.onMMIDone(this);
 
     }
@@ -1409,12 +1414,13 @@
             mState = State.FAILED;
             ImsSsInfo ssInfo = null;
             if (ar.result instanceof Bundle) {
-                Rlog.d(LOG_TAG, "Received CLIP/COLP/COLR Response.");
+                Rlog.d(LOG_TAG, "onSuppSvcQueryComplete: Received CLIP/COLP/COLR Response.");
                 // Response for CLIP, COLP and COLR queries.
                 Bundle ssInfoResp = (Bundle) ar.result;
                 ssInfo = (ImsSsInfo) ssInfoResp.getParcelable(UT_BUNDLE_KEY_SSINFO);
                 if (ssInfo != null) {
-                    Rlog.d(LOG_TAG, "ImsSsInfo mStatus = " + ssInfo.mStatus);
+                    Rlog.d(LOG_TAG,
+                            "onSuppSvcQueryComplete: ImsSsInfo mStatus = " + ssInfo.mStatus);
                     if (ssInfo.mStatus == ImsSsInfo.DISABLED) {
                         sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
                         mState = State.COMPLETE;
@@ -1429,7 +1435,7 @@
                 }
 
             } else {
-                Rlog.d(LOG_TAG, "Received Call Barring Response.");
+                Rlog.d(LOG_TAG, "onSuppSvcQueryComplete: Received Call Barring Response.");
                 // Response for Call Barring queries.
                 int[] cbInfos = (int[]) ar.result;
                 if (cbInfos[0] == 1) {
@@ -1443,11 +1449,12 @@
         }
 
         mMessage = sb;
+        Rlog.d(LOG_TAG, "onSuppSvcQueryComplete mmi=" + this);
         mPhone.onMMIDone(this);
     }
 
     private void onIcbQueryComplete(AsyncResult ar) {
-        Rlog.d(LOG_TAG, "onIcbQueryComplete ");
+        Rlog.d(LOG_TAG, "onIcbQueryComplete mmi=" + this);
         StringBuilder sb = new StringBuilder(getScString());
         sb.append("\n");
 
@@ -1508,7 +1515,7 @@
             int[] clirInfo = ssInfo.getIntArray(UT_BUNDLE_KEY_CLIR);
             // clirInfo[0] = The 'n' parameter from TS 27.007 7.7
             // clirInfo[1] = The 'm' parameter from TS 27.007 7.7
-            Rlog.d(LOG_TAG, "CLIR param n=" + clirInfo[0]
+            Rlog.d(LOG_TAG, "onQueryClirComplete: CLIR param n=" + clirInfo[0]
                     + " m=" + clirInfo[1]);
 
             // 'm' parameter.
@@ -1579,6 +1586,7 @@
         }
 
         mMessage = sb;
+        Rlog.d(LOG_TAG, "onQueryClirComplete mmi=" + this);
         mPhone.onMMIDone(this);
     }
 
@@ -1623,6 +1631,7 @@
         }
 
         mMessage = sb;
+        Rlog.d(LOG_TAG, "onQueryComplete mmi=" + this);
         mPhone.onMMIDone(this);
     }
 
@@ -1665,9 +1674,11 @@
         if (mSia != null) sb.append(" sia=" + mSia);
         if (mSib != null) sb.append(" sib=" + mSib);
         if (mSic != null) sb.append(" sic=" + mSic);
-        if (mPoundString != null) sb.append(" poundString=" + mPoundString);
-        if (mDialingNumber != null) sb.append(" dialingNumber=" + mDialingNumber);
-        if (mPwd != null) sb.append(" pwd=" + mPwd);
+        if (mPoundString != null) sb.append(" poundString=" + Rlog.pii(LOG_TAG, mPoundString));
+        if (mDialingNumber != null) sb.append(" dialingNumber="
+                + Rlog.pii(LOG_TAG, mDialingNumber));
+        if (mPwd != null) sb.append(" pwd=" + Rlog.pii(LOG_TAG, mPwd));
+        if (mCallbackReceiver != null) sb.append(" hasReceiver");
         sb.append("}");
         return sb.toString();
     }
diff --git a/src/java/com/android/internal/telephony/metrics/CallSessionEventBuilder.java b/src/java/com/android/internal/telephony/metrics/CallSessionEventBuilder.java
index 42d38a4..a8221b4 100644
--- a/src/java/com/android/internal/telephony/metrics/CallSessionEventBuilder.java
+++ b/src/java/com/android/internal/telephony/metrics/CallSessionEventBuilder.java
@@ -16,14 +16,14 @@
 
 package com.android.internal.telephony.metrics;
 
-import com.android.internal.telephony.TelephonyProto.ImsCapabilities;
-import com.android.internal.telephony.TelephonyProto.ImsConnectionState;
-import com.android.internal.telephony.TelephonyProto.ImsReasonInfo;
-import com.android.internal.telephony.TelephonyProto.RilDataCall;
-import com.android.internal.telephony.TelephonyProto.TelephonyCallSession;
-import com.android.internal.telephony.TelephonyProto.TelephonyCallSession.Event.RilCall;
-import com.android.internal.telephony.TelephonyProto.TelephonyServiceState;
-import com.android.internal.telephony.TelephonyProto.TelephonySettings;
+import com.android.internal.telephony.nano.TelephonyProto.ImsCapabilities;
+import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState;
+import com.android.internal.telephony.nano.TelephonyProto.ImsReasonInfo;
+import com.android.internal.telephony.nano.TelephonyProto.RilDataCall;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.RilCall;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyServiceState;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonySettings;
 
 public class CallSessionEventBuilder {
     private final TelephonyCallSession.Event mEvent = new TelephonyCallSession.Event();
@@ -33,46 +33,46 @@
     }
 
     public CallSessionEventBuilder(int type) {
-        mEvent.setType(type);
+        mEvent.type = type;
     }
 
     public CallSessionEventBuilder setDelay(int delay) {
-        mEvent.setDelay(delay);
+        mEvent.delay = delay;
         return this;
     }
 
     public CallSessionEventBuilder setRilRequest(int rilRequestType) {
-        mEvent.setRilRequest(rilRequestType);
+        mEvent.rilRequest = rilRequestType;
         return this;
     }
 
     public CallSessionEventBuilder setRilRequestId(int rilRequestId) {
-        mEvent.setRilRequestId(rilRequestId);
+        mEvent.rilRequestId = rilRequestId;
         return this;
     }
 
     public CallSessionEventBuilder setRilError(int rilError) {
-        mEvent.setError(rilError);
+        mEvent.error = rilError;
         return this;
     }
 
     public CallSessionEventBuilder setCallIndex(int callIndex) {
-        mEvent.setCallIndex(callIndex);
+        mEvent.callIndex = callIndex;
         return this;
     }
 
     public CallSessionEventBuilder setCallState(int state) {
-        mEvent.setCallState(state);
+        mEvent.callState = state;
         return this;
     }
 
     public CallSessionEventBuilder setSrvccState(int srvccState) {
-        mEvent.setSrvccState(srvccState);
+        mEvent.srvccState = srvccState;
         return this;
     }
 
     public CallSessionEventBuilder setImsCommand(int imsCommand) {
-        mEvent.setImsCommand(imsCommand);
+        mEvent.imsCommand = imsCommand;
         return this;
     }
 
@@ -82,12 +82,12 @@
     }
 
     public CallSessionEventBuilder setSrcAccessTech(int tech) {
-        mEvent.setSrcAccessTech(tech);
+        mEvent.srcAccessTech = tech;
         return this;
     }
 
     public CallSessionEventBuilder setTargetAccessTech(int tech) {
-        mEvent.setTargetAccessTech(tech);
+        mEvent.targetAccessTech = tech;
         return this;
     }
 
@@ -117,12 +117,12 @@
     }
 
     public CallSessionEventBuilder setPhoneState(int phoneState) {
-        mEvent.setPhoneState(phoneState);
+        mEvent.phoneState = phoneState;
         return this;
     }
 
     public CallSessionEventBuilder setNITZ(long timestamp) {
-        mEvent.setNitzTimestampMillis(timestamp);
+        mEvent.nitzTimestampMillis = timestamp;
         return this;
     }
 
diff --git a/src/java/com/android/internal/telephony/metrics/InProgressCallSession.java b/src/java/com/android/internal/telephony/metrics/InProgressCallSession.java
index 49a7ee4..748fe7d 100644
--- a/src/java/com/android/internal/telephony/metrics/InProgressCallSession.java
+++ b/src/java/com/android/internal/telephony/metrics/InProgressCallSession.java
@@ -18,7 +18,7 @@
 
 import android.os.SystemClock;
 
-import com.android.internal.telephony.TelephonyProto.TelephonyCallSession;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession;
 
 import java.util.ArrayDeque;
 import java.util.Deque;
@@ -47,6 +47,9 @@
     /** Indicating events dropped */
     private boolean mEventsDropped = false;
 
+    /** Last known phone state */
+    private int mLastKnownPhoneState;
+
     /** Check if events dropped */
     public boolean isEventsDropped() { return mEventsDropped; }
 
@@ -91,4 +94,34 @@
         events.add(builder.build());
         mLastElapsedTimeMs = timestamp;
     }
-}
\ No newline at end of file
+
+    /**
+     * Check if the Call Session contains CS calls
+     * @return true if there are CS calls in the call list
+     */
+    public boolean containsCsCalls() {
+        for (TelephonyCallSession.Event event : events) {
+            if (event.type == TelephonyCallSession.Event.Type.RIL_CALL_LIST_CHANGED) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Set Phone State
+     * @param state
+     */
+    public void setLastKnownPhoneState(int state) {
+        mLastKnownPhoneState = state;
+    }
+
+    /**
+     * Checks if Phone is in Idle state
+     * @return true if device is in Phone is idle state.
+     *
+     */
+    public boolean isPhoneIdle() {
+        return (mLastKnownPhoneState == TelephonyCallSession.Event.PhoneState.STATE_IDLE);
+    }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/InProgressSmsSession.java b/src/java/com/android/internal/telephony/metrics/InProgressSmsSession.java
index 4a01161..9da6536 100644
--- a/src/java/com/android/internal/telephony/metrics/InProgressSmsSession.java
+++ b/src/java/com/android/internal/telephony/metrics/InProgressSmsSession.java
@@ -18,7 +18,7 @@
 
 import android.os.SystemClock;
 
-import com.android.internal.telephony.TelephonyProto.SmsSession;
+import com.android.internal.telephony.nano.TelephonyProto.SmsSession;
 
 import java.util.ArrayDeque;
 import java.util.Deque;
diff --git a/src/java/com/android/internal/telephony/metrics/SmsSessionEventBuilder.java b/src/java/com/android/internal/telephony/metrics/SmsSessionEventBuilder.java
index aeaac19..530be1d 100644
--- a/src/java/com/android/internal/telephony/metrics/SmsSessionEventBuilder.java
+++ b/src/java/com/android/internal/telephony/metrics/SmsSessionEventBuilder.java
@@ -16,12 +16,12 @@
 
 package com.android.internal.telephony.metrics;
 
-import com.android.internal.telephony.TelephonyProto.ImsCapabilities;
-import com.android.internal.telephony.TelephonyProto.ImsConnectionState;
-import com.android.internal.telephony.TelephonyProto.RilDataCall;
-import com.android.internal.telephony.TelephonyProto.SmsSession;
-import com.android.internal.telephony.TelephonyProto.TelephonyServiceState;
-import com.android.internal.telephony.TelephonyProto.TelephonySettings;
+import com.android.internal.telephony.nano.TelephonyProto.ImsCapabilities;
+import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState;
+import com.android.internal.telephony.nano.TelephonyProto.RilDataCall;
+import com.android.internal.telephony.nano.TelephonyProto.SmsSession;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyServiceState;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonySettings;
 
 public class SmsSessionEventBuilder {
     SmsSession.Event mEvent = new SmsSession.Event();
@@ -31,26 +31,26 @@
     }
 
     public SmsSessionEventBuilder(int type) {
-        mEvent.setType(type);
+        mEvent.type = type;
     }
 
     public SmsSessionEventBuilder setDelay(int delay) {
-        mEvent.setDelay(delay);
+        mEvent.delay = delay;
         return this;
     }
 
     public SmsSessionEventBuilder setTech(int tech) {
-        mEvent.setTech(tech);
+        mEvent.tech = tech;
         return this;
     }
 
     public SmsSessionEventBuilder setErrorCode(int code) {
-        mEvent.setErrorCode(code);
+        mEvent.errorCode = code;
         return this;
     }
 
     public SmsSessionEventBuilder setRilErrno(int errno) {
-        mEvent.setError(errno);
+        mEvent.error = errno;
         return this;
     }
 
@@ -80,12 +80,12 @@
     }
 
     public SmsSessionEventBuilder setRilRequestId(int id) {
-        mEvent.setRilRequestId(id);
+        mEvent.rilRequestId = id;
         return this;
     }
 
     public SmsSessionEventBuilder setFormat(int format) {
-        mEvent.setFormat(format);
+        mEvent.format = format;
         return this;
     }
 }
diff --git a/src/java/com/android/internal/telephony/metrics/TelephonyEventBuilder.java b/src/java/com/android/internal/telephony/metrics/TelephonyEventBuilder.java
index 80c37ad..6530802 100644
--- a/src/java/com/android/internal/telephony/metrics/TelephonyEventBuilder.java
+++ b/src/java/com/android/internal/telephony/metrics/TelephonyEventBuilder.java
@@ -16,16 +16,16 @@
 
 package com.android.internal.telephony.metrics;
 
-import static com.android.internal.telephony.TelephonyProto.ImsCapabilities;
-import static com.android.internal.telephony.TelephonyProto.ImsConnectionState;
-import static com.android.internal.telephony.TelephonyProto.RilDataCall;
-import static com.android.internal.telephony.TelephonyProto.TelephonyEvent;
-import static com.android.internal.telephony.TelephonyProto.TelephonyEvent.ModemRestart;
-import static com.android.internal.telephony.TelephonyProto.TelephonyEvent.RilDeactivateDataCall;
-import static com.android.internal.telephony.TelephonyProto.TelephonyEvent.RilSetupDataCall;
-import static com.android.internal.telephony.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse;
-import static com.android.internal.telephony.TelephonyProto.TelephonyServiceState;
-import static com.android.internal.telephony.TelephonyProto.TelephonySettings;
+import static com.android.internal.telephony.nano.TelephonyProto.ImsCapabilities;
+import static com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState;
+import static com.android.internal.telephony.nano.TelephonyProto.RilDataCall;
+import static com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent;
+import static com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.ModemRestart;
+import static com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilDeactivateDataCall;
+import static com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCall;
+import static com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse;
+import static com.android.internal.telephony.nano.TelephonyProto.TelephonyServiceState;
+import static com.android.internal.telephony.nano.TelephonyProto.TelephonySettings;
 
 import android.os.SystemClock;
 
@@ -41,78 +41,78 @@
     }
 
     public TelephonyEventBuilder(long timestamp, int phoneId) {
-        mEvent.setTimestampMillis(timestamp);
-        mEvent.setPhoneId(phoneId);
+        mEvent.timestampMillis = timestamp;
+        mEvent.phoneId = phoneId;
     }
 
     public TelephonyEventBuilder setSettings(TelephonySettings settings) {
-        mEvent.setType(TelephonyEvent.Type.SETTINGS_CHANGED);
+        mEvent.type = TelephonyEvent.Type.SETTINGS_CHANGED;
         mEvent.settings = settings;
         return this;
     }
 
     public TelephonyEventBuilder setServiceState(TelephonyServiceState state) {
-        mEvent.setType(TelephonyEvent.Type.RIL_SERVICE_STATE_CHANGED);
+        mEvent.type = TelephonyEvent.Type.RIL_SERVICE_STATE_CHANGED;
         mEvent.serviceState = state;
         return this;
     }
 
     public TelephonyEventBuilder setImsConnectionState(ImsConnectionState state) {
-        mEvent.setType(TelephonyEvent.Type.IMS_CONNECTION_STATE_CHANGED);
+        mEvent.type = TelephonyEvent.Type.IMS_CONNECTION_STATE_CHANGED;
         mEvent.imsConnectionState = state;
         return this;
     }
 
     public TelephonyEventBuilder setImsCapabilities(ImsCapabilities capabilities) {
-        mEvent.setType(TelephonyEvent.Type.IMS_CAPABILITIES_CHANGED);
+        mEvent.type = TelephonyEvent.Type.IMS_CAPABILITIES_CHANGED;
         mEvent.imsCapabilities = capabilities;
         return this;
     }
 
     public TelephonyEventBuilder setDataStallRecoveryAction(int action) {
-        mEvent.setType(TelephonyEvent.Type.DATA_STALL_ACTION);
-        mEvent.setDataStallAction(action);
+        mEvent.type = TelephonyEvent.Type.DATA_STALL_ACTION;
+        mEvent.dataStallAction = action;
         return this;
     }
 
     public TelephonyEventBuilder setSetupDataCall(RilSetupDataCall request) {
-        mEvent.setType(TelephonyEvent.Type.DATA_CALL_SETUP);
+        mEvent.type = TelephonyEvent.Type.DATA_CALL_SETUP;
         mEvent.setupDataCall = request;
         return this;
     }
 
     public TelephonyEventBuilder setSetupDataCallResponse(RilSetupDataCallResponse rsp) {
-        mEvent.setType(TelephonyEvent.Type.DATA_CALL_SETUP_RESPONSE);
+        mEvent.type = TelephonyEvent.Type.DATA_CALL_SETUP_RESPONSE;
         mEvent.setupDataCallResponse = rsp;
         return this;
     }
 
     public TelephonyEventBuilder setDeactivateDataCall(RilDeactivateDataCall request) {
-        mEvent.setType(TelephonyEvent.Type.DATA_CALL_DEACTIVATE);
+        mEvent.type = TelephonyEvent.Type.DATA_CALL_DEACTIVATE;
         mEvent.deactivateDataCall = request;
         return this;
     }
 
     public TelephonyEventBuilder setDeactivateDataCallResponse(int errno) {
-        mEvent.setType(TelephonyEvent.Type.DATA_CALL_DEACTIVATE_RESPONSE);
-        mEvent.setError(errno);
+        mEvent.type = TelephonyEvent.Type.DATA_CALL_DEACTIVATE_RESPONSE;
+        mEvent.error = errno;
         return this;
     }
 
     public TelephonyEventBuilder setDataCalls(RilDataCall[] dataCalls) {
-        mEvent.setType(TelephonyEvent.Type.DATA_CALL_LIST_CHANGED);
+        mEvent.type = TelephonyEvent.Type.DATA_CALL_LIST_CHANGED;
         mEvent.dataCalls = dataCalls;
         return this;
     }
 
     public TelephonyEventBuilder setNITZ(long timestamp) {
-        mEvent.setType(TelephonyEvent.Type.NITZ_TIME);
-        mEvent.setNitzTimestampMillis(timestamp);
+        mEvent.type = TelephonyEvent.Type.NITZ_TIME;
+        mEvent.nitzTimestampMillis = timestamp;
         return this;
     }
 
     public TelephonyEventBuilder setModemRestart(ModemRestart modemRestart) {
-        mEvent.setType(TelephonyEvent.Type.MODEM_RESTART);
+        mEvent.type = TelephonyEvent.Type.MODEM_RESTART;
         mEvent.modemRestart = modemRestart;
         return this;
     }
diff --git a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
index 45df5ae..4c642c0 100644
--- a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
+++ b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
@@ -23,23 +23,25 @@
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DEACTIVATE_DATA_CALL;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DIAL;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP;
-import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND;
+import static com.android.internal.telephony.RILConstants
+        .RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IMS_SEND_SMS;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS_EXPECT_MORE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SETUP_DATA_CALL;
-import static com.android.internal.telephony.TelephonyProto.PdpType.PDP_TYPE_IP;
-import static com.android.internal.telephony.TelephonyProto.PdpType.PDP_TYPE_IPV4V6;
-import static com.android.internal.telephony.TelephonyProto.PdpType.PDP_TYPE_IPV6;
-import static com.android.internal.telephony.TelephonyProto.PdpType.PDP_TYPE_PPP;
-import static com.android.internal.telephony.TelephonyProto.PdpType.PDP_UNKNOWN;
+import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_IP;
+import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_IPV4V6;
+import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_IPV6;
+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.os.Build;
 import android.os.SystemClock;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyHistogram;
+import android.text.TextUtils;
 import android.util.Base64;
 import android.util.SparseArray;
 
@@ -51,34 +53,36 @@
 import com.android.internal.telephony.RIL;
 import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.SmsResponse;
-import com.android.internal.telephony.TelephonyProto;
-import com.android.internal.telephony.TelephonyProto.ImsCapabilities;
-import com.android.internal.telephony.TelephonyProto.ImsConnectionState;
-import com.android.internal.telephony.TelephonyProto.RilDataCall;
-import com.android.internal.telephony.TelephonyProto.SmsSession;
-import com.android.internal.telephony.TelephonyProto.TelephonyCallSession;
-import com.android.internal.telephony.TelephonyProto.TelephonyCallSession.Event.CallState.*;
-import com.android.internal.telephony.TelephonyProto.TelephonyCallSession.Event.RilCall;
-import com.android.internal.telephony.TelephonyProto.TelephonyCallSession.Event.RilCall.Type.*;
-import com.android.internal.telephony.TelephonyProto.TelephonyEvent;
-import com.android.internal.telephony.TelephonyProto.TelephonyEvent.ModemRestart;
-import com.android.internal.telephony.TelephonyProto.TelephonyEvent.RilDeactivateDataCall;
-import com.android.internal.telephony.TelephonyProto.TelephonyEvent.RilSetupDataCall;
-import com.android.internal.telephony.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse;
-import com.android.internal.telephony.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse.RilDataCallFailCause;
-import com.android.internal.telephony.TelephonyProto.TelephonyLog;
-import com.android.internal.telephony.TelephonyProto.TelephonyServiceState;
-import com.android.internal.telephony.TelephonyProto.TelephonySettings;
-import com.android.internal.telephony.TelephonyProto.TimeInterval;
 import com.android.internal.telephony.UUSInfo;
 import com.android.internal.telephony.dataconnection.DataCallResponse;
 import com.android.internal.telephony.imsphone.ImsPhoneCall;
+import com.android.internal.telephony.nano.TelephonyProto;
+import com.android.internal.telephony.nano.TelephonyProto.ImsCapabilities;
+import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState;
+import com.android.internal.telephony.nano.TelephonyProto.RilDataCall;
+import com.android.internal.telephony.nano.TelephonyProto.SmsSession;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.CallState;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.RilCall;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.RilCall.Type;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.ModemRestart;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilDeactivateDataCall;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCall;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse
+        .RilDataCallFailCause;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyLog;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyServiceState;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonySettings;
+import com.android.internal.telephony.nano.TelephonyProto.TimeInterval;
 import com.android.internal.util.IndentingPrintWriter;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Deque;
 import java.util.List;
 
@@ -142,6 +146,11 @@
      */
     private final SparseArray<ImsConnectionState> mLastImsConnectionState = new SparseArray<>();
 
+    /**
+     * Last settings state. This is for deduping same settings event logged.
+     */
+    private final SparseArray<TelephonySettings> mLastSettings = new SparseArray<>();
+
     /** The start system time of the TelephonyLog in milliseconds*/
     private long mStartSystemTimeMs;
 
@@ -326,23 +335,20 @@
         pw.println("Telephony events:");
         pw.increaseIndent();
         for (TelephonyEvent event : mTelephonyEvents) {
-            if (event.hasTimestampMillis()) {
-                pw.print(event.getTimestampMillis());
-                pw.print(" [");
-                if (event.hasPhoneId()) pw.print(event.getPhoneId());
-                pw.print("] ");
+            pw.print(event.timestampMillis);
+            pw.print(" [");
+            pw.print(event.phoneId);
+            pw.print("] ");
 
-                if (event.hasType()) {
-                    pw.print("T=");
-                    if (event.getType() == TelephonyEvent.Type.RIL_SERVICE_STATE_CHANGED) {
-                        pw.print(telephonyEventToString(event.getType())
-                                + "(" + event.serviceState.getDataRat() + ")");
-                    } else {
-                        pw.print(telephonyEventToString(event.getType()));
-                    }
-                }
-                pw.println("");
+            pw.print("T=");
+            if (event.type == TelephonyEvent.Type.RIL_SERVICE_STATE_CHANGED) {
+                pw.print(telephonyEventToString(event.type)
+                        + "(" + event.serviceState.dataRat + ")");
+            } else {
+                pw.print(telephonyEventToString(event.type));
             }
+
+            pw.println("");
         }
 
         pw.decreaseIndent();
@@ -350,22 +356,28 @@
         pw.increaseIndent();
 
         for (TelephonyCallSession callSession : mCompletedCallSessions) {
-            if (callSession.hasStartTimeMinutes()) {
-                pw.println("Start time in minutes: " + callSession.getStartTimeMinutes());
-            }
-            if (callSession.hasEventsDropped()) {
-                pw.println("Events dropped: " + callSession.getEventsDropped());
-            }
+            pw.println("Start time in minutes: " + callSession.startTimeMinutes);
+            pw.println("Events dropped: " + callSession.eventsDropped);
+
             pw.println("Events: ");
             pw.increaseIndent();
             for (TelephonyCallSession.Event event : callSession.events) {
-                pw.print(event.getDelay());
+                pw.print(event.delay);
                 pw.print(" T=");
-                if (event.getType() == TelephonyCallSession.Event.Type.RIL_SERVICE_STATE_CHANGED) {
-                    pw.println(callSessionEventToString(event.getType())
-                            + "(" + event.serviceState.getDataRat() + ")");
+                if (event.type == TelephonyCallSession.Event.Type.RIL_SERVICE_STATE_CHANGED) {
+                    pw.println(callSessionEventToString(event.type)
+                            + "(" + event.serviceState.dataRat + ")");
+                } else if (event.type == TelephonyCallSession.Event.Type.RIL_CALL_LIST_CHANGED) {
+                    pw.println(callSessionEventToString(event.type));
+                    pw.increaseIndent();
+                    for (RilCall call : event.calls) {
+                        pw.println(call.index + ". Type = " + call.type + " State = "
+                                + call.state + " End Reason " + call.callEndReason
+                                + " isMultiparty = " + call.isMultiparty);
+                    }
+                    pw.decreaseIndent();
                 } else {
-                    pw.println(callSessionEventToString(event.getType()));
+                    pw.println(callSessionEventToString(event.type));
                 }
             }
             pw.decreaseIndent();
@@ -378,19 +390,18 @@
         int count = 0;
         for (SmsSession smsSession : mCompletedSmsSessions) {
             count++;
-            if (smsSession.hasStartTimeMinutes()) {
-                pw.print("[" + count + "] Start time in minutes: "
-                        + smsSession.getStartTimeMinutes());
-            }
-            if (smsSession.hasEventsDropped()) {
-                pw.println(", events dropped: " + smsSession.getEventsDropped());
+            pw.print("[" + count + "] Start time in minutes: "
+                    + smsSession.startTimeMinutes);
+
+            if (smsSession.eventsDropped) {
+                pw.println(", events dropped: " + smsSession.eventsDropped);
             }
             pw.println("Events: ");
             pw.increaseIndent();
             for (SmsSession.Event event : smsSession.events) {
-                pw.print(event.getDelay());
+                pw.print(event.delay);
                 pw.print(" T=");
-                pw.println(smsSessionEventToString(event.getType()));
+                pw.println(smsSessionEventToString(event.type));
             }
             pw.decreaseIndent();
         }
@@ -460,7 +471,7 @@
         // Build telephony events
         log.events = new TelephonyEvent[mTelephonyEvents.size()];
         mTelephonyEvents.toArray(log.events);
-        log.setEventsDropped(mTelephonyEventsDropped);
+        log.eventsDropped = mTelephonyEventsDropped;
 
         // Build call sessions
         log.callSessions = new TelephonyCallSession[mCompletedCallSessions.size()];
@@ -478,25 +489,25 @@
             TelephonyHistogram rilHistogram = rilHistograms.get(i);
             TelephonyProto.TelephonyHistogram histogramProto = log.histograms[i];
 
-            histogramProto.setCategory(rilHistogram.getCategory());
-            histogramProto.setId(rilHistogram.getId());
-            histogramProto.setMinTimeMillis(rilHistogram.getMinTime());
-            histogramProto.setMaxTimeMillis(rilHistogram.getMaxTime());
-            histogramProto.setAvgTimeMillis(rilHistogram.getAverageTime());
-            histogramProto.setCount(rilHistogram.getSampleCount());
-            histogramProto.setBucketCount(rilHistogram.getBucketCount());
+            histogramProto.category = rilHistogram.getCategory();
+            histogramProto.id = rilHistogram.getId();
+            histogramProto.minTimeMillis = rilHistogram.getMinTime();
+            histogramProto.maxTimeMillis = rilHistogram.getMaxTime();
+            histogramProto.avgTimeMillis = rilHistogram.getAverageTime();
+            histogramProto.count = rilHistogram.getSampleCount();
+            histogramProto.bucketCount = rilHistogram.getBucketCount();
             histogramProto.bucketEndPoints = rilHistogram.getBucketEndPoints();
             histogramProto.bucketCounters = rilHistogram.getBucketCounters();
         }
 
         // Log the starting system time
         log.startTime = new TelephonyProto.Time();
-        log.startTime.setSystemTimestampMillis(mStartSystemTimeMs);
-        log.startTime.setElapsedTimestampMillis(mStartElapsedTimeMs);
+        log.startTime.systemTimestampMillis = mStartSystemTimeMs;
+        log.startTime.elapsedTimestampMillis = mStartElapsedTimeMs;
 
         log.endTime = new TelephonyProto.Time();
-        log.endTime.setSystemTimestampMillis(System.currentTimeMillis());
-        log.endTime.setElapsedTimestampMillis(SystemClock.elapsedRealtime());
+        log.endTime.systemTimestampMillis = System.currentTimeMillis();
+        log.endTime.elapsedTimestampMillis = SystemClock.elapsedRealtime();
 
         return log;
     }
@@ -573,39 +584,39 @@
     private TelephonyServiceState toServiceStateProto(ServiceState serviceState) {
         TelephonyServiceState ssProto = new TelephonyServiceState();
 
-        ssProto.setVoiceRoamingType(serviceState.getVoiceRoamingType());
-        ssProto.setDataRoamingType(serviceState.getDataRoamingType());
+        ssProto.voiceRoamingType = serviceState.getVoiceRoamingType();
+        ssProto.dataRoamingType = serviceState.getDataRoamingType();
 
         ssProto.voiceOperator = new TelephonyServiceState.TelephonyOperator();
 
         if (serviceState.getVoiceOperatorAlphaLong() != null) {
-            ssProto.voiceOperator.setAlphaLong(serviceState.getVoiceOperatorAlphaLong());
+            ssProto.voiceOperator.alphaLong = serviceState.getVoiceOperatorAlphaLong();
         }
 
         if (serviceState.getVoiceOperatorAlphaShort() != null) {
-            ssProto.voiceOperator.setAlphaShort(serviceState.getVoiceOperatorAlphaShort());
+            ssProto.voiceOperator.alphaShort = serviceState.getVoiceOperatorAlphaShort();
         }
 
         if (serviceState.getVoiceOperatorNumeric() != null) {
-            ssProto.voiceOperator.setNumeric(serviceState.getVoiceOperatorNumeric());
+            ssProto.voiceOperator.numeric = serviceState.getVoiceOperatorNumeric();
         }
 
         ssProto.dataOperator = new TelephonyServiceState.TelephonyOperator();
 
         if (serviceState.getDataOperatorAlphaLong() != null) {
-            ssProto.dataOperator.setAlphaLong(serviceState.getDataOperatorAlphaLong());
+            ssProto.dataOperator.alphaLong = serviceState.getDataOperatorAlphaLong();
         }
 
         if (serviceState.getDataOperatorAlphaShort() != null) {
-            ssProto.dataOperator.setAlphaShort(serviceState.getDataOperatorAlphaShort());
+            ssProto.dataOperator.alphaShort = serviceState.getDataOperatorAlphaShort();
         }
 
         if (serviceState.getDataOperatorNumeric() != null) {
-            ssProto.dataOperator.setNumeric(serviceState.getDataOperatorNumeric());
+            ssProto.dataOperator.numeric = serviceState.getDataOperatorNumeric();
         }
 
-        ssProto.setVoiceRat(serviceState.getRilVoiceRadioTechnology());
-        ssProto.setDataRat(serviceState.getRilDataRadioTechnology());
+        ssProto.voiceRat = serviceState.getRilVoiceRadioTechnology();
+        ssProto.dataRat = serviceState.getRilDataRadioTechnology();
         return ssProto;
     }
 
@@ -726,9 +737,9 @@
         TelephonyCallSession callSession = new TelephonyCallSession();
         callSession.events = new TelephonyCallSession.Event[inProgressCallSession.events.size()];
         inProgressCallSession.events.toArray(callSession.events);
-        callSession.setStartTimeMinutes(inProgressCallSession.startSystemTimeMin);
-        callSession.setPhoneId(inProgressCallSession.phoneId);
-        callSession.setEventsDropped(inProgressCallSession.isEventsDropped());
+        callSession.startTimeMinutes = inProgressCallSession.startSystemTimeMin;
+        callSession.phoneId = inProgressCallSession.phoneId;
+        callSession.eventsDropped = inProgressCallSession.isEventsDropped();
         if (mCompletedCallSessions.size() >= MAX_COMPLETED_CALL_SESSIONS) {
             mCompletedCallSessions.removeFirst();
         }
@@ -747,9 +758,9 @@
             SmsSession smsSession = new SmsSession();
             smsSession.events = new SmsSession.Event[inProgressSmsSession.events.size()];
             inProgressSmsSession.events.toArray(smsSession.events);
-            smsSession.setStartTimeMinutes(inProgressSmsSession.startSystemTimeMin);
-            smsSession.setPhoneId(inProgressSmsSession.phoneId);
-            smsSession.setEventsDropped(inProgressSmsSession.isEventsDropped());
+            smsSession.startTimeMinutes = inProgressSmsSession.startSystemTimeMin;
+            smsSession.phoneId = inProgressSmsSession.phoneId;
+            smsSession.eventsDropped = inProgressSmsSession.isEventsDropped();
             if (mCompletedSmsSessions.size() >= MAX_COMPLETED_SMS_SESSIONS) {
                 mCompletedSmsSessions.removeFirst();
             }
@@ -783,14 +794,21 @@
         TelephonyEvent event = new TelephonyEventBuilder(phoneId)
                 .setServiceState(toServiceStateProto(serviceState)).build();
 
+        // If service state doesn't change, we don't log the event.
+        if (mLastServiceState.get(phoneId) != null &&
+                Arrays.equals(TelephonyServiceState.toByteArray(mLastServiceState.get(phoneId)),
+                        TelephonyServiceState.toByteArray(event.serviceState))) {
+            return;
+        }
+
         mLastServiceState.put(phoneId, event.serviceState);
         addTelephonyEvent(event);
 
-        annotateInProgressCallSession(event.getTimestampMillis(), phoneId,
+        annotateInProgressCallSession(event.timestampMillis, phoneId,
                 new CallSessionEventBuilder(
                         TelephonyCallSession.Event.Type.RIL_SERVICE_STATE_CHANGED)
                         .setServiceState(event.serviceState));
-        annotateInProgressSmsSession(event.getTimestampMillis(), phoneId,
+        annotateInProgressSmsSession(event.timestampMillis, phoneId,
                 new SmsSessionEventBuilder(
                         SmsSession.Event.Type.RIL_SERVICE_STATE_CHANGED)
                         .setServiceState(event.serviceState));
@@ -821,26 +839,35 @@
         TelephonySettings s = new TelephonySettings();
         switch (feature) {
             case ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE:
-                s.setIsEnhanced4GLteModeEnabled(value != 0);
+                s.isEnhanced4GLteModeEnabled = (value != 0);
                 break;
             case ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI:
-                s.setIsWifiCallingEnabled(value != 0);
+                s.isWifiCallingEnabled = (value != 0);
                 break;
             case ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE:
-                s.setIsVtOverLteEnabled(value != 0);
+                s.isVtOverLteEnabled = (value != 0);
                 break;
             case ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_WIFI:
-                s.setIsVtOverWifiEnabled(value != 0);
+                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)),
+                        TelephonySettings.toByteArray(s))) {
+            return;
+        }
+
+        mLastSettings.put(phoneId, s);
+
         TelephonyEvent event = new TelephonyEventBuilder(phoneId).setSettings(s).build();
         addTelephonyEvent(event);
 
-        annotateInProgressCallSession(event.getTimestampMillis(), phoneId,
+        annotateInProgressCallSession(event.timestampMillis, phoneId,
                 new CallSessionEventBuilder(TelephonyCallSession.Event.Type.SETTINGS_CHANGED)
                         .setSettings(s));
-        annotateInProgressSmsSession(event.getTimestampMillis(), phoneId,
+        annotateInProgressSmsSession(event.timestampMillis, phoneId,
                 new SmsSessionEventBuilder(SmsSession.Event.Type.SETTINGS_CHANGED)
                         .setSettings(s));
     }
@@ -853,7 +880,17 @@
      */
     public void writeSetPreferredNetworkType(int phoneId, int networkType) {
         TelephonySettings s = new TelephonySettings();
-        s.setPreferredNetworkMode(networkType);
+        s.preferredNetworkMode = networkType + 1;
+
+        // If the settings don't change, we don't log the event.
+        if (mLastSettings.get(phoneId) != null &&
+                Arrays.equals(TelephonySettings.toByteArray(mLastSettings.get(phoneId)),
+                        TelephonySettings.toByteArray(s))) {
+            return;
+        }
+
+        mLastSettings.put(phoneId, s);
+
         addTelephonyEvent(new TelephonyEventBuilder(phoneId).setSettings(s).build());
     }
 
@@ -867,31 +904,39 @@
     public synchronized void writeOnImsConnectionState(int phoneId, int state,
                                                        ImsReasonInfo reasonInfo) {
         ImsConnectionState imsState = new ImsConnectionState();
-        imsState.setState(state);
-        mLastImsConnectionState.put(phoneId, imsState);
+        imsState.state = state;
 
         if (reasonInfo != null) {
             TelephonyProto.ImsReasonInfo ri = new TelephonyProto.ImsReasonInfo();
 
-            ri.setReasonCode(reasonInfo.getCode());
-            ri.setExtraCode(reasonInfo.getExtraCode());
+            ri.reasonCode = reasonInfo.getCode();
+            ri.extraCode = reasonInfo.getExtraCode();
             String extraMessage = reasonInfo.getExtraMessage();
             if (extraMessage != null) {
-                ri.setExtraMessage(extraMessage);
+                ri.extraMessage = extraMessage;
             }
 
             imsState.reasonInfo = ri;
         }
 
+        // If the connection state does not change, do not log it.
+        if (mLastImsConnectionState.get(phoneId) != null &&
+                Arrays.equals(ImsConnectionState.toByteArray(mLastImsConnectionState.get(phoneId)),
+                        ImsConnectionState.toByteArray(imsState))) {
+            return;
+        }
+
+        mLastImsConnectionState.put(phoneId, imsState);
+
         TelephonyEvent event = new TelephonyEventBuilder(phoneId)
                 .setImsConnectionState(imsState).build();
         addTelephonyEvent(event);
 
-        annotateInProgressCallSession(event.getTimestampMillis(), phoneId,
+        annotateInProgressCallSession(event.timestampMillis, phoneId,
                 new CallSessionEventBuilder(
                         TelephonyCallSession.Event.Type.IMS_CONNECTION_STATE_CHANGED)
                         .setImsConnectionState(event.imsConnectionState));
-        annotateInProgressSmsSession(event.getTimestampMillis(), phoneId,
+        annotateInProgressSmsSession(event.timestampMillis, phoneId,
                 new SmsSessionEventBuilder(
                         SmsSession.Event.Type.IMS_CONNECTION_STATE_CHANGED)
                         .setImsConnectionState(event.imsConnectionState));
@@ -906,22 +951,30 @@
     public synchronized void writeOnImsCapabilities(int phoneId, boolean[] capabilities) {
         ImsCapabilities cap = new ImsCapabilities();
 
-        cap.setVoiceOverLte(capabilities[0]);
-        cap.setVideoOverLte(capabilities[1]);
-        cap.setVoiceOverWifi(capabilities[2]);
-        cap.setVideoOverWifi(capabilities[3]);
-        cap.setUtOverLte(capabilities[4]);
-        cap.setUtOverWifi(capabilities[5]);
+        cap.voiceOverLte = capabilities[0];
+        cap.videoOverLte = capabilities[1];
+        cap.voiceOverWifi = capabilities[2];
+        cap.videoOverWifi = capabilities[3];
+        cap.utOverLte = capabilities[4];
+        cap.utOverWifi = capabilities[5];
 
         TelephonyEvent event = new TelephonyEventBuilder(phoneId).setImsCapabilities(cap).build();
+
+        // If the capabilities don't change, we don't log the event.
+        if (mLastImsCapabilities.get(phoneId) != null &&
+                Arrays.equals(ImsCapabilities.toByteArray(mLastImsCapabilities.get(phoneId)),
+                ImsCapabilities.toByteArray(cap))) {
+            return;
+        }
+
         mLastImsCapabilities.put(phoneId, cap);
         addTelephonyEvent(event);
 
-        annotateInProgressCallSession(event.getTimestampMillis(), phoneId,
+        annotateInProgressCallSession(event.timestampMillis, phoneId,
                 new CallSessionEventBuilder(
                         TelephonyCallSession.Event.Type.IMS_CAPABILITIES_CHANGED)
                         .setImsCapabilities(event.imsCapabilities));
-        annotateInProgressSmsSession(event.getTimestampMillis(), phoneId,
+        annotateInProgressSmsSession(event.timestampMillis, phoneId,
                 new SmsSessionEventBuilder(
                         SmsSession.Event.Type.IMS_CAPABILITIES_CHANGED)
                         .setImsCapabilities(event.imsCapabilities));
@@ -963,13 +1016,13 @@
                                       String apn, int authType, String protocol) {
 
         RilSetupDataCall setupDataCall = new RilSetupDataCall();
-        setupDataCall.setRat(radioTechnology);
-        setupDataCall.setDataProfile(profile + 1);  // off by 1 between proto and RIL constants.
+        setupDataCall.rat = radioTechnology;
+        setupDataCall.dataProfile = profile + 1;  // off by 1 between proto and RIL constants.
         if (apn != null) {
-            setupDataCall.setApn(apn);
+            setupDataCall.apn = apn;
         }
         if (protocol != null) {
-            setupDataCall.setType(toPdpType(protocol));
+            setupDataCall.type = toPdpType(protocol);
         }
 
         addTelephonyEvent(new TelephonyEventBuilder(phoneId).setSetupDataCall(
@@ -987,8 +1040,8 @@
     public void writeRilDeactivateDataCall(int phoneId, int rilSerial, int cid, int reason) {
 
         RilDeactivateDataCall deactivateDataCall = new RilDeactivateDataCall();
-        deactivateDataCall.setCid(cid);
-        deactivateDataCall.setReason(reason + 1);
+        deactivateDataCall.cid = cid;
+        deactivateDataCall.reason = reason + 1;
 
         addTelephonyEvent(new TelephonyEventBuilder(phoneId).setDeactivateDataCall(
                 deactivateDataCall).build());
@@ -1006,12 +1059,12 @@
 
         for (int i = 0; i < dcsList.size(); i++) {
             dataCalls[i] = new RilDataCall();
-            dataCalls[i].setCid(dcsList.get(i).cid);
-            if (dcsList.get(i).ifname != null) {
-                dataCalls[i].setIframe(dcsList.get(i).ifname);
+            dataCalls[i].cid = dcsList.get(i).cid;
+            if (!TextUtils.isEmpty(dcsList.get(i).ifname)) {
+                dataCalls[i].iframe = dcsList.get(i).ifname;
             }
-            if (dcsList.get(i).type != null) {
-                dataCalls[i].setType(toPdpType(dcsList.get(i).type));
+            if (!TextUtils.isEmpty(dcsList.get(i).type)) {
+                dataCalls[i].type = toPdpType(dcsList.get(i).type);
             }
         }
 
@@ -1038,15 +1091,25 @@
                             TelephonyCallSession.Event.Type.RIL_CALL_LIST_CHANGED)
                             .setRilCalls(calls)
             );
-            if (VDBG)  Rlog.v(TAG, "Logged Call list changed");
+            if (VDBG) Rlog.v(TAG, "Logged Call list changed");
+            if (callSession.isPhoneIdle() && disconnectReasonsKnown(calls)) {
+                finishCallSession(callSession);
+            }
         }
     }
 
+    private boolean disconnectReasonsKnown(RilCall[] calls) {
+        for (RilCall call : calls) {
+            if (call.callEndReason == 0) return false;
+        }
+        return true;
+    }
+
     private RilCall[] convertConnectionsToRilCalls(ArrayList<GsmCdmaConnection> mConnections) {
         RilCall[] calls = new RilCall[mConnections.size()];
         for (int i = 0; i < mConnections.size(); i++) {
             calls[i] = new RilCall();
-            calls[i].setIndex(i);
+            calls[i].index = i;
             convertConnectionToRilCall(mConnections.get(i), calls[i]);
         }
         return calls;
@@ -1054,44 +1117,44 @@
 
     private void convertConnectionToRilCall(GsmCdmaConnection conn, RilCall call) {
         if (conn.isIncoming()) {
-            call.setType(TelephonyCallSession.Event.RilCall.Type.MT);
+            call.type = Type.MT;
         } else {
-            call.setType(TelephonyCallSession.Event.RilCall.Type.MO);
+            call.type = Type.MO;
         }
         switch (conn.getState()) {
             case IDLE:
-                call.setState(TelephonyCallSession.Event.CallState.CALL_IDLE);
+                call.state = CallState.CALL_IDLE;
                 break;
             case ACTIVE:
-                call.setState(TelephonyCallSession.Event.CallState.CALL_ACTIVE);
+                call.state = CallState.CALL_ACTIVE;
                 break;
             case HOLDING:
-                call.setState(TelephonyCallSession.Event.CallState.CALL_HOLDING);
+                call.state = CallState.CALL_HOLDING;
                 break;
             case DIALING:
-                call.setState(TelephonyCallSession.Event.CallState.CALL_DIALING);
+                call.state = CallState.CALL_DIALING;
                 break;
             case ALERTING:
-                call.setState(TelephonyCallSession.Event.CallState.CALL_ALERTING);
+                call.state = CallState.CALL_ALERTING;
                 break;
             case INCOMING:
-                call.setState(TelephonyCallSession.Event.CallState.CALL_INCOMING);
+                call.state = CallState.CALL_INCOMING;
                 break;
             case WAITING:
-                call.setState(TelephonyCallSession.Event.CallState.CALL_WAITING);
+                call.state = CallState.CALL_WAITING;
                 break;
             case DISCONNECTED:
-                call.setState(TelephonyCallSession.Event.CallState.CALL_DISCONNECTED);
+                call.state = CallState.CALL_DISCONNECTED;
                 break;
             case DISCONNECTING:
-                call.setState(TelephonyCallSession.Event.CallState.CALL_DISCONNECTING);
+                call.state = CallState.CALL_DISCONNECTING;
                 break;
             default:
-                call.setState(TelephonyCallSession.Event.CallState.CALL_UNKNOWN);
+                call.state = CallState.CALL_UNKNOWN;
                 break;
         }
-        call.setCallEndReason(conn.getDisconnectCause());
-        call.setIsMultiparty(conn.isMultiparty());
+        call.callEndReason = conn.getDisconnectCause();
+        call.isMultiparty = conn.isMultiparty();
     }
 
     /**
@@ -1111,7 +1174,7 @@
         } else {
             RilCall[] calls = new RilCall[1];
             calls[0] = new RilCall();
-            calls[0].setIndex(-1);
+            calls[0].index = -1;
             convertConnectionToRilCall(conn, calls[0]);
             callSession.addEvent(callSession.startElapsedTimeMs,
                     new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_REQUEST)
@@ -1148,7 +1211,7 @@
         } else {
             RilCall[] calls = new RilCall[1];
             calls[0] = new RilCall();
-            calls[0].setIndex(callId);
+            calls[0].index = callId;
             convertConnectionToRilCall(conn, calls[0]);
             callSession.addEvent(
                     new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_REQUEST)
@@ -1244,17 +1307,17 @@
         RilDataCall dataCall = new RilDataCall();
 
         if (response != null) {
-            setupDataCallResponse.setStatus(
-                    response.status == 0 ? RilDataCallFailCause.PDP_FAIL_NONE : response.status);
-            setupDataCallResponse.setSuggestedRetryTimeMillis(response.suggestedRetryTime);
+            setupDataCallResponse.status =
+                    (response.status == 0 ? RilDataCallFailCause.PDP_FAIL_NONE : response.status);
+            setupDataCallResponse.suggestedRetryTimeMillis = response.suggestedRetryTime;
 
-            dataCall.setCid(response.cid);
-            if (response.type != null) {
-                dataCall.setType(toPdpType(response.type));
+            dataCall.cid = response.cid;
+            if (!TextUtils.isEmpty(response.type)) {
+                dataCall.type = toPdpType(response.type);
             }
 
-            if (response.ifname != null) {
-                dataCall.setIframe(response.ifname);
+            if (!TextUtils.isEmpty(response.ifname)) {
+                dataCall.iframe = response.ifname;
             }
         }
         setupDataCallResponse.call = dataCall;
@@ -1281,7 +1344,7 @@
                     TelephonyCallSession.Event.Type.RIL_RESPONSE)
                     .setRilRequest(toCallSessionRilRequest(rilRequest))
                     .setRilRequestId(rilSerial)
-                    .setRilError(rilError));
+                    .setRilError(rilError + 1));
         }
     }
 
@@ -1309,7 +1372,7 @@
             smsSession.addEvent(new SmsSessionEventBuilder(
                     SmsSession.Event.Type.SMS_SEND_RESULT)
                     .setErrorCode(errorCode)
-                    .setRilErrno(rilError)
+                    .setRilErrno(rilError + 1)
                     .setRilRequestId(rilSerial)
             );
 
@@ -1392,7 +1455,12 @@
         if (callSession == null) {
             Rlog.e(TAG, "writePhoneState: Call session is missing");
         } else {
-            if (state == TelephonyCallSession.Event.PhoneState.STATE_IDLE) {
+            // For CS Calls Finish the Call Session after Receiving the Last Call Fail Cause
+            // For IMS calls we receive the Disconnect Cause along with Call End event.
+            // So we can finish the call session here.
+            callSession.setLastKnownPhoneState(state);
+            if ((state == TelephonyCallSession.Event.PhoneState.STATE_IDLE)
+                    && (!callSession.containsCsCalls())) {
                 finishCallSession(callSession);
             }
             callSession.addEvent(new CallSessionEventBuilder(
@@ -1485,7 +1553,11 @@
      * @param session IMS call session
      */
     public void writeOnImsCallReceive(int phoneId, ImsCallSession session) {
-        writeOnImsCallStart(phoneId, session);
+        InProgressCallSession callSession = startNewCallSessionIfNeeded(phoneId);
+
+        callSession.addEvent(
+                new CallSessionEventBuilder(TelephonyCallSession.Event.Type.IMS_CALL_RECEIVE)
+                        .setCallIndex(getCallId(session)));
     }
 
     /**
@@ -1517,11 +1589,11 @@
     private TelephonyProto.ImsReasonInfo toImsReasonInfoProto(ImsReasonInfo reasonInfo) {
         TelephonyProto.ImsReasonInfo ri = new TelephonyProto.ImsReasonInfo();
         if (reasonInfo != null) {
-            ri.setReasonCode(reasonInfo.getCode());
-            ri.setExtraCode(reasonInfo.getExtraCode());
+            ri.reasonCode = reasonInfo.getCode();
+            ri.extraCode = reasonInfo.getExtraCode();
             String extraMessage = reasonInfo.getExtraMessage();
             if (extraMessage != null) {
-                ri.setExtraMessage(extraMessage);
+                ri.extraMessage = extraMessage;
             }
         }
         return ri;
@@ -1621,7 +1693,7 @@
         TelephonyEvent event = new TelephonyEventBuilder(phoneId).setNITZ(timestamp).build();
         addTelephonyEvent(event);
 
-        annotateInProgressCallSession(event.getTimestampMillis(), phoneId,
+        annotateInProgressCallSession(event.timestampMillis, phoneId,
                 new CallSessionEventBuilder(
                         TelephonyCallSession.Event.Type.NITZ_TIME)
                         .setNITZ(timestamp));
@@ -1636,8 +1708,8 @@
     public void writeModemRestartEvent(int phoneId, String reason) {
         final ModemRestart modemRestart = new ModemRestart();
         String basebandVersion = Build.getRadioVersion();
-        if (basebandVersion != null) modemRestart.setBasebandVersion(basebandVersion);
-        if (reason != null) modemRestart.setReason(reason);
+        if (basebandVersion != null) modemRestart.basebandVersion = basebandVersion;
+        if (reason != null) modemRestart.reason = reason;
         TelephonyEvent event = new TelephonyEventBuilder(phoneId).setModemRestart(
                 modemRestart).build();
         addTelephonyEvent(event);
diff --git a/src/java/com/android/internal/telephony/sip/SipCommandInterface.java b/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
index e72496a..fe1d7c5 100644
--- a/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
+++ b/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
@@ -20,6 +20,8 @@
 import android.os.Handler;
 import android.os.Message;
 import android.service.carrier.CarrierIdentifier;
+import android.telephony.ImsiEncryptionInfo;
+import android.telephony.NetworkScanRequest;
 
 import com.android.internal.telephony.BaseCommands;
 import com.android.internal.telephony.CommandsInterface;
@@ -28,7 +30,6 @@
 import com.android.internal.telephony.dataconnection.DataProfile;
 import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
 
-import java.security.PublicKey;
 import java.util.List;
 
 /**
@@ -335,7 +336,7 @@
     }
 
     @Override
-    public void startNetworkScan(Message response) {
+    public void startNetworkScan(NetworkScanRequest nsr, Message response) {
     }
 
     @Override
@@ -626,7 +627,7 @@
     }
 
     @Override
-    public void setCarrierInfoForImsiEncryption(PublicKey carrierPublicKey, String keyIdentifier,
+    public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo,
                                                 Message result) {
     }
 
diff --git a/src/java/com/android/internal/telephony/sip/SipPhoneBase.java b/src/java/com/android/internal/telephony/sip/SipPhoneBase.java
index eff731e..7bdfad8 100755
--- a/src/java/com/android/internal/telephony/sip/SipPhoneBase.java
+++ b/src/java/com/android/internal/telephony/sip/SipPhoneBase.java
@@ -27,6 +27,7 @@
 import android.os.SystemProperties;
 import android.os.WorkSource;
 import android.telephony.CellLocation;
+import android.telephony.NetworkScanRequest;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
@@ -394,7 +395,7 @@
     }
 
     @Override
-    public void startNetworkScan(Message response) {
+    public void startNetworkScan(NetworkScanRequest nsr, Message response) {
     }
 
     @Override
diff --git a/src/java/com/android/internal/telephony/test/SimulatedCommands.java b/src/java/com/android/internal/telephony/test/SimulatedCommands.java
index 59d999f..3360058 100644
--- a/src/java/com/android/internal/telephony/test/SimulatedCommands.java
+++ b/src/java/com/android/internal/telephony/test/SimulatedCommands.java
@@ -30,6 +30,8 @@
 import android.telephony.CellInfo;
 import android.telephony.CellInfoGsm;
 import android.telephony.IccOpenLogicalChannelResponse;
+import android.telephony.ImsiEncryptionInfo;
+import android.telephony.NetworkScanRequest;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
@@ -53,7 +55,6 @@
 import com.android.internal.telephony.uicc.IccCardStatus;
 import com.android.internal.telephony.uicc.IccIoResult;
 
-import java.security.PublicKey;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -1369,7 +1370,7 @@
      * Starts a network scan
      */
     @Override
-    public void startNetworkScan(Message result) {
+    public void startNetworkScan(NetworkScanRequest nsr, Message result) {
         unimplemented(result);
     }
 
@@ -1454,11 +1455,11 @@
     }
 
     @Override
-    public void setCarrierInfoForImsiEncryption(PublicKey publicKey, String keyIdentifier,
+    public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo,
                                                 Message response) {
         // Just echo back data
         if (response != null) {
-            AsyncResult.forMessage(response).result = publicKey;
+            AsyncResult.forMessage(response).result = imsiEncryptionInfo;
             response.sendToTarget();
         }
     }
@@ -2150,4 +2151,10 @@
                     new AsyncResult(null, restrictedState, null));
         }
     }
+
+    @Override
+    public void setOnRestrictedStateChanged(Handler h, int what, Object obj) {
+        super.setOnRestrictedStateChanged(h, what, obj);
+        SimulatedCommandsVerifier.getInstance().setOnRestrictedStateChanged(h, what, obj);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/test/SimulatedCommandsVerifier.java b/src/java/com/android/internal/telephony/test/SimulatedCommandsVerifier.java
index c5da1e0..91b86e3 100644
--- a/src/java/com/android/internal/telephony/test/SimulatedCommandsVerifier.java
+++ b/src/java/com/android/internal/telephony/test/SimulatedCommandsVerifier.java
@@ -19,6 +19,8 @@
 import android.os.Handler;
 import android.os.Message;
 import android.service.carrier.CarrierIdentifier;
+import android.telephony.ImsiEncryptionInfo;
+import android.telephony.NetworkScanRequest;
 
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.RadioCapability;
@@ -27,7 +29,6 @@
 import com.android.internal.telephony.dataconnection.DataProfile;
 import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
 
-import java.security.PublicKey;
 import java.util.List;
 
 public class SimulatedCommandsVerifier implements CommandsInterface {
@@ -946,7 +947,7 @@
     }
 
     @Override
-    public void startNetworkScan(Message response) {
+    public void startNetworkScan(NetworkScanRequest nsr, Message response) {
 
     }
 
@@ -1349,7 +1350,7 @@
     }
 
     @Override
-    public void setCarrierInfoForImsiEncryption(PublicKey carrierPublicKey, String keyIdentifier,
+    public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo,
                                                 Message result) {
 
     }
diff --git a/src/java/com/android/internal/telephony/uicc/AdnRecord.java b/src/java/com/android/internal/telephony/uicc/AdnRecord.java
index 203236c..4414caf 100644
--- a/src/java/com/android/internal/telephony/uicc/AdnRecord.java
+++ b/src/java/com/android/internal/telephony/uicc/AdnRecord.java
@@ -19,8 +19,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
 import android.telephony.Rlog;
+import android.text.TextUtils;
 
 import com.android.internal.telephony.GsmAlphabet;
 
@@ -248,7 +248,8 @@
             Rlog.w(LOG_TAG, "[buildAdnString] Max length of tag is " + footerOffset);
             return null;
         } else {
-            bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD(mNumber);
+            bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD(
+                    mNumber, PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN);
 
             System.arraycopy(bcdNumber, 0, adnString,
                     footerOffset + ADN_TON_AND_NPI, bcdNumber.length);
@@ -289,7 +290,10 @@
             }
 
             mNumber += PhoneNumberUtils.calledPartyBCDFragmentToString(
-                                        extRecord, 2, 0xff & extRecord[1]);
+                    extRecord,
+                    2,
+                    0xff & extRecord[1],
+                    PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN);
 
             // We don't support ext record chaining.
 
@@ -327,7 +331,10 @@
             // the ME (see note 2)."
 
             mNumber = PhoneNumberUtils.calledPartyBCDToString(
-                            record, footerOffset + 1, numberLength);
+                    record,
+                    footerOffset + 1,
+                    numberLength,
+                    PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN);
 
 
             mExtRecord = 0xff & record[record.length - 1];
diff --git a/src/java/com/android/internal/telephony/uicc/CarrierTestOverride.java b/src/java/com/android/internal/telephony/uicc/CarrierTestOverride.java
new file mode 100644
index 0000000..18d2937
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/CarrierTestOverride.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.uicc;
+
+import android.os.Environment;
+import android.telephony.Rlog;
+import android.util.Xml;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashMap;
+
+/**
+ * Provide a machanism to override MVNO paramteres under CarrierConfig through a config file.
+ */
+public class CarrierTestOverride {
+    static final String LOG_TAG = "CarrierTestOverride";
+
+    /**
+     * Config file that can be created and adb-pushed by tester/developer
+     *
+     * Sample xml:
+     * <carrierTestOverrides>
+       <carrierTestOverride key="isInTestMode" value="true"/>
+       <carrierTestOverride key="gid1" value="bae0000000000000"/>
+       <carrierTestOverride key="gid2" value="ffffffffffffffff"/>
+       <carrierTestOverride key="imsi" value="310010123456789"/>
+       <carrierTestOverride key="spn" value="Verizon"/>
+       </carrierTestOverrides>
+     */
+    static final String DATA_CARRIER_TEST_OVERRIDE_PATH =
+            "/user_de/0/com.android.phone/files/carrier_test_conf.xml";
+    static final String CARRIER_TEST_XML_HEADER = "carrierTestOverrides";
+    static final String CARRIER_TEST_XML_SUBHEADER = "carrierTestOverride";
+    static final String CARRIER_TEST_XML_ITEM_KEY = "key";
+    static final String CARRIER_TEST_XML_ITEM_VALUE = "value";
+    static final String CARRIER_TEST_XML_ITEM_KEY_STRING_ISINTESTMODE = "isInTestMode";
+    static final String CARRIER_TEST_XML_ITEM_KEY_STRING_GID1 = "gid1";
+    static final String CARRIER_TEST_XML_ITEM_KEY_STRING_GID2 = "gid2";
+    static final String CARRIER_TEST_XML_ITEM_KEY_STRING_IMSI = "imsi";
+    static final String CARRIER_TEST_XML_ITEM_KEY_STRING_SPN = "spn";
+
+    private HashMap<String, String> mCarrierTestParamMap;
+
+    CarrierTestOverride() {
+        mCarrierTestParamMap = new HashMap<String, String>();
+        loadCarrierTestOverrides();
+    }
+
+    boolean isInTestMode() {
+        return mCarrierTestParamMap.containsKey(CARRIER_TEST_XML_ITEM_KEY_STRING_ISINTESTMODE)
+                && mCarrierTestParamMap.get(CARRIER_TEST_XML_ITEM_KEY_STRING_ISINTESTMODE)
+                .equals("true");
+    }
+
+    String getFakeSpn() {
+        try {
+            String spn = mCarrierTestParamMap.get(CARRIER_TEST_XML_ITEM_KEY_STRING_SPN);
+            Rlog.d(LOG_TAG, "reading spn from CarrierTestConfig file: " + spn);
+            return spn;
+        } catch (NullPointerException e) {
+            Rlog.w(LOG_TAG, "No spn in CarrierTestConfig file ");
+            return null;
+        }
+    }
+
+    String getFakeIMSI() {
+        try {
+            String imsi = mCarrierTestParamMap.get(CARRIER_TEST_XML_ITEM_KEY_STRING_IMSI);
+            Rlog.d(LOG_TAG, "reading imsi from CarrierTestConfig file: " + imsi);
+            return imsi;
+        } catch (NullPointerException e) {
+            Rlog.w(LOG_TAG, "No imsi in CarrierTestConfig file ");
+            return null;
+        }
+    }
+
+    String getFakeGid1() {
+        try {
+            String gid1 = mCarrierTestParamMap.get(CARRIER_TEST_XML_ITEM_KEY_STRING_GID1);
+            Rlog.d(LOG_TAG, "reading gid1 from CarrierTestConfig file: " + gid1);
+            return gid1;
+        } catch (NullPointerException e) {
+            Rlog.w(LOG_TAG, "No gid1 in CarrierTestConfig file ");
+            return null;
+        }
+    }
+
+    String getFakeGid2() {
+        try {
+            String gid2 = mCarrierTestParamMap.get(CARRIER_TEST_XML_ITEM_KEY_STRING_GID2);
+            Rlog.d(LOG_TAG, "reading gid2 from CarrierTestConfig file: " + gid2);
+            return gid2;
+        } catch (NullPointerException e) {
+            Rlog.w(LOG_TAG, "No gid2 in CarrierTestConfig file ");
+            return null;
+        }
+    }
+
+    private void loadCarrierTestOverrides() {
+
+        FileReader carrierTestConfigReader;
+
+        File carrierTestConfigFile = new File(Environment.getDataDirectory(),
+                DATA_CARRIER_TEST_OVERRIDE_PATH);
+
+        try {
+            carrierTestConfigReader = new FileReader(carrierTestConfigFile);
+            Rlog.d(LOG_TAG, "CarrierTestConfig file Modified Timestamp: "
+                    + carrierTestConfigFile.lastModified());
+        } catch (FileNotFoundException e) {
+            Rlog.w(LOG_TAG, "Can not open " + carrierTestConfigFile.getAbsolutePath());
+            return;
+        }
+
+        try {
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(carrierTestConfigReader);
+
+            XmlUtils.beginDocument(parser, CARRIER_TEST_XML_HEADER);
+
+            while (true) {
+                XmlUtils.nextElement(parser);
+
+                String name = parser.getName();
+                if (!CARRIER_TEST_XML_SUBHEADER.equals(name)) {
+                    break;
+                }
+
+                String key = parser.getAttributeValue(null, CARRIER_TEST_XML_ITEM_KEY);
+                String value = parser.getAttributeValue(null, CARRIER_TEST_XML_ITEM_VALUE);
+
+                Rlog.d(LOG_TAG,
+                        "extracting key-values from CarrierTestConfig file: " + key + "|" + value);
+                mCarrierTestParamMap.put(key, value);
+            }
+            carrierTestConfigReader.close();
+        } catch (XmlPullParserException e) {
+            Rlog.w(LOG_TAG, "Exception in carrier_test_conf parser " + e);
+        } catch (IOException e) {
+            Rlog.w(LOG_TAG, "Exception in carrier_test_conf parser " + e);
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/IccCardProxy.java b/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
index 5466402..241f211 100644
--- a/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
+++ b/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
@@ -16,9 +16,7 @@
 
 package com.android.internal.telephony.uicc;
 
-import static android.Manifest.permission.READ_PHONE_STATE;
-
-import android.app.ActivityManagerNative;
+import android.app.ActivityManager;
 import android.content.Context;
 import android.content.Intent;
 import android.os.AsyncResult;
@@ -38,6 +36,7 @@
 import com.android.internal.telephony.IccCard;
 import com.android.internal.telephony.IccCardConstants;
 import com.android.internal.telephony.IccCardConstants.State;
+import com.android.internal.telephony.IntentBroadcaster;
 import com.android.internal.telephony.MccTable;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
@@ -48,7 +47,6 @@
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
 import com.android.internal.telephony.uicc.IccCardStatus.CardState;
 import com.android.internal.telephony.uicc.IccCardStatus.PinState;
-import com.android.internal.telephony.uicc.UiccController;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -89,7 +87,7 @@
     private static final int EVENT_ICC_RECORD_EVENTS = 500;
     private static final int EVENT_SUBSCRIPTION_ACTIVATED = 501;
     private static final int EVENT_SUBSCRIPTION_DEACTIVATED = 502;
-    private static final int EVENT_CARRIER_PRIVILIGES_LOADED = 503;
+    private static final int EVENT_CARRIER_PRIVILEGES_LOADED = 503;
 
     private Integer mPhoneId = null;
 
@@ -169,17 +167,31 @@
      */
     private void updateQuietMode() {
         synchronized (mLock) {
+            boolean oldQuietMode = mQuietMode;
             boolean newQuietMode;
             int cdmaSource = Phone.CDMA_SUBSCRIPTION_UNKNOWN;
+            boolean isLteOnCdmaMode = TelephonyManager.getLteOnCdmaModeStatic()
+                    == PhoneConstants.LTE_ON_CDMA_TRUE;
             if (mCurrentAppType == UiccController.APP_FAM_3GPP) {
                 newQuietMode = false;
                 if (DBG) log("updateQuietMode: 3GPP subscription -> newQuietMode=" + newQuietMode);
             } else {
+                if (isLteOnCdmaMode) {
+                    log("updateQuietMode: is cdma/lte device, force IccCardProxy into 3gpp mode");
+                    mCurrentAppType = UiccController.APP_FAM_3GPP;
+                }
                 cdmaSource = mCdmaSSM != null ?
                         mCdmaSSM.getCdmaSubscriptionSource() : Phone.CDMA_SUBSCRIPTION_UNKNOWN;
 
                 newQuietMode = (cdmaSource == Phone.CDMA_SUBSCRIPTION_NV)
-                        && (mCurrentAppType == UiccController.APP_FAM_3GPP2);
+                        && (mCurrentAppType == UiccController.APP_FAM_3GPP2)
+                        && !isLteOnCdmaMode;
+                if (DBG) {
+                    log("updateQuietMode: cdmaSource=" + cdmaSource
+                            + " mCurrentAppType=" + mCurrentAppType
+                            + " isLteOnCdmaMode=" + isLteOnCdmaMode
+                            + " newQuietMode=" + newQuietMode);
+                }
             }
 
             if (mQuietMode == false && newQuietMode == true) {
@@ -200,7 +212,8 @@
             }
             if (DBG) {
                 log("updateQuietMode: QuietMode is " + mQuietMode + " (app_type="
-                    + mCurrentAppType + " cdmaSource=" + cdmaSource + ")");
+                    + mCurrentAppType + " isLteOnCdmaMode=" + isLteOnCdmaMode
+                    + " cdmaSource=" + cdmaSource + ")");
             }
             mInitialized = true;
             sendMessage(obtainMessage(EVENT_ICC_CHANGED));
@@ -261,7 +274,7 @@
                 }
                 if (mUiccCard != null && !mUiccCard.areCarrierPriviligeRulesLoaded()) {
                     mUiccCard.registerForCarrierPrivilegeRulesLoaded(
-                        this, EVENT_CARRIER_PRIVILIGES_LOADED, null);
+                            this, EVENT_CARRIER_PRIVILEGES_LOADED, null);
                 } else {
                     onRecordsLoaded();
                 }
@@ -297,7 +310,7 @@
                 }
                 break;
 
-            case EVENT_CARRIER_PRIVILIGES_LOADED:
+            case EVENT_CARRIER_PRIVILEGES_LOADED:
                 log("EVENT_CARRIER_PRIVILEGES_LOADED");
                 if (mUiccCard != null) {
                     mUiccCard.unregisterForCarrierPrivilegeRulesLoaded(this);
@@ -469,6 +482,7 @@
 
     private void unregisterUiccCardEvents() {
         if (mUiccCard != null) mUiccCard.unregisterForAbsent(this);
+        if (mUiccCard != null) mUiccCard.unregisterForCarrierPrivilegeRulesLoaded(this);
         if (mUiccApplication != null) mUiccApplication.unregisterForReady(this);
         if (mUiccApplication != null) mUiccApplication.unregisterForLocked(this);
         if (mUiccApplication != null) mUiccApplication.unregisterForNetworkLocked(this);
@@ -508,8 +522,7 @@
             SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhoneId);
             log("broadcastIccStateChangedIntent intent ACTION_SIM_STATE_CHANGED value=" + value
                 + " reason=" + reason + " for mPhoneId=" + mPhoneId);
-            ActivityManagerNative.broadcastStickyIntent(intent, READ_PHONE_STATE,
-                    UserHandle.USER_ALL);
+            IntentBroadcaster.getInstance().broadcastStickyIntent(intent, mPhoneId);
         }
     }
 
@@ -527,8 +540,9 @@
             intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE, value);
             intent.putExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON, reason);
             intent.putExtra(PhoneConstants.PHONE_KEY, mPhoneId);  // SubId may not be valid.
-            log("Sending intent ACTION_INTERNAL_SIM_STATE_CHANGED" + " for mPhoneId : " + mPhoneId);
-            ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);
+            log("Sending intent ACTION_INTERNAL_SIM_STATE_CHANGED value=" + value
+                    + " for mPhoneId : " + mPhoneId);
+            ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL);
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/uicc/IccCardStatus.java b/src/java/com/android/internal/telephony/uicc/IccCardStatus.java
index 8992e9f..f14f21d 100644
--- a/src/java/com/android/internal/telephony/uicc/IccCardStatus.java
+++ b/src/java/com/android/internal/telephony/uicc/IccCardStatus.java
@@ -116,30 +116,33 @@
         StringBuilder sb = new StringBuilder();
         sb.append("IccCardState {").append(mCardState).append(",")
         .append(mUniversalPinState)
-        .append(",num_apps=").append(mApplications.length)
-        .append(",gsm_id=").append(mGsmUmtsSubscriptionAppIndex);
-        if (mGsmUmtsSubscriptionAppIndex >=0
-                && mGsmUmtsSubscriptionAppIndex <CARD_MAX_APPS) {
+        .append(",num_apps=").append(mApplications.length);
+
+        sb.append(",gsm_id=").append(mGsmUmtsSubscriptionAppIndex);
+        if (mApplications != null
+                && mGsmUmtsSubscriptionAppIndex >= 0
+                && mGsmUmtsSubscriptionAppIndex < mApplications.length) {
             app = mApplications[mGsmUmtsSubscriptionAppIndex];
             sb.append(app == null ? "null" : app);
         }
 
         sb.append(",cdma_id=").append(mCdmaSubscriptionAppIndex);
-        if (mCdmaSubscriptionAppIndex >=0
-                && mCdmaSubscriptionAppIndex <CARD_MAX_APPS) {
+        if (mApplications != null
+                && mCdmaSubscriptionAppIndex >= 0
+                && mCdmaSubscriptionAppIndex < mApplications.length) {
             app = mApplications[mCdmaSubscriptionAppIndex];
             sb.append(app == null ? "null" : app);
         }
 
         sb.append(",ims_id=").append(mImsSubscriptionAppIndex);
-        if (mImsSubscriptionAppIndex >=0
-                && mImsSubscriptionAppIndex <CARD_MAX_APPS) {
+        if (mApplications != null
+                && mImsSubscriptionAppIndex >= 0
+                && mImsSubscriptionAppIndex < mApplications.length) {
             app = mApplications[mImsSubscriptionAppIndex];
             sb.append(app == null ? "null" : app);
         }
 
         sb.append("}");
-
         return sb.toString();
     }
 
diff --git a/src/java/com/android/internal/telephony/uicc/IccRecords.java b/src/java/com/android/internal/telephony/uicc/IccRecords.java
index cf33498..af26f5c 100644
--- a/src/java/com/android/internal/telephony/uicc/IccRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IccRecords.java
@@ -78,15 +78,20 @@
     protected String mNewVoiceMailTag = null;
     protected boolean mIsVoiceMailFixed = false;
     protected String mImsi;
+    protected String mFakeImsi;
     private IccIoResult auth_rsp;
 
     protected int mMncLength = UNINITIALIZED;
     protected int mMailboxIndex = 0; // 0 is no mailbox dailing number associated
 
     private String mSpn;
+    private String mFakeSpn;
 
     protected String mGid1;
+    protected String mFakeGid1;
     protected String mGid2;
+    protected String mFakeGid2;
+
     protected String mPrefLang;
 
     protected PlmnActRecord[] mHplmnActRecords;
@@ -98,6 +103,8 @@
 
     private final Object mLock = new Object();
 
+    CarrierTestOverride mCarrierTestOverride;
+
     //Arbitrary offset for the Handler
     protected static final int HANDLER_ACTION_BASE = 0x12E500;
     protected static final int HANDLER_ACTION_NONE = HANDLER_ACTION_BASE + 0;
@@ -157,9 +164,13 @@
                 + " isVoiceMailFixed=" + mIsVoiceMailFixed
                 + " mImsi=" + ((mImsi != null) ?
                 mImsi.substring(0, 6) + Rlog.pii(VDBG, mImsi.substring(6)) : "null")
+                + (mCarrierTestOverride.isInTestMode()
+                ? (" mFakeImsi=" + ((mFakeImsi != null) ? mFakeImsi : "null")) : "")
                 + " mncLength=" + mMncLength
                 + " mailboxIndex=" + mMailboxIndex
-                + " spn=" + mSpn;
+                + " spn=" + mSpn
+                + (mCarrierTestOverride.isInTestMode()
+                ? (" mFakeSpn=" + ((mFakeSpn != null) ? mFakeSpn : "null")) : "");
 
     }
 
@@ -187,6 +198,22 @@
         mParentApp = app;
         mTelephonyManager = (TelephonyManager) mContext.getSystemService(
                 Context.TELEPHONY_SERVICE);
+
+        mCarrierTestOverride = new CarrierTestOverride();
+
+        if (mCarrierTestOverride.isInTestMode()) {
+            mFakeImsi = mCarrierTestOverride.getFakeIMSI();
+            log("load mFakeImsi: " + mFakeImsi);
+
+            mFakeGid1 = mCarrierTestOverride.getFakeGid1();
+            log("load mFakeGid1: " + mFakeGid1);
+
+            mFakeGid2 = mCarrierTestOverride.getFakeGid2();
+            log("load mFakeGid2: " + mFakeGid2);
+
+            mFakeSpn = mCarrierTestOverride.getFakeSpn();
+            log("load mFakeSpn: " + mFakeSpn);
+        }
     }
 
     /**
@@ -279,7 +306,7 @@
         Registrant r = new Registrant(h, what, obj);
         mImsiReadyRegistrants.add(r);
 
-        if (mImsi != null) {
+        if (getIMSI() != null) {
             r.notifyRegistrant(new AsyncResult(null, null, null));
         }
     }
@@ -325,7 +352,11 @@
      * @return null if SIM is not yet ready or unavailable
      */
     public String getIMSI() {
-        return null;
+        if (mCarrierTestOverride.isInTestMode() && mFakeImsi != null) {
+            return mFakeImsi;
+        } else {
+            return mImsi;
+        }
     }
 
     /**
@@ -356,7 +387,11 @@
      * @return null if SIM is not yet ready
      */
     public String getGid1() {
-        return null;
+        if (mCarrierTestOverride.isInTestMode() && mFakeGid1 != null) {
+            return mFakeGid1;
+        } else {
+            return mGid1;
+        }
     }
 
     /**
@@ -364,7 +399,11 @@
      * @return null if SIM is not yet ready
      */
     public String getGid2() {
-        return null;
+        if (mCarrierTestOverride.isInTestMode() && mFakeGid2 != null) {
+            return mFakeGid2;
+        } else {
+            return mGid2;
+        }
     }
 
     public void setMsisdnNumber(String alphaTag, String number,
@@ -390,6 +429,9 @@
      * @return null if SIM is not yet ready or no RUIM entry
      */
     public String getServiceProviderName() {
+        if (mCarrierTestOverride.isInTestMode() && mFakeSpn != null) {
+            return mFakeSpn;
+        }
         String providerName = mSpn;
 
         // Check for null pointers, mParentApp can be null after dispose,
@@ -476,6 +518,7 @@
      */
     protected void onIccRefreshInit() {
         mAdnCache.reset();
+        mMncLength = UNINITIALIZED;
         UiccCardApplication parentApp = mParentApp;
         if ((parentApp != null) &&
                 (parentApp.getState() == AppState.APPSTATE_READY)) {
@@ -777,9 +820,15 @@
         pw.println(" mIsVoiceMailFixed=" + mIsVoiceMailFixed);
         pw.println(" mImsi=" + ((mImsi != null) ?
                 mImsi.substring(0, 6) + Rlog.pii(VDBG, mImsi.substring(6)) : "null"));
+        if (mCarrierTestOverride.isInTestMode()) {
+            pw.println(" mFakeImsi=" + ((mFakeImsi != null) ? mFakeImsi : "null"));
+        }
         pw.println(" mMncLength=" + mMncLength);
         pw.println(" mMailboxIndex=" + mMailboxIndex);
         pw.println(" mSpn=" + mSpn);
+        if (mCarrierTestOverride.isInTestMode()) {
+            pw.println(" mFakeSpn=" + ((mFakeSpn != null) ? mFakeSpn : "null"));
+        }
         pw.flush();
     }
 }
diff --git a/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java b/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
index 83f7e02..194d259 100644
--- a/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
@@ -16,13 +16,18 @@
 
 package com.android.internal.telephony.uicc;
 
+import static com.android.internal.telephony.uicc.IccConstants.EF_DOMAIN;
+import static com.android.internal.telephony.uicc.IccConstants.EF_IMPI;
+import static com.android.internal.telephony.uicc.IccConstants.EF_IMPU;
+import static com.android.internal.telephony.uicc.IccConstants.EF_IST;
+import static com.android.internal.telephony.uicc.IccConstants.EF_PCSCF;
+
 import android.content.Context;
+import android.content.Intent;
 import android.os.AsyncResult;
-import android.os.Handler;
 import android.os.Message;
 import android.telephony.Rlog;
-import android.content.Intent;
-
+import android.text.TextUtils;
 
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.gsm.SimTlv;
@@ -34,12 +39,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 
-import static com.android.internal.telephony.uicc.IccConstants.EF_DOMAIN;
-import static com.android.internal.telephony.uicc.IccConstants.EF_IMPI;
-import static com.android.internal.telephony.uicc.IccConstants.EF_IMPU;
-import static com.android.internal.telephony.uicc.IccConstants.EF_IST;
-import static com.android.internal.telephony.uicc.IccConstants.EF_PCSCF;
-
 /**
  * {@hide}
  */
@@ -349,8 +348,8 @@
             return;
         }
 
-        if (refreshResponse.aid != null &&
-                !refreshResponse.aid.equals(mParentApp.getAid())) {
+        if (!TextUtils.isEmpty(refreshResponse.aid)
+                && !refreshResponse.aid.equals(mParentApp.getAid())) {
             // This is for different app. Ignore.
             if (DBG) log("handleIsimRefresh received different app");
             return;
diff --git a/src/java/com/android/internal/telephony/uicc/RuimRecords.java b/src/java/com/android/internal/telephony/uicc/RuimRecords.java
index 4e486d2..b303ca8 100644
--- a/src/java/com/android/internal/telephony/uicc/RuimRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/RuimRecords.java
@@ -18,31 +18,31 @@
 
 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_TEST_CSIM;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Locale;
 import android.content.Context;
+import android.content.res.Resources;
 import android.os.AsyncResult;
 import android.os.Message;
 import android.os.SystemProperties;
+import android.telephony.Rlog;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
-import android.telephony.Rlog;
 import android.text.TextUtils;
 import android.util.Log;
-import android.content.res.Resources;
 
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.GsmAlphabet;
 import com.android.internal.telephony.MccTable;
 import com.android.internal.telephony.SubscriptionController;
-
 import com.android.internal.telephony.cdma.sms.UserData;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
 import com.android.internal.util.BitwiseInputStream;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Locale;
+
 /**
  * {@hide}
  */
@@ -153,11 +153,6 @@
         mRecordsRequested = false;
     }
 
-    @Override
-    public String getIMSI() {
-        return mImsi;
-    }
-
     public String getMdnNumber() {
         return mMyMobileNumber;
     }
@@ -215,21 +210,23 @@
      *  provided the RUIM card. Returns null of RUIM is not yet ready
      */
     public String getRUIMOperatorNumeric() {
-        if (mImsi == null) {
+        String imsi = getIMSI();
+
+        if (imsi == null) {
             return null;
         }
 
         if (mMncLength != UNINITIALIZED && mMncLength != UNKNOWN) {
             // Length = length of MCC + length of MNC
             // length of mcc = 3 (3GPP2 C.S0005 - Section 2.3)
-            return mImsi.substring(0, 3 + mMncLength);
+            return imsi.substring(0, 3 + mMncLength);
         }
 
         // Guess the MNC length based on the MCC if we don't
         // have a valid value in ef[ad]
 
-        int mcc = Integer.parseInt(mImsi.substring(0,3));
-        return mImsi.substring(0, 3 + MccTable.smallestDigitsMccForMnc(mcc));
+        int mcc = Integer.parseInt(imsi.substring(0, 3));
+        return imsi.substring(0, 3 + MccTable.smallestDigitsMccForMnc(mcc));
     }
 
     // Refer to ETSI TS 102.221
@@ -774,12 +771,14 @@
                 log("onAllRecordsLoaded empty 'gsm.sim.operator.numeric' skipping");
             }
 
-            if (!TextUtils.isEmpty(mImsi)) {
-                log("onAllRecordsLoaded set mcc imsi=" + (VDBG ? ("=" + mImsi) : ""));
+            String imsi = getIMSI();
+
+            if (!TextUtils.isEmpty(imsi)) {
+                log("onAllRecordsLoaded set mcc imsi=" + (VDBG ? ("=" + imsi) : ""));
                 mTelephonyManager.setSimCountryIsoForPhone(
                         mParentApp.getPhoneId(),
                         MccTable.countryCodeForMcc(
-                        Integer.parseInt(mImsi.substring(0,3))));
+                        Integer.parseInt(imsi.substring(0, 3))));
             } else {
                 log("onAllRecordsLoaded empty imsi skipping setting mcc");
             }
@@ -796,9 +795,9 @@
         // TODO: The below is hacky since the SubscriptionController may not be ready at this time.
         if (!TextUtils.isEmpty(mMdn)) {
             int phoneId = mParentApp.getUiccCard().getPhoneId();
-            int[] subIds = SubscriptionController.getInstance().getSubId(phoneId);
-            if (subIds != null) {
-                SubscriptionManager.from(mContext).setDisplayNumber(mMdn, subIds[0]);
+            int subId = SubscriptionController.getInstance().getSubIdUsingPhoneId(phoneId);
+            if (SubscriptionManager.isValidSubscriptionId(subId)) {
+                SubscriptionManager.from(mContext).setDisplayNumber(mMdn, subId);
             } else {
                 log("Cannot call setDisplayNumber: invalid subId");
             }
@@ -918,8 +917,8 @@
             return;
         }
 
-        if (refreshResponse.aid != null &&
-                !refreshResponse.aid.equals(mParentApp.getAid())) {
+        if (!TextUtils.isEmpty(refreshResponse.aid)
+                && !refreshResponse.aid.equals(mParentApp.getAid())) {
             // This is for different app. Ignore.
             return;
         }
diff --git a/src/java/com/android/internal/telephony/uicc/SIMRecords.java b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
index 61ce151..dad1ee2 100755
--- a/src/java/com/android/internal/telephony/uicc/SIMRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
@@ -33,7 +33,6 @@
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.MccTable;
 import com.android.internal.telephony.SmsConstants;
-import com.android.internal.telephony.SubscriptionController;
 import com.android.internal.telephony.gsm.SimTlv;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
@@ -251,6 +250,7 @@
         mCi.unSetOnSmsOnSim(this);
         mParentApp.unregisterForReady(this);
         mParentApp.unregisterForLocked(this);
+        mContext.unregisterReceiver(mReceiver);
         resetRecords();
         super.dispose();
     }
@@ -296,33 +296,14 @@
         mRecordsRequested = false;
     }
 
-
     //***** Public Methods
 
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public String getIMSI() {
-        return mImsi;
-    }
-
     @Override
     public String getMsisdnNumber() {
         return mMsisdn;
     }
 
     @Override
-    public String getGid1() {
-        return mGid1;
-    }
-
-    @Override
-    public String getGid2() {
-        return mGid2;
-    }
-
-    @Override
     public UsimServiceTable getUsimServiceTable() {
         return mUsimServiceTable;
     }
@@ -582,7 +563,8 @@
                 // Spec reference for EF_CFIS contents, TS 51.011 section 10.3.46.
                 if (enable && !TextUtils.isEmpty(dialNumber)) {
                     logv("EF_CFIS: updating cf number, " + Rlog.pii(LOG_TAG, dialNumber));
-                    byte[] bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD(dialNumber);
+                    byte[] bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD(
+                            dialNumber, PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN);
 
                     System.arraycopy(bcdNumber, 0, mEfCfis, CFIS_TON_NPI_OFFSET, bcdNumber.length);
 
@@ -639,7 +621,8 @@
      */
     @Override
     public String getOperatorNumeric() {
-        if (mImsi == null) {
+        String imsi = getIMSI();
+        if (imsi == null) {
             log("getOperatorNumeric: IMSI == null");
             return null;
         }
@@ -650,7 +633,11 @@
 
         // Length = length of MCC + length of MNC
         // length of mcc = 3 (TS 23.003 Section 2.2)
-        return mImsi.substring(0, 3 + mMncLength);
+        if (imsi.length() >= 3 + mMncLength) {
+            return imsi.substring(0, 3 + mMncLength);
+        } else {
+            return null;
+        }
     }
 
     // ***** Overridden from Handler
@@ -700,11 +687,17 @@
                     }
 
                     log("IMSI: mMncLength=" + mMncLength);
-                    log("IMSI: " + mImsi.substring(0, 6) + Rlog.pii(LOG_TAG, mImsi.substring(6)));
+
+                    if (mImsi != null && mImsi.length() >= 6) {
+                        log("IMSI: " + mImsi.substring(0, 6)
+                                + Rlog.pii(LOG_TAG, mImsi.substring(6)));
+                    }
+
+                    String imsi = getIMSI();
 
                     if (((mMncLength == UNKNOWN) || (mMncLength == 2))
-                            && ((mImsi != null) && (mImsi.length() >= 6))) {
-                        String mccmncCode = mImsi.substring(0, 6);
+                            && ((imsi != null) && (imsi.length() >= 6))) {
+                        String mccmncCode = imsi.substring(0, 6);
                         for (String mccmnc : MCCMNC_CODES_HAVING_3DIGITS_MNC) {
                             if (mccmnc.equals(mccmncCode)) {
                                 mMncLength = 3;
@@ -718,7 +711,7 @@
                         // the SIM has told us all it knows, but it didn't know the mnc length.
                         // guess using the mcc
                         try {
-                            int mcc = Integer.parseInt(mImsi.substring(0, 3));
+                            int mcc = Integer.parseInt(imsi.substring(0, 3));
                             mMncLength = MccTable.smallestDigitsMccForMnc(mcc);
                             log("setting2 mMncLength=" + mMncLength);
                         } catch (NumberFormatException e) {
@@ -727,12 +720,13 @@
                         }
                     }
 
-                    if (mMncLength != UNKNOWN && mMncLength != UNINITIALIZED) {
-                        log("update mccmnc=" + mImsi.substring(0, 3 + mMncLength));
+                    if (mMncLength != UNKNOWN && mMncLength != UNINITIALIZED
+                            && imsi.length() >= 3 + mMncLength) {
+                        log("update mccmnc=" + imsi.substring(0, 3 + mMncLength));
                         // finally have both the imsi and the mncLength and
                         // can parse the imsi properly
                         MccTable.updateMccMncConfiguration(mContext,
-                                mImsi.substring(0, 3 + mMncLength), false);
+                                imsi.substring(0, 3 + mMncLength), false);
                     }
                     mImsiReadyRegistrants.notifyRegistrants();
                     break;
@@ -926,28 +920,40 @@
                     try {
                         isRecordLoadResponse = true;
 
-                        ar = (AsyncResult) msg.obj;
-                        data = (byte[]) ar.result;
+                        if (mCarrierTestOverride.isInTestMode() && getIMSI() != null) {
+                            imsi = getIMSI();
+                            try {
+                                int mcc = Integer.parseInt(imsi.substring(0, 3));
+                                mMncLength = MccTable.smallestDigitsMccForMnc(mcc);
+                                log("[TestMode] mMncLength=" + mMncLength);
+                            } catch (NumberFormatException e) {
+                                mMncLength = UNKNOWN;
+                                loge("[TestMode] Corrupt IMSI! mMncLength=" + mMncLength);
+                            }
+                        } else {
+                            ar = (AsyncResult) msg.obj;
+                            data = (byte[]) ar.result;
 
-                        if (ar.exception != null) {
-                            break;
+                            if (ar.exception != null) {
+                                break;
+                            }
+
+                            log("EF_AD: " + IccUtils.bytesToHexString(data));
+
+                            if (data.length < 3) {
+                                log("Corrupt AD data on SIM");
+                                break;
+                            }
+
+                            if (data.length == 3) {
+                                log("MNC length not present in EF_AD");
+                                break;
+                            }
+
+                            mMncLength = data[3] & 0xf;
+                            log("setting4 mMncLength=" + mMncLength);
                         }
 
-                        log("EF_AD: " + IccUtils.bytesToHexString(data));
-
-                        if (data.length < 3) {
-                            log("Corrupt AD data on SIM");
-                            break;
-                        }
-
-                        if (data.length == 3) {
-                            log("MNC length not present in EF_AD");
-                            break;
-                        }
-
-                        mMncLength = data[3] & 0xf;
-                        log("setting4 mMncLength=" + mMncLength);
-
                         if (mMncLength == 0xf) {
                             mMncLength = UNKNOWN;
                             log("setting5 mMncLength=" + mMncLength);
@@ -956,10 +962,14 @@
                             log("setting5 mMncLength=" + mMncLength);
                         }
                     } finally {
+
+                        // IMSI could be a value reading from Sim or a fake IMSI if in the test mode
+                        imsi = getIMSI();
+
                         if (((mMncLength == UNINITIALIZED) || (mMncLength == UNKNOWN)
-                                    || (mMncLength == 2)) && ((mImsi != null)
-                                    && (mImsi.length() >= 6))) {
-                            String mccmncCode = mImsi.substring(0, 6);
+                                    || (mMncLength == 2)) && ((imsi != null)
+                                    && (imsi.length() >= 6))) {
+                            String mccmncCode = imsi.substring(0, 6);
                             log("mccmncCode=" + mccmncCode);
                             for (String mccmnc : MCCMNC_CODES_HAVING_3DIGITS_MNC) {
                                 if (mccmnc.equals(mccmncCode)) {
@@ -971,9 +981,9 @@
                         }
 
                         if (mMncLength == UNKNOWN || mMncLength == UNINITIALIZED) {
-                            if (mImsi != null) {
+                            if (imsi != null) {
                                 try {
-                                    int mcc = Integer.parseInt(mImsi.substring(0, 3));
+                                    int mcc = Integer.parseInt(imsi.substring(0, 3));
 
                                     mMncLength = MccTable.smallestDigitsMccForMnc(mcc);
                                     log("setting7 mMncLength=" + mMncLength);
@@ -988,12 +998,13 @@
                                         + "mMncLength=" + mMncLength);
                             }
                         }
-                        if (mImsi != null && mMncLength != UNKNOWN) {
+                        if (imsi != null && mMncLength != UNKNOWN
+                                && imsi.length() >= 3 + mMncLength) {
                             // finally have both imsi and the length of the mnc and can parse
                             // the imsi properly
-                            log("update mccmnc=" + mImsi.substring(0, 3 + mMncLength));
+                            log("update mccmnc=" + imsi.substring(0, 3 + mMncLength));
                             MccTable.updateMccMncConfiguration(mContext,
-                                    mImsi.substring(0, 3 + mMncLength), false);
+                                    imsi.substring(0, 3 + mMncLength), false);
                         }
                     }
                     break;
@@ -1249,7 +1260,9 @@
                         mGid1 = null;
                         break;
                     }
+
                     mGid1 = IccUtils.bytesToHexString(data);
+
                     log("GID1: " + mGid1);
 
                     break;
@@ -1264,12 +1277,15 @@
                         mGid2 = null;
                         break;
                     }
+
                     mGid2 = IccUtils.bytesToHexString(data);
+
                     log("GID2: " + mGid2);
 
                     break;
 
                 case EVENT_GET_PLMN_W_ACT_DONE:
+                    isRecordLoadResponse = true;
                     ar = (AsyncResult) msg.obj;
                     data = (byte[]) ar.result;
 
@@ -1284,6 +1300,7 @@
                     break;
 
                 case EVENT_GET_OPLMN_W_ACT_DONE:
+                    isRecordLoadResponse = true;
                     ar = (AsyncResult) msg.obj;
                     data = (byte[]) ar.result;
 
@@ -1299,6 +1316,7 @@
                     break;
 
                 case EVENT_GET_HPLMN_W_ACT_DONE:
+                    isRecordLoadResponse = true;
                     ar = (AsyncResult) msg.obj;
                     data = (byte[]) ar.result;
 
@@ -1313,6 +1331,7 @@
                     break;
 
                 case EVENT_GET_EHPLMN_DONE:
+                    isRecordLoadResponse = true;
                     ar = (AsyncResult) msg.obj;
                     data = (byte[]) ar.result;
                     if (ar.exception != null || data == null) {
@@ -1438,8 +1457,8 @@
             return;
         }
 
-        if (refreshResponse.aid != null &&
-                !refreshResponse.aid.equals(mParentApp.getAid())) {
+        if (!TextUtils.isEmpty(refreshResponse.aid)
+                && !refreshResponse.aid.equals(mParentApp.getAid())) {
             // This is for different app. Ignore.
             return;
         }
@@ -1588,20 +1607,19 @@
         if (!TextUtils.isEmpty(operator)) {
             log("onAllRecordsLoaded set 'gsm.sim.operator.numeric' to operator='" +
                     operator + "'");
-            log("update icc_operator_numeric=" + operator);
             mTelephonyManager.setSimOperatorNumericForPhone(
                     mParentApp.getPhoneId(), operator);
-            final SubscriptionController subController = SubscriptionController.getInstance();
-            subController.setMccMnc(operator, subController.getDefaultSubId());
         } else {
             log("onAllRecordsLoaded empty 'gsm.sim.operator.numeric' skipping");
         }
 
-        if (!TextUtils.isEmpty(mImsi)) {
-            log("onAllRecordsLoaded set mcc imsi" + (VDBG ? ("=" + mImsi) : ""));
+        String imsi = getIMSI();
+
+        if (!TextUtils.isEmpty(imsi) && imsi.length() >= 3) {
+            log("onAllRecordsLoaded set mcc imsi" + (VDBG ? ("=" + imsi) : ""));
             mTelephonyManager.setSimCountryIsoForPhone(
                     mParentApp.getPhoneId(), MccTable.countryCodeForMcc(
-                    Integer.parseInt(mImsi.substring(0,3))));
+                    Integer.parseInt(imsi.substring(0, 3))));
         } else {
             log("onAllRecordsLoaded empty imsi skipping setting mcc");
         }
@@ -1753,12 +1771,16 @@
         mRecordsToLoad++;
 
         mFh.loadEFTransparent(EF_PLMN_W_ACT, obtainMessage(EVENT_GET_PLMN_W_ACT_DONE));
+        mRecordsToLoad++;
 
         mFh.loadEFTransparent(EF_OPLMN_W_ACT, obtainMessage(EVENT_GET_OPLMN_W_ACT_DONE));
+        mRecordsToLoad++;
 
         mFh.loadEFTransparent(EF_HPLMN_W_ACT, obtainMessage(EVENT_GET_HPLMN_W_ACT_DONE));
+        mRecordsToLoad++;
 
         mFh.loadEFTransparent(EF_EHPLMN, obtainMessage(EVENT_GET_EHPLMN_DONE));
+        mRecordsToLoad++;
 
         mFh.loadEFTransparent(EF_FPLMN, obtainMessage(
                     EVENT_GET_FPLMN_DONE, HANDLER_ACTION_NONE, -1));
@@ -1905,7 +1927,7 @@
                     mSpnDisplayCondition = 0xff & data[0];
 
                     setServiceProviderName(IccUtils.adnStringFieldToString(
-                            data, 1, data.length - 1));
+                                data, 1, data.length - 1));
                     // for card double-check and brand override
                     // we have to do this:
                     final String spn = getServiceProviderName();
@@ -1939,7 +1961,7 @@
                     data = (byte[]) ar.result;
 
                     setServiceProviderName(IccUtils.adnStringFieldToString(
-                            data, 0, data.length));
+                                data, 0, data.length));
                     // for card double-check and brand override
                     // we have to do this:
                     final String spn = getServiceProviderName();
@@ -1971,7 +1993,7 @@
                     data = (byte[]) ar.result;
 
                     setServiceProviderName(IccUtils.adnStringFieldToString(
-                            data, 0, data.length));
+                                data, 0, data.length));
                     // for card double-check and brand override
                     // we have to do this:
                     final String spn = getServiceProviderName();
@@ -2029,10 +2051,10 @@
 
         for (int i = 0 ; i + 2 < plmnEntries.length ; i += 3) {
             String plmnCode;
-            plmnCode = IccUtils.bcdToString(plmnEntries, i, 3);
+            plmnCode = IccUtils.bcdPlmnToString(plmnEntries, i);
 
             // Valid operator codes are 5 or 6 digits
-            if (plmnCode.length() >= 5) {
+            if (plmnCode != null && plmnCode.length() >= 5) {
                 log("EF_SPDI network: " + plmnCode);
                 mSpdiNetworks.add(plmnCode);
             }
@@ -2154,7 +2176,13 @@
         pw.println(" mPnnHomeName=" + mPnnHomeName);
         pw.println(" mUsimServiceTable=" + mUsimServiceTable);
         pw.println(" mGid1=" + mGid1);
+        if (mCarrierTestOverride.isInTestMode()) {
+            pw.println(" mFakeGid1=" + ((mFakeGid1 != null) ? mFakeGid1 : "null"));
+        }
         pw.println(" mGid2=" + mGid2);
+        if (mCarrierTestOverride.isInTestMode()) {
+            pw.println(" mFakeGid2=" + ((mFakeGid2 != null) ? mFakeGid2 : "null"));
+        }
         pw.println(" mPlmnActRecords[]=" + Arrays.toString(mPlmnActRecords));
         pw.println(" mOplmnActRecords[]=" + Arrays.toString(mOplmnActRecords));
         pw.println(" mHplmnActRecords[]=" + Arrays.toString(mHplmnActRecords));
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCard.java b/src/java/com/android/internal/telephony/uicc/UiccCard.java
index 1a038cf..baad60b 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCard.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCard.java
@@ -44,18 +44,16 @@
 import android.util.LocalLog;
 import android.view.WindowManager;
 
+import com.android.internal.R;
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.CommandsInterface.RadioState;
+import com.android.internal.telephony.cat.CatService;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
 import com.android.internal.telephony.uicc.IccCardStatus.CardState;
 import com.android.internal.telephony.uicc.IccCardStatus.PinState;
-import com.android.internal.telephony.cat.CatService;
-
-import com.android.internal.R;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
@@ -96,27 +94,19 @@
     private static final int EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE = 17;
     private static final int EVENT_TRANSMIT_APDU_BASIC_CHANNEL_DONE = 18;
     private static final int EVENT_SIM_IO_DONE = 19;
-    private static final int EVENT_CARRIER_PRIVILIGES_LOADED = 20;
+    private static final int EVENT_CARRIER_PRIVILEGES_LOADED = 20;
 
     private static final LocalLog mLocalLog = new LocalLog(100);
 
-    private int mPhoneId;
-
-    public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics) {
-        if (DBG) log("Creating");
-        mCardState = ics.mCardState;
-        update(c, ci, ics);
-    }
+    private final int mPhoneId;
 
     public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId) {
+        if (DBG) log("Creating");
         mCardState = ics.mCardState;
         mPhoneId = phoneId;
         update(c, ci, ics);
     }
 
-    protected UiccCard() {
-    }
-
     public void dispose() {
         synchronized (mLock) {
             if (DBG) log("Disposing card");
@@ -162,18 +152,19 @@
                 }
             }
 
-            createAndUpdateCatService();
+            createAndUpdateCatServiceLocked();
 
             // Reload the carrier privilege rules if necessary.
             log("Before privilege rules: " + mCarrierPrivilegeRules + " : " + mCardState);
             if (mCarrierPrivilegeRules == null && mCardState == CardState.CARDSTATE_PRESENT) {
                 mCarrierPrivilegeRules = new UiccCarrierPrivilegeRules(this,
-                        mHandler.obtainMessage(EVENT_CARRIER_PRIVILIGES_LOADED));
-            } else if (mCarrierPrivilegeRules != null && mCardState != CardState.CARDSTATE_PRESENT) {
+                        mHandler.obtainMessage(EVENT_CARRIER_PRIVILEGES_LOADED));
+            } else if (mCarrierPrivilegeRules != null
+                    && mCardState != CardState.CARDSTATE_PRESENT) {
                 mCarrierPrivilegeRules = null;
             }
 
-            sanitizeApplicationIndexes();
+            sanitizeApplicationIndexesLocked();
 
             RadioState radioState = mCi.getRadioState();
             if (DBG) log("update: radioState=" + radioState + " mLastRadioState="
@@ -195,13 +186,13 @@
         }
     }
 
-    protected void createAndUpdateCatService() {
+    private void createAndUpdateCatServiceLocked() {
         if (mUiccApplications.length > 0 && mUiccApplications[0] != null) {
             // Initialize or Reinitialize CatService
             if (mCatService == null) {
                 mCatService = CatService.getInstance(mCi, mContext, this, mPhoneId);
             } else {
-                ((CatService)mCatService).update(mCi, mContext, this);
+                mCatService.update(mCi, mContext, this);
             }
         } else {
             if (mCatService != null) {
@@ -211,10 +202,6 @@
         }
     }
 
-    public CatService getCatService() {
-        return mCatService;
-    }
-
     @Override
     protected void finalize() {
         if (DBG) log("UiccCard finalized");
@@ -225,16 +212,18 @@
      * and resets invalid indexes. (This should never happen, but in case
      * RIL misbehaves we need to manage situation gracefully)
      */
-    private void sanitizeApplicationIndexes() {
+    private void sanitizeApplicationIndexesLocked() {
         mGsmUmtsSubscriptionAppIndex =
-                checkIndex(mGsmUmtsSubscriptionAppIndex, AppType.APPTYPE_SIM, AppType.APPTYPE_USIM);
+                checkIndexLocked(
+                        mGsmUmtsSubscriptionAppIndex, AppType.APPTYPE_SIM, AppType.APPTYPE_USIM);
         mCdmaSubscriptionAppIndex =
-                checkIndex(mCdmaSubscriptionAppIndex, AppType.APPTYPE_RUIM, AppType.APPTYPE_CSIM);
+                checkIndexLocked(
+                        mCdmaSubscriptionAppIndex, AppType.APPTYPE_RUIM, AppType.APPTYPE_CSIM);
         mImsSubscriptionAppIndex =
-                checkIndex(mImsSubscriptionAppIndex, AppType.APPTYPE_ISIM, null);
+                checkIndexLocked(mImsSubscriptionAppIndex, AppType.APPTYPE_ISIM, null);
     }
 
-    private int checkIndex(int index, AppType expectedAppType, AppType altExpectedAppType) {
+    private int checkIndexLocked(int index, AppType expectedAppType, AppType altExpectedAppType) {
         if (mUiccApplications == null || index >= mUiccApplications.length) {
             loge("App index " + index + " is invalid since there are no applications");
             return -1;
@@ -395,7 +384,7 @@
                     AsyncResult.forMessage((Message)ar.userObj, ar.result, ar.exception);
                     ((Message)ar.userObj).sendToTarget();
                     break;
-                case EVENT_CARRIER_PRIVILIGES_LOADED:
+                case EVENT_CARRIER_PRIVILEGES_LOADED:
                     onCarrierPriviligesLoadedMessage();
                     break;
                 default:
@@ -562,22 +551,31 @@
         synchronized (mLock) {
             boolean changed = false;
             for (int i = 0; i < mUiccApplications.length; i++) {
-                if (mUiccApplications[i] != null &&
-                    (aid == null || aid.equals(mUiccApplications[i].getAid()))) {
+                if (mUiccApplications[i] != null
+                        && (TextUtils.isEmpty(aid) || aid.equals(mUiccApplications[i].getAid()))) {
                     // Delete removed applications
                     mUiccApplications[i].dispose();
                     mUiccApplications[i] = null;
                     changed = true;
                 }
             }
+            if (TextUtils.isEmpty(aid)) {
+                if (mCarrierPrivilegeRules != null) {
+                    mCarrierPrivilegeRules = null;
+                    changed = true;
+                }
+                if (mCatService != null) {
+                    mCatService.dispose();
+                    mCatService = null;
+                    changed = true;
+                }
+            }
             return changed;
         }
-        // TODO: For a card level notification, we should delete the CarrierPrivilegeRules and the
-        // CAT service.
     }
 
     /**
-     * Exposes {@link CommandsInterface.iccOpenLogicalChannel}
+     * Exposes {@link CommandsInterface#iccOpenLogicalChannel}
      */
     public void iccOpenLogicalChannel(String AID, int p2, Message response) {
         loglocal("Open Logical Channel: " + AID + " , " + p2 + " by pid:" + Binder.getCallingPid()
@@ -587,7 +585,7 @@
     }
 
     /**
-     * Exposes {@link CommandsInterface.iccCloseLogicalChannel}
+     * Exposes {@link CommandsInterface#iccCloseLogicalChannel}
      */
     public void iccCloseLogicalChannel(int channel, Message response) {
         loglocal("Close Logical Channel: " + channel);
@@ -596,7 +594,7 @@
     }
 
     /**
-     * Exposes {@link CommandsInterface.iccTransmitApduLogicalChannel}
+     * Exposes {@link CommandsInterface#iccTransmitApduLogicalChannel}
      */
     public void iccTransmitApduLogicalChannel(int channel, int cla, int command,
             int p1, int p2, int p3, String data, Message response) {
@@ -605,7 +603,7 @@
     }
 
     /**
-     * Exposes {@link CommandsInterface.iccTransmitApduBasicChannel}
+     * Exposes {@link CommandsInterface#iccTransmitApduBasicChannel}
      */
     public void iccTransmitApduBasicChannel(int cla, int command,
             int p1, int p2, int p3, String data, Message response) {
@@ -614,7 +612,7 @@
     }
 
     /**
-     * Exposes {@link CommandsInterface.iccIO}
+     * Exposes {@link CommandsInterface#iccIO}
      */
     public void iccExchangeSimIO(int fileID, int command, int p1, int p2, int p3,
             String pathID, Message response) {
@@ -623,7 +621,7 @@
     }
 
     /**
-     * Exposes {@link CommandsInterface.sendEnvelopeWithStatus}
+     * Exposes {@link CommandsInterface#sendEnvelopeWithStatus}
      */
     public void sendEnvelopeWithStatus(String contents, Message response) {
         mCi.sendEnvelopeWithStatus(contents, response);
@@ -648,62 +646,76 @@
      * Returns true iff carrier privileges rules are null (dont need to be loaded) or loaded.
      */
     public boolean areCarrierPriviligeRulesLoaded() {
-        return mCarrierPrivilegeRules == null
-            || mCarrierPrivilegeRules.areCarrierPriviligeRulesLoaded();
+        UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
+        return carrierPrivilegeRules == null
+                || carrierPrivilegeRules.areCarrierPriviligeRulesLoaded();
     }
 
     /**
      * Returns true if there are some carrier privilege rules loaded and specified.
      */
     public boolean hasCarrierPrivilegeRules() {
-        return mCarrierPrivilegeRules != null
-                && mCarrierPrivilegeRules.hasCarrierPrivilegeRules();
+        UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
+        return carrierPrivilegeRules != null && carrierPrivilegeRules.hasCarrierPrivilegeRules();
     }
 
     /**
-     * Exposes {@link UiccCarrierPrivilegeRules.getCarrierPrivilegeStatus}.
+     * Exposes {@link UiccCarrierPrivilegeRules#getCarrierPrivilegeStatus}.
      */
     public int getCarrierPrivilegeStatus(Signature signature, String packageName) {
-        return mCarrierPrivilegeRules == null ?
-            TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
-            mCarrierPrivilegeRules.getCarrierPrivilegeStatus(signature, packageName);
+        UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
+        return carrierPrivilegeRules == null
+                ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
+                carrierPrivilegeRules.getCarrierPrivilegeStatus(signature, packageName);
     }
 
     /**
-     * Exposes {@link UiccCarrierPrivilegeRules.getCarrierPrivilegeStatus}.
+     * Exposes {@link UiccCarrierPrivilegeRules#getCarrierPrivilegeStatus}.
      */
     public int getCarrierPrivilegeStatus(PackageManager packageManager, String packageName) {
-        return mCarrierPrivilegeRules == null ?
-            TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
-            mCarrierPrivilegeRules.getCarrierPrivilegeStatus(packageManager, packageName);
+        UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
+        return carrierPrivilegeRules == null
+                ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
+                carrierPrivilegeRules.getCarrierPrivilegeStatus(packageManager, packageName);
     }
 
     /**
-     * Exposes {@link UiccCarrierPrivilegeRules.getCarrierPrivilegeStatus}.
+     * Exposes {@link UiccCarrierPrivilegeRules#getCarrierPrivilegeStatus}.
      */
     public int getCarrierPrivilegeStatus(PackageInfo packageInfo) {
-        return mCarrierPrivilegeRules == null ?
-            TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
-            mCarrierPrivilegeRules.getCarrierPrivilegeStatus(packageInfo);
+        UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
+        return carrierPrivilegeRules == null
+                ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
+                carrierPrivilegeRules.getCarrierPrivilegeStatus(packageInfo);
     }
 
     /**
-     * Exposes {@link UiccCarrierPrivilegeRules.getCarrierPrivilegeStatusForCurrentTransaction}.
+     * Exposes {@link UiccCarrierPrivilegeRules#getCarrierPrivilegeStatusForCurrentTransaction}.
      */
     public int getCarrierPrivilegeStatusForCurrentTransaction(PackageManager packageManager) {
-        return mCarrierPrivilegeRules == null ?
-            TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
-            mCarrierPrivilegeRules.getCarrierPrivilegeStatusForCurrentTransaction(packageManager);
+        UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
+        return carrierPrivilegeRules == null
+                ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
+                carrierPrivilegeRules.getCarrierPrivilegeStatusForCurrentTransaction(
+                        packageManager);
     }
 
     /**
-     * Exposes {@link UiccCarrierPrivilegeRules.getCarrierPackageNamesForIntent}.
+     * Exposes {@link UiccCarrierPrivilegeRules#getCarrierPackageNamesForIntent}.
      */
     public List<String> getCarrierPackageNamesForIntent(
             PackageManager packageManager, Intent intent) {
-        return mCarrierPrivilegeRules == null ? null :
-            mCarrierPrivilegeRules.getCarrierPackageNamesForIntent(
-                    packageManager, intent);
+        UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
+        return carrierPrivilegeRules == null ? null :
+                carrierPrivilegeRules.getCarrierPackageNamesForIntent(
+                        packageManager, intent);
+    }
+
+    /** Returns a reference to the current {@link UiccCarrierPrivilegeRules}. */
+    private UiccCarrierPrivilegeRules getCarrierPrivilegeRules() {
+        synchronized (mLock) {
+            return mCarrierPrivilegeRules;
+        }
     }
 
     public boolean setOperatorBrandOverride(String brand) {
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java b/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
index 3ec7f34..68b72c8 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
@@ -31,20 +31,11 @@
 import android.text.TextUtils;
 
 import com.android.internal.telephony.CommandException;
-import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.uicc.IccUtils;
 
-import java.io.ByteArrayInputStream;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.lang.IllegalArgumentException;
-import java.lang.IndexOutOfBoundsException;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -65,7 +56,10 @@
     private static final String LOG_TAG = "UiccCarrierPrivilegeRules";
     private static final boolean DBG = false;
 
-    private static final String AID = "A00000015141434C00";
+    private static final String ARAM_AID = "A00000015141434C00";
+    private static final String ARAD_AID = "A00000015144414300";
+    private static final int ARAM = 1;
+    private static final int ARAD = 0;
     private static final int CLA = 0x80;
     private static final int COMMAND = 0xCA;
     private static final int P1 = 0xFF;
@@ -103,6 +97,8 @@
     private static final String TAG_PKG_REF_DO = "CA";
     private static final String TAG_AR_DO = "E3";
     private static final String TAG_PERM_AR_DO = "DB";
+    private static final String TAG_AID_REF_DO = "4F";
+    private static final String CARRIER_PRIVILEGE_AID = "FFFFFFFFFFFF";
 
     private static final int EVENT_OPEN_LOGICAL_CHANNEL_DONE = 1;
     private static final int EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE = 2;
@@ -217,18 +213,21 @@
     private String mStatusMessage;  // Only used for debugging.
     private int mChannelId; // Channel Id for communicating with UICC.
     private int mRetryCount;  // Number of retries for open logical channel.
+    private boolean mCheckedRules = false;  // Flag that used to mark whether get rules from ARA-D.
+    private int mAIDInUse;  // Message component to identify which AID is currently in-use.
     private final Runnable mRetryRunnable = new Runnable() {
         @Override
         public void run() {
-            openChannel();
+            openChannel(mAIDInUse);
         }
     };
 
-    private void openChannel() {
+    private void openChannel(int aidId) {
         // Send open logical channel request.
+        String aid = (aidId == ARAD) ? ARAD_AID : ARAM_AID;
         int p2 = 0x00;
-        mUiccCard.iccOpenLogicalChannel(AID, p2, /* supported p2 value */
-            obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE, null));
+        mUiccCard.iccOpenLogicalChannel(aid, p2, /* supported p2 value */
+                obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE, 0, aidId, null));
     }
 
     public UiccCarrierPrivilegeRules(UiccCard uiccCard, Message loadedCallback) {
@@ -240,7 +239,9 @@
         mRules = "";
         mAccessRules = new ArrayList<AccessRule>();
 
-        openChannel();
+        // Open logical channel with ARA_D.
+        mAIDInUse = ARAD;
+        openChannel(mAIDInUse);
     }
 
     /**
@@ -419,91 +420,120 @@
     @Override
     public void handleMessage(Message msg) {
         AsyncResult ar;
+        mAIDInUse = msg.arg2;  // 0 means ARA-D and 1 means ARA-M.
 
         switch (msg.what) {
 
-          case EVENT_OPEN_LOGICAL_CHANNEL_DONE:
-              log("EVENT_OPEN_LOGICAL_CHANNEL_DONE");
-              ar = (AsyncResult) msg.obj;
-              if (ar.exception == null && ar.result != null) {
-                  mChannelId = ((int[]) ar.result)[0];
-                  mUiccCard.iccTransmitApduLogicalChannel(mChannelId, CLA, COMMAND, P1, P2, P3, DATA,
-                      obtainMessage(EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE, new Integer(mChannelId)));
-              } else {
-                  // MISSING_RESOURCE could be due to logical channels temporarily unavailable,
-                  // so we retry up to MAX_RETRY times, with an interval of RETRY_INTERVAL_MS.
-                  if (ar.exception instanceof CommandException && mRetryCount < MAX_RETRY &&
-                      ((CommandException) (ar.exception)).getCommandError() ==
-                              CommandException.Error.MISSING_RESOURCE) {
-                      mRetryCount++;
-                      removeCallbacks(mRetryRunnable);
-                      postDelayed(mRetryRunnable, RETRY_INTERVAL_MS);
-                  } else {
-                      // if rules cannot be read from ARA applet,
-                      // fallback to PKCS15-based ARF.
-                      log("No ARA, try ARF next.");
-                      mUiccPkcs15 = new UiccPkcs15(mUiccCard,
-                              obtainMessage(EVENT_PKCS15_READ_DONE));
-                  }
-              }
-              break;
+            case EVENT_OPEN_LOGICAL_CHANNEL_DONE:
+                log("EVENT_OPEN_LOGICAL_CHANNEL_DONE");
+                ar = (AsyncResult) msg.obj;
+                if (ar.exception == null && ar.result != null) {
+                    mChannelId = ((int[]) ar.result)[0];
+                    mUiccCard.iccTransmitApduLogicalChannel(mChannelId, CLA, COMMAND, P1, P2, P3,
+                            DATA, obtainMessage(EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE, mChannelId,
+                                    mAIDInUse));
+                } else {
+                    // MISSING_RESOURCE could be due to logical channels temporarily unavailable,
+                    // so we retry up to MAX_RETRY times, with an interval of RETRY_INTERVAL_MS.
+                    if (ar.exception instanceof CommandException && mRetryCount < MAX_RETRY
+                            && ((CommandException) (ar.exception)).getCommandError()
+                            == CommandException.Error.MISSING_RESOURCE) {
+                        mRetryCount++;
+                        removeCallbacks(mRetryRunnable);
+                        postDelayed(mRetryRunnable, RETRY_INTERVAL_MS);
+                    } else {
+                        if (mAIDInUse == ARAD) {
+                            // Open logical channel with ARA_M.
+                            mRules = "";
+                            openChannel(1);
+                        }
+                        if (mAIDInUse == ARAM) {
+                            if (mCheckedRules) {
+                                updateState(STATE_LOADED, "Success!");
+                            } else {
+                                // if rules cannot be read from both ARA_D and ARA_M applet,
+                                // fallback to PKCS15-based ARF.
+                                log("No ARA, try ARF next.");
+                                mUiccPkcs15 = new UiccPkcs15(mUiccCard,
+                                        obtainMessage(EVENT_PKCS15_READ_DONE));
+                            }
+                        }
+                    }
+                }
+                break;
 
-          case EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE:
-              log("EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE");
-              ar = (AsyncResult) msg.obj;
-              if (ar.exception == null && ar.result != null) {
-                  IccIoResult response = (IccIoResult) ar.result;
-                  if (response.sw1 == 0x90 && response.sw2 == 0x00 &&
-                      response.payload != null && response.payload.length > 0) {
-                      try {
-                          mRules += IccUtils.bytesToHexString(response.payload).toUpperCase(Locale.US);
-                          if (isDataComplete()) {
-                              mAccessRules = parseRules(mRules);
-                              updateState(STATE_LOADED, "Success!");
-                          } else {
-                              mUiccCard.iccTransmitApduLogicalChannel(mChannelId, CLA, COMMAND, P1, P2_EXTENDED_DATA, P3, DATA,
-                                  obtainMessage(EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE, new Integer(mChannelId)));
-                              break;
-                          }
-                      } catch (IllegalArgumentException ex) {
-                          updateState(STATE_ERROR, "Error parsing rules: " + ex);
-                      } catch (IndexOutOfBoundsException ex) {
-                          updateState(STATE_ERROR, "Error parsing rules: " + ex);
-                      }
-                   } else {
-                      String errorMsg = "Invalid response: payload=" + response.payload +
-                              " sw1=" + response.sw1 + " sw2=" + response.sw2;
-                      updateState(STATE_ERROR, errorMsg);
-                   }
-              } else {
-                  updateState(STATE_ERROR, "Error reading value from SIM.");
-              }
+            case EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE:
+                log("EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE");
+                ar = (AsyncResult) msg.obj;
+                if (ar.exception == null && ar.result != null) {
+                    IccIoResult response = (IccIoResult) ar.result;
+                    if (response.sw1 == 0x90 && response.sw2 == 0x00
+                            && response.payload != null && response.payload.length > 0) {
+                        try {
+                            mRules += IccUtils.bytesToHexString(response.payload)
+                                    .toUpperCase(Locale.US);
+                            if (isDataComplete()) {
+                                mAccessRules.addAll(parseRules(mRules));
+                                if (mAIDInUse == ARAD) {
+                                    mCheckedRules = true;
+                                } else {
+                                    updateState(STATE_LOADED, "Success!");
+                                }
+                            } else {
+                                mUiccCard.iccTransmitApduLogicalChannel(mChannelId, CLA, COMMAND,
+                                        P1, P2_EXTENDED_DATA, P3, DATA,
+                                        obtainMessage(EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE,
+                                                mChannelId, mAIDInUse));
+                                break;
+                            }
+                        } catch (IllegalArgumentException | IndexOutOfBoundsException ex) {
+                            if (mAIDInUse == ARAM) {
+                                updateState(STATE_ERROR, "Error parsing rules: " + ex);
+                            }
+                        }
+                    } else {
+                        if (mAIDInUse == ARAM) {
+                            String errorMsg = "Invalid response: payload=" + response.payload
+                                    + " sw1=" + response.sw1 + " sw2=" + response.sw2;
+                            updateState(STATE_ERROR, errorMsg);
+                        }
+                    }
+                } else {
+                    if (mAIDInUse == ARAM) {
+                        updateState(STATE_ERROR, "Error reading value from SIM.");
+                    }
+                }
 
-              mUiccCard.iccCloseLogicalChannel(mChannelId, obtainMessage(
-                      EVENT_CLOSE_LOGICAL_CHANNEL_DONE));
-              mChannelId = -1;
-              break;
+                mUiccCard.iccCloseLogicalChannel(mChannelId, obtainMessage(
+                        EVENT_CLOSE_LOGICAL_CHANNEL_DONE, 0, mAIDInUse));
+                mChannelId = -1;
+                break;
 
-          case EVENT_CLOSE_LOGICAL_CHANNEL_DONE:
-              log("EVENT_CLOSE_LOGICAL_CHANNEL_DONE");
-              break;
+            case EVENT_CLOSE_LOGICAL_CHANNEL_DONE:
+                log("EVENT_CLOSE_LOGICAL_CHANNEL_DONE");
+                if (mAIDInUse == ARAD) {
+                    // Close logical channel with ARA_D and then open logical channel with ARA_M.
+                    mRules = "";
+                    openChannel(1);
+                }
+                break;
 
-          case EVENT_PKCS15_READ_DONE:
-              log("EVENT_PKCS15_READ_DONE");
-              if (mUiccPkcs15 == null || mUiccPkcs15.getRules() == null) {
-                  updateState(STATE_ERROR, "No ARA or ARF.");
-              } else {
-                  for (String cert : mUiccPkcs15.getRules()) {
-                      AccessRule accessRule = new AccessRule(
-                              IccUtils.hexStringToBytes(cert), "", 0x00);
-                      mAccessRules.add(accessRule);
-                  }
-                  updateState(STATE_LOADED, "Success!");
-              }
-              break;
+            case EVENT_PKCS15_READ_DONE:
+                log("EVENT_PKCS15_READ_DONE");
+                if (mUiccPkcs15 == null || mUiccPkcs15.getRules() == null) {
+                    updateState(STATE_ERROR, "No ARA or ARF.");
+                } else {
+                    for (String cert : mUiccPkcs15.getRules()) {
+                        AccessRule accessRule = new AccessRule(
+                                IccUtils.hexStringToBytes(cert), "", 0x00);
+                        mAccessRules.add(accessRule);
+                    }
+                    updateState(STATE_LOADED, "Success!");
+                }
+                break;
 
-          default:
-              Rlog.e(LOG_TAG, "Unknown event " + msg.what);
+            default:
+                Rlog.e(LOG_TAG, "Unknown event " + msg.what);
         }
     }
 
@@ -518,7 +548,7 @@
             String lengthBytes = allRules.parseLength(mRules);
             log("isDataComplete lengthBytes: " + lengthBytes);
             if (mRules.length() == TAG_ALL_REF_AR_DO.length() + lengthBytes.length() +
-                                   allRules.length) {
+                    allRules.length) {
                 log("isDataComplete yes");
                 return true;
             } else {
@@ -548,7 +578,7 @@
             if (accessRule != null) {
                 accessRules.add(accessRule);
             } else {
-              Rlog.e(LOG_TAG, "Skip unrecognized rule." + refArDo.value);
+                Rlog.e(LOG_TAG, "Skip unrecognized rule." + refArDo.value);
             }
         }
         return accessRules;
@@ -569,37 +599,52 @@
             if (rule.startsWith(TAG_REF_DO)) {
                 TLV refDo = new TLV(TAG_REF_DO); //E1
                 rule = refDo.parse(rule, false);
-
-                // Skip unrelated rules.
-                if (!refDo.value.startsWith(TAG_DEVICE_APP_ID_REF_DO)) {
+                // Allow 4F tag with a default value "FF FF FF FF FF FF" to be compatible with
+                // devices having GP access control enforcer:
+                //  - If no 4F tag is present, it's a CP rule.
+                //  - If 4F tag has value "FF FF FF FF FF FF", it's a CP rule.
+                //  - If 4F tag has other values, it's not a CP rule and Android should ignore it.
+                TLV deviceDo = new TLV(TAG_DEVICE_APP_ID_REF_DO); //C1
+                if (refDo.value.startsWith(TAG_AID_REF_DO)) {
+                    TLV cpDo = new TLV(TAG_AID_REF_DO); //4F
+                    String remain = cpDo.parse(refDo.value, false);
+                    if (!cpDo.lengthBytes.equals("06") || !cpDo.value.equals(CARRIER_PRIVILEGE_AID)
+                            || remain.isEmpty() || !remain.startsWith(TAG_DEVICE_APP_ID_REF_DO)) {
+                        return null;
+                    }
+                    tmp = deviceDo.parse(remain, false);
+                    certificateHash = deviceDo.value;
+                } else if (refDo.value.startsWith(TAG_DEVICE_APP_ID_REF_DO)) {
+                    tmp = deviceDo.parse(refDo.value, false);
+                    certificateHash = deviceDo.value;
+                } else {
                     return null;
                 }
-
-                TLV deviceDo = new TLV(TAG_DEVICE_APP_ID_REF_DO); //C1
-                tmp = deviceDo.parse(refDo.value, false);
-                certificateHash = deviceDo.value;
-
                 if (!tmp.isEmpty()) {
-                  if (!tmp.startsWith(TAG_PKG_REF_DO)) {
-                      return null;
-                  }
-                  TLV pkgDo = new TLV(TAG_PKG_REF_DO); //CA
-                  pkgDo.parse(tmp, true);
-                  packageName = new String(IccUtils.hexStringToBytes(pkgDo.value));
+                    if (!tmp.startsWith(TAG_PKG_REF_DO)) {
+                        return null;
+                    }
+                    TLV pkgDo = new TLV(TAG_PKG_REF_DO); //CA
+                    pkgDo.parse(tmp, true);
+                    packageName = new String(IccUtils.hexStringToBytes(pkgDo.value));
                 } else {
-                  packageName = null;
+                    packageName = null;
                 }
             } else if (rule.startsWith(TAG_AR_DO)) {
                 TLV arDo = new TLV(TAG_AR_DO); //E3
                 rule = arDo.parse(rule, false);
-
-                // Skip unrelated rules.
-                if (!arDo.value.startsWith(TAG_PERM_AR_DO)) {
+                // Skip all the irrelevant tags (All the optional tags here are two bytes
+                // according to the spec GlobalPlatform Secure Element Access Control).
+                String remain = arDo.value;
+                while (!remain.isEmpty() && !remain.startsWith(TAG_PERM_AR_DO)) {
+                    TLV tmpDo = new TLV(remain.substring(0, 2));
+                    remain = tmpDo.parse(remain, false);
+                }
+                if (remain.isEmpty()) {
                     return null;
                 }
-
                 TLV permDo = new TLV(TAG_PERM_AR_DO); //DB
-                permDo.parse(arDo.value, true);
+                permDo.parse(remain, true);
             } else  {
                 // Spec requires it must be either TAG_REF_DO or TAG_AR_DO.
                 throw new RuntimeException("Invalid Rule type");
@@ -668,15 +713,15 @@
      * Converts state into human readable format.
      */
     private String getStateString(int state) {
-      switch (state) {
-        case STATE_LOADING:
-            return "STATE_LOADING";
-        case STATE_LOADED:
-            return "STATE_LOADED";
-        case STATE_ERROR:
-            return "STATE_ERROR";
-        default:
-            return "UNKNOWN";
-      }
+        switch (state) {
+            case STATE_LOADING:
+                return "STATE_LOADING";
+            case STATE_LOADED:
+                return "STATE_LOADED";
+            case STATE_ERROR:
+                return "STATE_ERROR";
+            default:
+                return "UNKNOWN";
+        }
     }
-}
+}
\ No newline at end of file
diff --git a/src/java/com/android/internal/telephony/uicc/UiccController.java b/src/java/com/android/internal/telephony/uicc/UiccController.java
index 65aacd1..a948b75 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccController.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccController.java
@@ -89,8 +89,6 @@
     private static final int EVENT_RADIO_UNAVAILABLE = 3;
     private static final int EVENT_SIM_REFRESH = 4;
 
-    private static final String DECRYPT_STATE = "trigger_restart_framework";
-
     private CommandsInterface[] mCis;
     private UiccCard[] mUiccCards = new UiccCard[TelephonyManager.getDefault().getPhoneCount()];
 
@@ -125,12 +123,11 @@
             Integer index = new Integer(i);
             mCis[i].registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, index);
             // TODO remove this once modem correctly notifies the unsols
-            // If the device has been decrypted or FBE is supported, read SIM when radio state is
-            // available.
+            // If the device is unencrypted or has been decrypted or FBE is supported,
+            // i.e. not in cryptkeeper bounce, read SIM when radio state isavailable.
             // Else wait for radio to be on. This is needed for the scenario when SIM is locked --
             // to avoid overlap of CryptKeeper and SIM unlock screen.
-            if (DECRYPT_STATE.equals(SystemProperties.get("vold.decrypt")) ||
-                    StorageManager.isFileEncryptedNativeOrEmulated()) {
+            if (!StorageManager.inCryptKeeperBounce()) {
                 mCis[i].registerForAvailable(this, EVENT_ICC_STATUS_CHANGED, index);
             } else {
                 mCis[i].registerForOn(this, EVENT_ICC_STATUS_CHANGED, index);
@@ -342,7 +339,7 @@
             if (requirePowerOffOnSimRefreshReset) {
                 mCis[index].setRadioPower(false, null);
             } else {
-                mCis[index].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE));
+                mCis[index].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE, index));
             }
             mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null));
         }
diff --git a/src/java/com/android/internal/telephony/util/NotificationChannelController.java b/src/java/com/android/internal/telephony/util/NotificationChannelController.java
new file mode 100644
index 0000000..09519c4
--- /dev/null
+++ b/src/java/com/android/internal/telephony/util/NotificationChannelController.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony.util;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioAttributes;
+import android.net.Uri;
+import android.provider.Settings;
+import android.telephony.SubscriptionManager;
+
+import com.android.internal.R;
+
+import java.util.Arrays;
+import java.util.List;
+
+
+public class NotificationChannelController {
+
+    /**
+     * list of {@link android.app.NotificationChannel} for telephony service.
+     */
+    public static final String CHANNEL_ID_ALERT = "alert";
+    public static final String CHANNEL_ID_CALL_FORWARD = "callForward";
+    public static final String CHANNEL_ID_MOBILE_DATA_ALERT = "mobileDataAlert";
+    public static final String CHANNEL_ID_SMS = "sms";
+    public static final String CHANNEL_ID_VOICE_MAIL = "voiceMail";
+    public static final String CHANNEL_ID_WFC = "wfc";
+
+    /**
+     * Creates all notification channels and registers with NotificationManager. If a channel
+     * with the same ID is already registered, NotificationManager will ignore this call.
+     */
+    private static void createAll(Context context) {
+        final NotificationChannel alertChannel = new NotificationChannel(
+                CHANNEL_ID_ALERT,
+                context.getText(R.string.notification_channel_network_alert),
+                NotificationManager.IMPORTANCE_DEFAULT);
+        alertChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI,
+                new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).build());
+
+        context.getSystemService(NotificationManager.class)
+                .createNotificationChannels(Arrays.asList(
+                new NotificationChannel(CHANNEL_ID_CALL_FORWARD,
+                        context.getText(R.string.notification_channel_call_forward),
+                        NotificationManager.IMPORTANCE_LOW),
+                new NotificationChannel(CHANNEL_ID_MOBILE_DATA_ALERT,
+                        context.getText(R.string.notification_channel_mobile_data_alert),
+                        NotificationManager.IMPORTANCE_DEFAULT),
+                new NotificationChannel(CHANNEL_ID_SMS,
+                        context.getText(R.string.notification_channel_sms),
+                        NotificationManager.IMPORTANCE_HIGH),
+                new NotificationChannel(CHANNEL_ID_WFC,
+                        context.getText(R.string.notification_channel_wfc),
+                        NotificationManager.IMPORTANCE_LOW),
+                alertChannel));
+        // only for update
+        if (getChannel(CHANNEL_ID_VOICE_MAIL, context) != null) {
+            migrateVoicemailNotificationSettings(context);
+        }
+    }
+
+    public NotificationChannelController(Context context) {
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_LOCALE_CHANGED);
+        intentFilter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
+        context.registerReceiver(mBroadcastReceiver, intentFilter);
+        createAll(context);
+    }
+
+    public static NotificationChannel getChannel(String channelId, Context context) {
+        return context.getSystemService(NotificationManager.class)
+                .getNotificationChannel(channelId);
+    }
+
+    /**
+     * migrate deprecated voicemail notification settings to initial notification channel settings
+     * {@link VoicemailNotificationSettingsUtil#getRingTonePreference(Context)}}
+     * {@link VoicemailNotificationSettingsUtil#getVibrationPreference(Context)}
+     * notification settings are based on subId, only migrate if sub id matches.
+     * otherwise fallback to predefined voicemail channel settings.
+     * @param context
+     */
+    private static void migrateVoicemailNotificationSettings(Context context) {
+        final NotificationChannel voiceMailChannel = new NotificationChannel(
+                CHANNEL_ID_VOICE_MAIL,
+                context.getText(R.string.notification_channel_voice_mail),
+                NotificationManager.IMPORTANCE_DEFAULT);
+        voiceMailChannel.enableVibration(
+                VoicemailNotificationSettingsUtil.getVibrationPreference(context));
+        Uri sound = VoicemailNotificationSettingsUtil.getRingTonePreference(context);
+        voiceMailChannel.setSound(
+                (sound == null) ? Settings.System.DEFAULT_NOTIFICATION_URI : sound,
+                new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).build());
+        context.getSystemService(NotificationManager.class)
+                .createNotificationChannel(voiceMailChannel);
+    }
+
+    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
+                // rename all notification channels on locale change
+                createAll(context);
+            } else if (Intent.ACTION_SIM_STATE_CHANGED.equals(intent.getAction())) {
+                // migrate voicemail notification settings on sim load
+                if (SubscriptionManager.INVALID_SUBSCRIPTION_ID !=
+                        SubscriptionManager.getDefaultSubscriptionId()) {
+                    migrateVoicemailNotificationSettings(context);
+                }
+            }
+        }
+    };
+}
diff --git a/src/java/com/android/internal/telephony/util/VoicemailNotificationSettingsUtil.java b/src/java/com/android/internal/telephony/util/VoicemailNotificationSettingsUtil.java
new file mode 100644
index 0000000..d8988e3
--- /dev/null
+++ b/src/java/com/android/internal/telephony/util/VoicemailNotificationSettingsUtil.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.util;
+
+import android.app.NotificationChannel;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.preference.PreferenceManager;
+import android.provider.Settings;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+
+public class VoicemailNotificationSettingsUtil {
+    private static final String VOICEMAIL_NOTIFICATION_RINGTONE_SHARED_PREFS_KEY_PREFIX =
+            "voicemail_notification_ringtone_";
+    private static final String VOICEMAIL_NOTIFICATION_VIBRATION_SHARED_PREFS_KEY_PREFIX =
+            "voicemail_notification_vibrate_";
+
+    // Old voicemail notification vibration string constants used for migration.
+    private static final String OLD_VOICEMAIL_NOTIFICATION_RINGTONE_SHARED_PREFS_KEY =
+            "button_voicemail_notification_ringtone_key";
+    private static final String OLD_VOICEMAIL_NOTIFICATION_VIBRATION_SHARED_PREFS_KEY =
+            "button_voicemail_notification_vibrate_key";
+    private static final String OLD_VOICEMAIL_VIBRATE_WHEN_SHARED_PREFS_KEY =
+            "button_voicemail_notification_vibrate_when_key";
+    private static final String OLD_VOICEMAIL_RINGTONE_SHARED_PREFS_KEY =
+            "button_voicemail_notification_ringtone_key";
+    private static final String OLD_VOICEMAIL_VIBRATION_ALWAYS = "always";
+    private static final String OLD_VOICEMAIL_VIBRATION_NEVER = "never";
+
+    public static void setVibrationEnabled(Context context, boolean isEnabled) {
+        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+        SharedPreferences.Editor editor = prefs.edit();
+        editor.putBoolean(getVoicemailVibrationSharedPrefsKey(), isEnabled);
+        editor.commit();
+    }
+
+    public static boolean isVibrationEnabled(Context context) {
+        final NotificationChannel channel = NotificationChannelController.getChannel(
+                NotificationChannelController.CHANNEL_ID_VOICE_MAIL, context);
+        return (channel != null) ? channel.shouldVibrate() : getVibrationPreference(context);
+    }
+
+    public static boolean getVibrationPreference(Context context) {
+        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+        migrateVoicemailVibrationSettingsIfNeeded(context, prefs);
+        return prefs.getBoolean(getVoicemailVibrationSharedPrefsKey(), false /* defValue */);
+    }
+
+   public static void setRingtoneUri(Context context, Uri ringtoneUri) {
+        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+        String ringtoneUriStr = ringtoneUri != null ? ringtoneUri.toString() : "";
+
+        SharedPreferences.Editor editor = prefs.edit();
+        editor.putString(getVoicemailRingtoneSharedPrefsKey(), ringtoneUriStr);
+        editor.commit();
+    }
+
+    public static Uri getRingtoneUri(Context context) {
+        final NotificationChannel channel = NotificationChannelController.getChannel(
+                NotificationChannelController.CHANNEL_ID_VOICE_MAIL, context);
+        return (channel != null) ? channel.getSound() : getRingTonePreference(context);
+    }
+
+    public static Uri getRingTonePreference(Context context) {
+        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+        migrateVoicemailRingtoneSettingsIfNeeded(context, prefs);
+        String uriString = prefs.getString(
+                getVoicemailRingtoneSharedPrefsKey(),
+                Settings.System.DEFAULT_NOTIFICATION_URI.toString());
+        return !TextUtils.isEmpty(uriString) ? Uri.parse(uriString) : null;
+    }
+
+    /**
+     * Migrate voicemail settings from {@link #OLD_VIBRATE_WHEN_KEY} or
+     * {@link #OLD_VOICEMAIL_NOTIFICATION_VIBRATE_KEY}.
+     *
+     * TODO: Add helper which migrates settings from old version to new version.
+     */
+    private static void migrateVoicemailVibrationSettingsIfNeeded(
+            Context context, SharedPreferences prefs) {
+        String key = getVoicemailVibrationSharedPrefsKey();
+        TelephonyManager telephonyManager = TelephonyManager.from(context);
+
+        // Skip if a preference exists, or if phone is MSIM.
+        if (prefs.contains(key) || telephonyManager.getPhoneCount() != 1) {
+            return;
+        }
+
+        if (prefs.contains(OLD_VOICEMAIL_NOTIFICATION_VIBRATION_SHARED_PREFS_KEY)) {
+            boolean voicemailVibrate = prefs.getBoolean(
+                    OLD_VOICEMAIL_NOTIFICATION_VIBRATION_SHARED_PREFS_KEY, false /* defValue */);
+
+            SharedPreferences.Editor editor = prefs.edit();
+            editor.putBoolean(key, voicemailVibrate)
+                    .remove(OLD_VOICEMAIL_VIBRATE_WHEN_SHARED_PREFS_KEY)
+                    .commit();
+        }
+
+        if (prefs.contains(OLD_VOICEMAIL_VIBRATE_WHEN_SHARED_PREFS_KEY)) {
+            // If vibrateWhen is always, then voicemailVibrate should be true.
+            // If it is "only in silent mode", or "never", then voicemailVibrate should be false.
+            String vibrateWhen = prefs.getString(
+                    OLD_VOICEMAIL_VIBRATE_WHEN_SHARED_PREFS_KEY, OLD_VOICEMAIL_VIBRATION_NEVER);
+            boolean voicemailVibrate = vibrateWhen.equals(OLD_VOICEMAIL_VIBRATION_ALWAYS);
+
+            SharedPreferences.Editor editor = prefs.edit();
+            editor.putBoolean(key, voicemailVibrate)
+                    .remove(OLD_VOICEMAIL_NOTIFICATION_VIBRATION_SHARED_PREFS_KEY)
+                    .commit();
+        }
+    }
+
+    /**
+     * Migrate voicemail settings from OLD_VOICEMAIL_NOTIFICATION_RINGTONE_SHARED_PREFS_KEY.
+     *
+     * TODO: Add helper which migrates settings from old version to new version.
+     */
+    private static void migrateVoicemailRingtoneSettingsIfNeeded(
+            Context context, SharedPreferences prefs) {
+        String key = getVoicemailRingtoneSharedPrefsKey();
+        TelephonyManager telephonyManager = TelephonyManager.from(context);
+
+        // Skip if a preference exists, or if phone is MSIM.
+        if (prefs.contains(key) || telephonyManager.getPhoneCount() != 1) {
+            return;
+        }
+
+        if (prefs.contains(OLD_VOICEMAIL_NOTIFICATION_RINGTONE_SHARED_PREFS_KEY)) {
+            String uriString = prefs.getString(
+                    OLD_VOICEMAIL_NOTIFICATION_RINGTONE_SHARED_PREFS_KEY, null /* defValue */);
+
+            SharedPreferences.Editor editor = prefs.edit();
+            editor.putString(key, uriString)
+                    .remove(OLD_VOICEMAIL_NOTIFICATION_RINGTONE_SHARED_PREFS_KEY)
+                    .commit();
+        }
+    }
+
+    private static String getVoicemailVibrationSharedPrefsKey() {
+        return VOICEMAIL_NOTIFICATION_VIBRATION_SHARED_PREFS_KEY_PREFIX
+                + SubscriptionManager.getDefaultSubscriptionId();
+    }
+
+    private static String getVoicemailRingtoneSharedPrefsKey() {
+        return VOICEMAIL_NOTIFICATION_RINGTONE_SHARED_PREFS_KEY_PREFIX
+                + SubscriptionManager.getDefaultSubscriptionId();
+    }
+}
diff --git a/src/java/com/google/android/mms/pdu/PduPersister.java b/src/java/com/google/android/mms/pdu/PduPersister.java
index 4adcbf8..e32d121 100755
--- a/src/java/com/google/android/mms/pdu/PduPersister.java
+++ b/src/java/com/google/android/mms/pdu/PduPersister.java
@@ -17,15 +17,6 @@
 
 package com.google.android.mms.pdu;
 
-import com.google.android.mms.ContentType;
-import com.google.android.mms.InvalidHeaderValueException;
-import com.google.android.mms.MmsException;
-import com.google.android.mms.util.DownloadDrmHelper;
-import com.google.android.mms.util.DrmConvertSession;
-import com.google.android.mms.util.PduCache;
-import com.google.android.mms.util.PduCacheEntry;
-import com.google.android.mms.util.SqliteWrapper;
-
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
@@ -38,17 +29,26 @@
 import android.provider.MediaStore;
 import android.provider.Telephony;
 import android.provider.Telephony.Mms;
-import android.provider.Telephony.MmsSms;
-import android.provider.Telephony.Threads;
 import android.provider.Telephony.Mms.Addr;
 import android.provider.Telephony.Mms.Part;
+import android.provider.Telephony.MmsSms;
 import android.provider.Telephony.MmsSms.PendingMessages;
-import android.telephony.SubscriptionManager;
+import android.provider.Telephony.Threads;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.google.android.mms.ContentType;
+import com.google.android.mms.InvalidHeaderValueException;
+import com.google.android.mms.MmsException;
+import com.google.android.mms.util.DownloadDrmHelper;
+import com.google.android.mms.util.DrmConvertSession;
+import com.google.android.mms.util.PduCache;
+import com.google.android.mms.util.PduCacheEntry;
+import com.google.android.mms.util.SqliteWrapper;
+
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -60,10 +60,8 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
-import java.util.Set;
 import java.util.Map.Entry;
-
-import com.google.android.mms.pdu.EncodedStringValue;
+import java.util.Set;
 
 /**
  * This class is the high-level manager of PDU storage.
@@ -838,7 +836,7 @@
                 os = mContentResolver.openOutputStream(uri);
                 if (data == null) {
                     dataUri = part.getDataUri();
-                    if ((dataUri == null) || (dataUri == uri)) {
+                    if ((dataUri == null) || (dataUri.equals(uri))) {
                         Log.w(TAG, "Can't find data for this part.");
                         return;
                     }
@@ -1138,7 +1136,7 @@
         // 1. New binary data supplied or
         // 2. The Uri of the part is different from the current one.
         if ((part.getData() != null)
-                || (uri != part.getDataUri())) {
+                || (!uri.equals(part.getDataUri()))) {
             persistData(part, uri, contentType, preOpenedFiles);
         }
     }
@@ -1183,7 +1181,8 @@
             for (int i = 0; i < partsNum; i++) {
                 PduPart part = body.getPart(i);
                 Uri partUri = part.getDataUri();
-                if ((partUri == null) || !partUri.getAuthority().startsWith("mms")) {
+                if ((partUri == null) || TextUtils.isEmpty(partUri.getAuthority())
+                        || !partUri.getAuthority().startsWith("mms")) {
                     toBeCreated.add(part);
                 } else {
                     toBeUpdated.put(partUri, part);
diff --git a/tests/telephonytests/Android.mk b/tests/telephonytests/Android.mk
index 1324279..334d62f 100644
--- a/tests/telephonytests/Android.mk
+++ b/tests/telephonytests/Android.mk
@@ -9,7 +9,7 @@
 
 LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common ims-common services.core
 LOCAL_STATIC_JAVA_LIBRARIES := guava \
-                               mockito-target \
+                               mockito-target-minus-junit4 \
                                android-support-test \
                                platform-test-annotations \
                                legacy-android-test
diff --git a/tests/telephonytests/src/android/telephony/ims/ImsCallProfileTest.java b/tests/telephonytests/src/android/telephony/ims/ImsCallProfileTest.java
new file mode 100644
index 0000000..bc8d4e1
--- /dev/null
+++ b/tests/telephonytests/src/android/telephony/ims/ImsCallProfileTest.java
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+// 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;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.test.runner.AndroidJUnit4;
+import android.telecom.DisconnectCause;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.ims.ImsCallProfile;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for the {@link com.android.ims.ImsCallProfile} class.
+ */
+@RunWith(AndroidJUnit4.class)
+public class ImsCallProfileTest {
+    // A test-only parcelable class which is not in the android.* namespace.
+    private static class JunkParcelable implements Parcelable {
+        private int mTest;
+
+        JunkParcelable() {
+        }
+
+        protected JunkParcelable(Parcel in) {
+            mTest = in.readInt();
+        }
+
+        public static final Creator<JunkParcelable> CREATOR = new Creator<JunkParcelable>() {
+            @Override
+            public JunkParcelable createFromParcel(Parcel in) {
+                return new JunkParcelable(in);
+            }
+
+            @Override
+            public JunkParcelable[] newArray(int size) {
+                return new JunkParcelable[size];
+            }
+        };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(mTest);
+        }
+    }
+
+    /**
+     * Ensures that the {@link ImsCallProfile} will discard invalid extras when it is parceled.
+     */
+    @Test
+    @SmallTest
+    public void testExtrasCleanup() {
+        ImsCallProfile srcParcel = new ImsCallProfile();
+        // Put in a private parcelable type.
+        srcParcel.mCallExtras.putParcelable("JUNK", new JunkParcelable());
+        // Put in an api defined parcelable type.
+        srcParcel.mCallExtras.putParcelable("NOTJUNK", new DisconnectCause(DisconnectCause.BUSY));
+        // Put in some valid things.
+        srcParcel.mCallExtras.putInt("INT", 1);
+        srcParcel.mCallExtras.putString("STRING", "hello");
+
+        // Parcel it.
+        Parcel parcel = Parcel.obtain();
+        srcParcel.writeToParcel(parcel, 0);
+        byte[] parcelBytes = parcel.marshall();
+        parcel.recycle();
+
+        // Unparcel it.
+        parcel = Parcel.obtain();
+        parcel.unmarshall(parcelBytes, 0, parcelBytes.length);
+        parcel.setDataPosition(0);
+        ImsCallProfile unparceledProfile = ImsCallProfile.CREATOR.createFromParcel(parcel);
+        parcel.recycle();
+
+        assertNotNull(unparceledProfile.mCallExtras);
+        assertEquals(3, unparceledProfile.mCallExtras.size());
+        assertEquals(1, unparceledProfile.getCallExtraInt("INT"));
+        assertEquals("hello", unparceledProfile.getCallExtra("STRING"));
+        assertFalse(unparceledProfile.mCallExtras.containsKey("JUNK"));
+
+        DisconnectCause parceledCause = unparceledProfile.mCallExtras.getParcelable("NOTJUNK");
+        assertEquals(DisconnectCause.BUSY, parceledCause.getCode());
+    }
+}
diff --git a/tests/telephonytests/src/android/telephony/ims/ImsServiceTest.java b/tests/telephonytests/src/android/telephony/ims/ImsServiceTest.java
index dab0237..8887027 100644
--- a/tests/telephonytests/src/android/telephony/ims/ImsServiceTest.java
+++ b/tests/telephonytests/src/android/telephony/ims/ImsServiceTest.java
@@ -16,10 +16,32 @@
 
 package android.telephony.ims;
 
+import static android.Manifest.permission.MODIFY_PHONE_STATE;
+import static android.Manifest.permission.READ_PHONE_STATE;
+import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
+
+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.fail;
+
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.nullable;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.os.RemoteException;
+import android.support.test.filters.FlakyTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.telephony.ims.feature.ImsFeature;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -31,30 +53,13 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import static android.Manifest.permission.MODIFY_PHONE_STATE;
-import static android.Manifest.permission.READ_PHONE_STATE;
-import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
-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.fail;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.nullable;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
 /**
  * Unit tests for ImsService
  */
@@ -152,8 +157,9 @@
         }
     }
 
+    @FlakyTest
+    @Ignore
     @Test
-    @SmallTest
     public void testMethodWithNoPermissions() throws RemoteException {
         when(mMockContext.checkCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE)).thenReturn(
                 PackageManager.PERMISSION_DENIED);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierActionAgentTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierActionAgentTest.java
index 711c84e..ca21914 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierActionAgentTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierActionAgentTest.java
@@ -20,16 +20,18 @@
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Matchers.anyLong;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.content.Intent;
 import android.database.ContentObserver;
 import android.net.Uri;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Message;
 import android.provider.Settings;
-import android.test.mock.MockContentProvider;
+import android.provider.Telephony;
+import android.telephony.CarrierConfigManager;
 import android.test.mock.MockContentResolver;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -42,7 +44,6 @@
 public class CarrierActionAgentTest extends TelephonyTest {
     private CarrierActionAgent mCarrierActionAgentUT;
     private FakeContentResolver mFakeContentResolver;
-    private FakeContentProvider mFakeContentProvider;
     private static int DATA_CARRIER_ACTION_EVENT = 0;
     private static int RADIO_CARRIER_ACTION_EVENT = 1;
     private CarrierActionAgentHandler mCarrierActionAgentHandler;
@@ -64,23 +65,6 @@
         }
     }
 
-    private class FakeContentProvider extends MockContentProvider {
-        private int mExpectedValue;
-        public void simulateChange(Uri uri) {
-            mFakeContentResolver.notifyChange(uri, null);
-        }
-        @Override
-        public Bundle call(String method, String request, Bundle args) {
-            Bundle result = new Bundle();
-            if (Settings.CALL_METHOD_GET_GLOBAL.equals(method)) {
-                result.putString(Settings.NameValueTable.VALUE, Integer.toString(mExpectedValue));
-            } else {
-                mExpectedValue = Integer.parseInt(args.getString(Settings.NameValueTable.VALUE));
-            }
-            return result;
-        }
-    }
-
     private class CarrierActionAgentHandler extends HandlerThread {
 
         private CarrierActionAgentHandler(String name) {
@@ -105,8 +89,6 @@
         logd("CarrierActionAgentTest +Setup!");
         super.setUp(getClass().getSimpleName());
         mFakeContentResolver = new FakeContentResolver();
-        mFakeContentProvider = new FakeContentProvider();
-        mFakeContentResolver.addProvider(Settings.AUTHORITY, mFakeContentProvider);
         doReturn(mFakeContentResolver).when(mContext).getContentResolver();
         mCarrierActionAgentHandler = new CarrierActionAgentHandler(getClass().getSimpleName());
         mCarrierActionAgentHandler.start();
@@ -117,17 +99,33 @@
     @Test
     @SmallTest
     public void testCarrierActionResetOnAPM() {
-        Settings.Global.putInt(mFakeContentResolver, Settings.Global.AIRPLANE_MODE_ON, 1);
-        mFakeContentProvider.simulateChange(
-                Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON));
+        // setting observer register at sim loading
+        final Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+        intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
+                IccCardConstants.INTENT_VALUE_ICC_LOADED);
+        mContext.sendBroadcast(intent);
         waitForMs(200);
-        ArgumentCaptor<Message> message = ArgumentCaptor.forClass(Message.class);
 
+        // carrier actions triggered from sim loading
+        ArgumentCaptor<Message> message = ArgumentCaptor.forClass(Message.class);
         verify(mDataActionHandler).sendMessageAtTime(message.capture(), anyLong());
         assertEquals(DATA_CARRIER_ACTION_EVENT, message.getValue().what);
 
         verify(mRadioActionHandler).sendMessageAtTime(message.capture(), anyLong());
         assertEquals(RADIO_CARRIER_ACTION_EVENT, message.getValue().what);
+
+        // simulate APM change from off -> on
+        Settings.Global.putInt(mFakeContentResolver, Settings.Global.AIRPLANE_MODE_ON, 1);
+        mFakeContentResolver.notifyChange(
+                Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), null);
+        waitForMs(200);
+
+        // carrier actions triggered from APM
+        verify(mDataActionHandler, times(2)).sendMessageAtTime(message.capture(), anyLong());
+        assertEquals(DATA_CARRIER_ACTION_EVENT, message.getValue().what);
+
+        verify(mRadioActionHandler, times(2)).sendMessageAtTime(message.capture(), anyLong());
+        assertEquals(RADIO_CARRIER_ACTION_EVENT, message.getValue().what);
     }
 
     @After
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierSignalAgentTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierSignalAgentTest.java
index dcc13bb..7cb7d45 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierSignalAgentTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierSignalAgentTest.java
@@ -17,6 +17,7 @@
 
 import android.content.Intent;
 import android.content.pm.ResolveInfo;
+import android.os.Message;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -35,6 +36,7 @@
 import static com.android.internal.telephony.TelephonyIntents.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED;
 import static com.android.internal.telephony.TelephonyIntents.ACTION_CARRIER_SIGNAL_PCO_VALUE;
 import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.times;
@@ -102,11 +104,11 @@
         logd(mCaptorIntent.getAllValues().toString());
         Intent capturedIntent = mCaptorIntent.getAllValues().get(1);
         assertEquals(ACTION_CARRIER_SIGNAL_PCO_VALUE, capturedIntent.getAction());
-        assertEquals(PCO_RECEIVER, capturedIntent.getComponent().flattenToString());
+        assertEquals(DC_ERROR_RECEIVER, capturedIntent.getComponent().flattenToString());
 
         capturedIntent = mCaptorIntent.getAllValues().get(2);
         assertEquals(ACTION_CARRIER_SIGNAL_PCO_VALUE, capturedIntent.getAction());
-        assertEquals(DC_ERROR_RECEIVER, capturedIntent.getComponent().flattenToString());
+        assertEquals(PCO_RECEIVER, capturedIntent.getComponent().flattenToString());
     }
 
     @Test
@@ -210,4 +212,43 @@
         mCaptorIntent = ArgumentCaptor.forClass(Intent.class);
         verify(mContext, times(++count)).sendBroadcast(mCaptorIntent.capture());
     }
+
+
+    @Test
+    @SmallTest
+    public void testCarrierConfigChange() {
+        // default config value
+        mBundle.putStringArray(
+                CarrierConfigManager.KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
+                new String[]{ PCO_RECEIVER + ":" + ACTION_CARRIER_SIGNAL_PCO_VALUE + ","
+                        + ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED });
+        mContext.sendBroadcast(new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+        waitForMs(50);
+        // verify no reset action on initial config load
+        verify(mCarrierActionAgent, times(0)).sendMessageAtTime(any(Message.class), anyLong());
+
+        // new carrier config with different receiver intent order
+        mBundle.putStringArray(
+                CarrierConfigManager.KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
+                new String[]{ PCO_RECEIVER + ":" + ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED
+                        + "," + ACTION_CARRIER_SIGNAL_PCO_VALUE});
+        mContext.sendBroadcast(new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+        waitForMs(50);
+        // verify no reset action for the same config (different order)
+        verify(mCarrierActionAgent, times(0)).sendMessageAtTime(any(Message.class), anyLong());
+
+        // new different config value
+        mBundle.putStringArray(
+                CarrierConfigManager.KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
+                new String[]{ DC_ERROR_RECEIVER + ":" + ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED
+                        + "," + ACTION_CARRIER_SIGNAL_PCO_VALUE});
+        mContext.sendBroadcast(new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+        waitForMs(50);
+        // verify there is no reset action
+        ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
+        verify(mCarrierActionAgent, times(1))
+                .sendMessageAtTime(messageArgumentCaptor.capture(), anyLong());
+        assertEquals(CarrierActionAgent.CARRIER_ACTION_RESET,
+                messageArgumentCaptor.getValue().what);
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ClientWakelockAccountantTest.java b/tests/telephonytests/src/com/android/internal/telephony/ClientWakelockAccountantTest.java
index 2167c13..f7132b8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ClientWakelockAccountantTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ClientWakelockAccountantTest.java
@@ -18,7 +18,6 @@
 
 import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
 
-import android.test.TestRunner;
 import android.os.Build;
 import android.util.Log;
 import android.telephony.Rlog;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
index 419e61b..23e30a2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
@@ -59,6 +59,7 @@
 import android.preference.PreferenceManager;
 import android.provider.Settings;
 import android.provider.Telephony.ServiceStateTable;
+import android.telecom.TelecomManager;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -234,6 +235,8 @@
                     return mUsageStatManager;
                 case Context.BATTERY_SERVICE:
                     return mBatteryManager;
+                case Context.TELECOM_SERVICE:
+                    return mTelecomManager;
                 case Context.DISPLAY_SERVICE:
                 case Context.POWER_SERVICE:
                     // PowerManager and DisplayManager are final classes so cannot be mocked,
@@ -436,7 +439,14 @@
 
         @Override
         public int checkCallingOrSelfPermission(String permission) {
-            return PackageManager.PERMISSION_GRANTED;
+            if (mPermissionTable.contains(permission)
+                    || mPermissionTable.contains(PERMISSION_ENABLE_ALL)) {
+                logd("checkCallingOrSelfPermission: " + permission + " return GRANTED");
+                return PackageManager.PERMISSION_GRANTED;
+            } else {
+                logd("checkCallingOrSelfPermission: " + permission + " return DENIED");
+                return PackageManager.PERMISSION_DENIED;
+            }
         }
 
         @Override
@@ -492,6 +502,7 @@
     private final UsageStatsManager mUsageStatManager = null;
     private final WifiManager mWifiManager = mock(WifiManager.class);
     private final BatteryManager mBatteryManager = mock(BatteryManager.class);
+    private final TelecomManager mTelecomManager = mock(TelecomManager.class);
 
     private final ContentProvider mContentProvider = spy(new FakeContentProvider());
 
@@ -530,6 +541,9 @@
         doReturn(mConfiguration).when(mResources).getConfiguration();
 
         mContentResolver.addProvider(Settings.AUTHORITY, mContentProvider);
+        // Settings caches the provider after first get/set call, this is needed to make sure
+        // Settings is using mContentProvider as the cached provider across all tests.
+        Settings.Global.getInt(mContentResolver, Settings.Global.AIRPLANE_MODE_ON, 0);
         mContentResolver.addProvider(ServiceStateTable.AUTHORITY, mContentProvider);
         mPermissionTable.add(PERMISSION_ENABLE_ALL);
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/DeviceStateMonitorTest.java b/tests/telephonytests/src/com/android/internal/telephony/DeviceStateMonitorTest.java
index 5d89d08..591b111 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/DeviceStateMonitorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/DeviceStateMonitorTest.java
@@ -33,14 +33,14 @@
 import android.os.HandlerThread;
 import android.os.Message;
 import android.support.test.filters.FlakyTest;
-import android.test.suitebuilder.annotation.SmallTest;
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Test;
+import org.junit.Ignore;
 
 import java.util.ArrayList;
 
+@Ignore
 public class DeviceStateMonitorTest extends TelephonyTest {
 
     private DeviceStateMonitor mDSM;
@@ -76,8 +76,6 @@
     }
 
     @FlakyTest
-    @Test
-    @SmallTest
     public void testTethering() throws Exception {
         // Turn tethering on
         Intent intent = new Intent(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
@@ -103,8 +101,6 @@
     }
 
     @FlakyTest
-    @Test
-    @SmallTest
     public void testCharging() throws Exception {
         // Charging
         Intent intent = new Intent(BatteryManager.ACTION_CHARGING);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
index bc43aae..ebb8dad 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
@@ -15,7 +15,21 @@
  */
 package com.android.internal.telephony;
 
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.isA;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.Message;
 import android.support.test.filters.FlakyTest;
 import android.telephony.DisconnectCause;
 import android.telephony.PhoneNumberUtils;
@@ -23,21 +37,13 @@
 import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import android.os.Message;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
-import org.mockito.Mock;
 import org.mockito.ArgumentCaptor;
-import android.os.Handler;
-
-import static org.junit.Assert.assertNotNull;
-import static org.mockito.Mockito.doReturn;
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.*;
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
-
+import org.mockito.Mock;
 
 public class GsmCdmaCallTrackerTest extends TelephonyTest {
     private static final int VOICE_CALL_STARTED_EVENT = 0;
@@ -47,7 +53,7 @@
     private GsmCdmaCallTracker mCTUT;
     private GsmCdmaCTHandlerThread mGsmCdmaCTHandlerThread;
     @Mock
-    GsmCdmaCall mCall;
+    GsmCdmaConnection mConnection;
     @Mock
     private Handler mHandler;
 
@@ -68,8 +74,6 @@
         super.setUp(this.getClass().getSimpleName());
         mSimulatedCommands.setRadioPower(true, null);
         mPhone.mCi = this.mSimulatedCommands;
-        mContextFixture.putStringArrayResource(com.android.internal.R.array.dial_string_replace,
-                new String[]{});
 
         mGsmCdmaCTHandlerThread = new GsmCdmaCTHandlerThread(TAG);
         mGsmCdmaCTHandlerThread.start();
@@ -128,6 +132,8 @@
         assertEquals(GsmCdmaCall.State.IDLE, mCTUT.mBackgroundCall.getState());
     }
 
+    @FlakyTest
+    @Ignore
     @Test
     @MediumTest
     public void testMOCallHangup() {
@@ -151,6 +157,7 @@
     }
 
     @FlakyTest
+    @Ignore
     @Test
     @MediumTest
     public void testMOCallDialPickUpHangup() {
@@ -159,8 +166,8 @@
         assertEquals(PhoneConstants.State.OFFHOOK, mCTUT.getState());
         assertEquals(1, mCTUT.mForegroundCall.getConnections().size());
          /* get the reference of the connection before reject */
-        Connection mConnection = mCTUT.mForegroundCall.getConnections().get(0);
-        assertEquals(DisconnectCause.NOT_DISCONNECTED, mConnection.getDisconnectCause());
+        Connection connection = mCTUT.mForegroundCall.getConnections().get(0);
+        assertEquals(DisconnectCause.NOT_DISCONNECTED, connection.getDisconnectCause());
         logd("hang up MO call after pickup");
         try {
             mCTUT.hangup(mCTUT.mForegroundCall);
@@ -173,7 +180,7 @@
         assertEquals(GsmCdmaCall.State.IDLE, mCTUT.mForegroundCall.getState());
         assertEquals(0, mCTUT.mForegroundCall.getConnections().size());
         assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
-        assertEquals(DisconnectCause.LOCAL, mConnection.getDisconnectCause());
+        assertEquals(DisconnectCause.LOCAL, connection.getDisconnectCause());
 
     }
 
@@ -224,6 +231,8 @@
 
     @Test
     @SmallTest
+    @FlakyTest
+    @Ignore
     public void testMTCallRinging() {
         /* Mock there is a MT call mRinging call and try to accept this MT call */
         /* if we got a active state followed by another MT call-> move to background call */
@@ -240,6 +249,8 @@
 
     @Test
     @SmallTest
+    @FlakyTest
+    @Ignore
     public void testMTCallAccept() {
         testMTCallRinging();
         assertEquals(mCTUT.mForegroundCall.getConnections().size(),0);
@@ -265,9 +276,9 @@
         testMTCallRinging();
         logd("MT call ringing and rejected ");
         /* get the reference of the connection before reject */
-        Connection mConnection = mCTUT.mRingingCall.getConnections().get(0);
-        assertNotNull(mConnection);
-        assertEquals(DisconnectCause.NOT_DISCONNECTED, mConnection.getDisconnectCause());
+        Connection connection = mCTUT.mRingingCall.getConnections().get(0);
+        assertNotNull(connection);
+        assertEquals(DisconnectCause.NOT_DISCONNECTED, connection.getDisconnectCause());
         try {
             mCTUT.rejectCall();
         } catch(Exception ex) {
@@ -279,7 +290,7 @@
         assertEquals(GsmCdmaCall.State.IDLE, mCTUT.mForegroundCall.getState());
         assertEquals(0, mCTUT.mForegroundCall.getConnections().size());
         /* ? why rejectCall didnt -> hang up locally to set the cause to LOCAL? */
-        assertEquals(DisconnectCause.INCOMING_MISSED, mConnection.getDisconnectCause());
+        assertEquals(DisconnectCause.INCOMING_MISSED, connection.getDisconnectCause());
 
     }
 
@@ -302,6 +313,8 @@
         assertEquals(GsmCdmaCall.State.HOLDING, mCTUT.mBackgroundCall.getState());
     }
 
+    @FlakyTest
+    @Ignore
     @Test
     @MediumTest
     public void testMOCallPickUpHangUpResumeBackGround() {
@@ -340,11 +353,14 @@
         testMOCallPickUp();
         ArgumentCaptor<Message> mCaptorMessage = ArgumentCaptor.forClass(Message.class);
         ArgumentCaptor<Long> mCaptorLong = ArgumentCaptor.forClass(Long.class);
-        verify(mHandler,times(1)).sendMessageAtTime(mCaptorMessage.capture(), mCaptorLong.capture());
+        verify(mHandler, times(1))
+                .sendMessageAtTime(mCaptorMessage.capture(), mCaptorLong.capture());
         assertEquals(VOICE_CALL_STARTED_EVENT, mCaptorMessage.getValue().what);
 
     }
 
+    @FlakyTest
+    @Ignore
     @Test @SmallTest
     public void testVoiceCallEndedListener(){
         logd("register for voice call ended event");
@@ -352,7 +368,8 @@
         ArgumentCaptor<Message> mCaptorMessage = ArgumentCaptor.forClass(Message.class);
         ArgumentCaptor<Long> mCaptorLong = ArgumentCaptor.forClass(Long.class);
         testMOCallHangup();
-        verify(mHandler,times(1)).sendMessageAtTime(mCaptorMessage.capture(), mCaptorLong.capture());
+        verify(mHandler, times(1))
+                .sendMessageAtTime(mCaptorMessage.capture(), mCaptorLong.capture());
         assertEquals(VOICE_CALL_ENDED_EVENT, mCaptorMessage.getValue().what);
     }
 
@@ -392,5 +409,27 @@
         assertEquals(GsmCdmaCall.State.IDLE, mCTUT.mBackgroundCall.getState());
         assertEquals(GsmCdmaCall.State.IDLE, mCTUT.mRingingCall.getState());
     }
-}
 
+    @Test
+    @SmallTest
+    public void testUpdatePhoneTypeWithActiveCall() {
+        // verify getCurrentCalls is called on init
+        verify(mSimulatedCommandsVerifier).getCurrentCalls(any(Message.class));
+
+        // fake connection
+        mCTUT.mConnections[0] = mConnection;
+
+        // update phone type (call the function on same thread as the call tracker)
+        Handler updatePhoneTypeHandler = new Handler(mCTUT.getLooper()) {
+            @Override
+            public void handleMessage(Message msg) {
+                mCTUT.updatePhoneType();
+            }
+        };
+        updatePhoneTypeHandler.sendEmptyMessage(0);
+        waitForMs(100);
+
+        // verify that the active call is disconnected
+        verify(mConnection).onDisconnect(DisconnectCause.ERROR_UNSPECIFIED);
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
index 6301d54..3a80fe0 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
@@ -16,6 +16,24 @@
 
 package com.android.internal.telephony;
 
+import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.nullable;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
 import android.app.Activity;
 import android.app.IApplicationThread;
 import android.content.IIntentReceiver;
@@ -44,29 +62,13 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
 import java.util.List;
 
-import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE;
-import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.nullable;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
 public class GsmCdmaPhoneTest extends TelephonyTest {
     @Mock
     private Handler mTestHandler;
@@ -403,7 +405,7 @@
 
     @FlakyTest
     @Test
-    @SmallTest
+    @Ignore
     public void testVoiceMailCount() {
         // initial value
         assertEquals(0, mPhoneUT.getVoiceMessageCount());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java
index f2e5da1..06cd54f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java
@@ -41,6 +41,10 @@
         assertEquals("+14155551212", sms.getServiceCenterAddress());
         assertEquals("+16505551111", sms.getOriginatingAddress());
         assertEquals("(Subject)Test", sms.getMessageBody());
+
+        pdu = "07914151551512F20409E1BADCBE5AF100006060605130308A04D4F29C0E";
+        sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
+        assertEquals("*#abc#*51", sms.getOriginatingAddress());
     }
 
     @SmallTest
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ImsSMSDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/ImsSMSDispatcherTest.java
index be829ef..105e0f8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ImsSMSDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ImsSMSDispatcherTest.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -24,11 +26,11 @@
 import static org.mockito.Matchers.isNull;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 
-import android.app.ActivityManagerNative;
+import android.app.ActivityManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -36,13 +38,13 @@
 import android.content.IntentFilter;
 import android.os.HandlerThread;
 import android.os.Message;
+import android.test.FlakyTest;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Singleton;
 
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
-
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
@@ -99,7 +101,7 @@
         super.tearDown();
     }
 
-    @Test @SmallTest
+    @Test @SmallTest @FlakyTest @Ignore
     public void testSmsHandleStateUpdate() throws Exception {
         assertEquals(SmsConstants.FORMAT_UNKNOWN, mImsSmsDispatcher.getImsSmsFormat());
         //Mock ImsNetWorkStateChange with GSM phone type
@@ -113,7 +115,7 @@
         assertTrue(mImsSmsDispatcher.isIms());
     }
 
-    @Test @SmallTest
+    @Test @SmallTest @FlakyTest @Ignore
     public void testSendImsGmsTest() throws Exception {
         switchImsSmsFormat(PhoneConstants.PHONE_TYPE_GSM);
         mImsSmsDispatcher.sendText("111"/* desAddr*/, "222" /*scAddr*/, TAG,
@@ -172,7 +174,7 @@
         // unmock ActivityManager to be able to register receiver, create real PendingIntent and
         // receive TEST_INTENT
         restoreInstance(Singleton.class, "mInstance", mIActivityManagerSingleton);
-        restoreInstance(ActivityManagerNative.class, "gDefault", null);
+        restoreInstance(ActivityManager.class, "IActivityManagerSingleton", null);
 
         Context realContext = TestApplication.getAppContext();
         realContext.registerReceiver(mTestReceiver, new IntentFilter(TEST_INTENT));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ImsiEncryptionInfoTest.java b/tests/telephonytests/src/com/android/internal/telephony/ImsiEncryptionInfoTest.java
new file mode 100644
index 0000000..761bd5f
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/ImsiEncryptionInfoTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Parcel;
+import android.telephony.ImsiEncryptionInfo;
+import android.telephony.TelephonyManager;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Base64;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.security.PublicKey;
+import java.security.cert.CertificateFactory;
+import java.util.Date;
+
+public class ImsiEncryptionInfoTest {
+    private ImsiEncryptionInfo mImsiEncryptionInfo;
+    private PublicKey mPublicKey;
+    private Date mDate = new Date(1496795015);
+
+    private static final String TEST_CERT = ""
+            + "MIIDsjCCAxugAwIBAgIJAPLf2gS0zYGUMA0GCSqGSIb3DQEBBQUAMIGYMQswCQYDVQQGEwJVUzET"
+            + "MBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEPMA0GA1UEChMGR29v"
+            + "Z2xlMRAwDgYDVQQLEwd0ZXN0aW5nMRYwFAYDVQQDEw1HZXJlbXkgQ29uZHJhMSEwHwYJKoZIhvcN"
+            + "AQkBFhJnY29uZHJhQGdvb2dsZS5jb20wHhcNMTIwNzE0MTc1MjIxWhcNMTIwODEzMTc1MjIxWjCB"
+            + "mDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDU1vdW50YWluIFZp"
+            + "ZXcxDzANBgNVBAoTBkdvb2dsZTEQMA4GA1UECxMHdGVzdGluZzEWMBQGA1UEAxMNR2VyZW15IENv"
+            + "bmRyYTEhMB8GCSqGSIb3DQEJARYSZ2NvbmRyYUBnb29nbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUA"
+            + "A4GNADCBiQKBgQCjGGHATBYlmas+0sEECkno8LZ1KPglb/mfe6VpCT3GhSr+7br7NG/ZwGZnEhLq"
+            + "E7YIH4fxltHmQC3Tz+jM1YN+kMaQgRRjo/LBCJdOKaMwUbkVynAH6OYsKevjrOPk8lfM5SFQzJMG"
+            + "sA9+Tfopr5xg0BwZ1vA/+E3mE7Tr3M2UvwIDAQABo4IBADCB/TAdBgNVHQ4EFgQUhzkS9E6G+x8W"
+            + "L4EsmRjDxu28tHUwgc0GA1UdIwSBxTCBwoAUhzkS9E6G+x8WL4EsmRjDxu28tHWhgZ6kgZswgZgx"
+            + "CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3"
+            + "MQ8wDQYDVQQKEwZHb29nbGUxEDAOBgNVBAsTB3Rlc3RpbmcxFjAUBgNVBAMTDUdlcmVteSBDb25k"
+            + "cmExITAfBgkqhkiG9w0BCQEWEmdjb25kcmFAZ29vZ2xlLmNvbYIJAPLf2gS0zYGUMAwGA1UdEwQF"
+            + "MAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAYiugFDmbDOQ2U/+mqNt7o8ftlEo9SJrns6O8uTtK6AvR"
+            + "orDrR1AXTXkuxwLSbmVfedMGOZy7Awh7iZa8hw5x9XmUudfNxvmrKVEwGQY2DZ9PXbrnta/dwbhK"
+            + "mWfoepESVbo7CKIhJp8gRW0h1Z55ETXD57aGJRvQS4pxkP8ANhM=";
+
+    @After
+    public void tearDown() throws Exception {
+        mImsiEncryptionInfo = null;
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        mPublicKey = createPublicKey(TEST_CERT);
+        mImsiEncryptionInfo = new ImsiEncryptionInfo("310", "270", TelephonyManager.KEY_TYPE_WLAN,
+                "key1=value", mPublicKey, mDate);
+    }
+
+    private static PublicKey createPublicKey(String cert) throws Exception {
+        byte[] derCert = Base64.decode(cert.getBytes(), Base64.DEFAULT);
+        InputStream istream = new ByteArrayInputStream(derCert);
+        CertificateFactory cf = CertificateFactory.getInstance("X.509");
+        return cf.generateCertificate(istream).getPublicKey();
+    }
+
+    /**
+     * Tests that all the class variables are set correctly.
+     */
+    @Test
+    @SmallTest
+    public void testSubProperties() {
+        assertEquals("310", mImsiEncryptionInfo.getMcc());
+        assertEquals("270", mImsiEncryptionInfo.getMnc());
+        assertEquals(TelephonyManager.KEY_TYPE_WLAN, mImsiEncryptionInfo.getKeyType());
+        assertEquals("key1=value", mImsiEncryptionInfo.getKeyIdentifier());
+        Date date = mImsiEncryptionInfo.getExpirationTime();
+        assertEquals(mDate, mImsiEncryptionInfo.getExpirationTime());
+    }
+
+    /**
+     * Tests the parceling/un-parceling of the object.
+     */
+    @Test
+    @SmallTest
+    public void testParcel() {
+        Parcel p = Parcel.obtain();
+        p.setDataPosition(0);
+        mImsiEncryptionInfo.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        ImsiEncryptionInfo nw = new ImsiEncryptionInfo(p);
+        assertEquals("310", mImsiEncryptionInfo.getMcc());
+        assertEquals("270", mImsiEncryptionInfo.getMnc());
+        assertEquals(TelephonyManager.KEY_TYPE_WLAN, mImsiEncryptionInfo.getKeyType());
+        assertEquals("key1=value", mImsiEncryptionInfo.getKeyIdentifier());
+        assertEquals(mPublicKey, mImsiEncryptionInfo.getPublicKey());
+        assertEquals(mDate, mImsiEncryptionInfo.getExpirationTime());
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/MccTableTest.java b/tests/telephonytests/src/com/android/internal/telephony/MccTableTest.java
index e88a783..de98c67 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/MccTableTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/MccTableTest.java
@@ -33,6 +33,9 @@
         assertEquals("Asia/Tokyo", MccTable.defaultTimeZoneForMcc(441));
         assertEquals("Asia/Singapore", MccTable.defaultTimeZoneForMcc(525));
         assertEquals("Europe/Stockholm", MccTable.defaultTimeZoneForMcc(240));
+
+        /* A test for the special handling for MCC 505. http://b/33228250. */
+        assertEquals("Australia/Sydney", MccTable.defaultTimeZoneForMcc(505));
         assertEquals(null, MccTable.defaultTimeZoneForMcc(0));    // mcc not defined, hence default
         assertEquals(null, MccTable.defaultTimeZoneForMcc(2000)); // mcc not defined, hence default
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
index 0c8c3aa..faeabd5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
@@ -16,16 +16,24 @@
 
 package com.android.internal.telephony;
 
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
 import android.net.Uri;
 import android.support.test.filters.FlakyTest;
 import android.telephony.PhoneNumberUtils;
-import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.text.SpannableStringBuilder;
 
-public class PhoneNumberUtilsTest extends AndroidTestCase {
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class PhoneNumberUtilsTest {
 
     @SmallTest
+    @Test
     public void testExtractNetworkPortion() throws Exception {
         assertEquals(
                 "+17005554141",
@@ -192,6 +200,21 @@
     }
 
     @SmallTest
+    @Test
+    public void testNonIntegerAddress() {
+        byte[] b = new byte[6];
+        b[0] = (byte) 0x81; b[1] = (byte) 0xba; b[2] = (byte) 0xdc; b[3] = (byte) 0xbe;
+        b[4] = (byte) 0x5a; b[5] = (byte) 0xf1;
+        assertEquals("*#abc#*51",
+                PhoneNumberUtils.calledPartyBCDToString(
+                        b, 0, 6, PhoneNumberUtils.BCD_EXTENDED_TYPE_CALLED_PARTY));
+        assertEquals("*#,N;#*51",
+                PhoneNumberUtils.calledPartyBCDToString(
+                        b, 0, 6, PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN));
+    }
+
+    @SmallTest
+    @Test
     public void testExtractNetworkPortionAlt() throws Exception {
         assertEquals(
                 "+17005554141",
@@ -254,6 +277,7 @@
     }
 
     @SmallTest
+    @Test
     public void testB() throws Exception {
         assertEquals("", PhoneNumberUtils.extractPostDialPortion("+17005554141"));
         assertEquals("", PhoneNumberUtils.extractPostDialPortion("+1 (700).555-4141"));
@@ -265,6 +289,7 @@
     }
 
     @SmallTest
+    @Test
     public void testCompare() throws Exception {
         // this is odd
         assertFalse(PhoneNumberUtils.compare("", ""));
@@ -330,8 +355,8 @@
         assertTrue(PhoneNumberUtils.compare("404-04", "40404"));
     }
 
-
     @SmallTest
+    @Test
     public void testToCallerIDIndexable() throws Exception {
         assertEquals("1414555", PhoneNumberUtils.toCallerIDMinMatch("17005554141"));
         assertEquals("1414555", PhoneNumberUtils.toCallerIDMinMatch("1-700-555-4141"));
@@ -349,6 +374,7 @@
     }
 
     @SmallTest
+    @Test
     public void testGetIndexable() throws Exception {
         assertEquals("14145550071", PhoneNumberUtils.getStrippedReversed("1-700-555-4141"));
         assertEquals("14145550071", PhoneNumberUtils.getStrippedReversed("1-700-555-4141,1234"));
@@ -365,6 +391,7 @@
     }
 
     @SmallTest
+    @Test
     public void testNanpFormatting() {
         SpannableStringBuilder number = new SpannableStringBuilder();
         number.append("8005551212");
@@ -393,6 +420,7 @@
     }
 
     @SmallTest
+    @Test
     public void testConvertKeypadLettersToDigits() {
         assertEquals("1-800-4664-411",
                      PhoneNumberUtils.convertKeypadLettersToDigits("1-800-GOOG-411"));
@@ -467,6 +495,7 @@
     }
 
     @SmallTest
+    @Test
     public void testCheckAndProcessPlusCodeByNumberFormat() {
         assertEquals("18475797000",
                 PhoneNumberUtils.cdmaCheckAndProcessPlusCodeByNumberFormat("+18475797000",
@@ -477,6 +506,7 @@
      * Basic checks for the VoiceMail number.
      */
     @SmallTest
+    @Test
     public void testWithNumberNotEqualToVoiceMail() throws Exception {
         assertFalse(PhoneNumberUtils.isVoiceMailNumber("911"));
         assertFalse(PhoneNumberUtils.isVoiceMailNumber("tel:911"));
@@ -492,6 +522,7 @@
     }
 
     @SmallTest
+    @Test
     public void testFormatNumberToE164() {
         // Note: ISO 3166-1 only allows upper case country codes.
         assertEquals("+16502910000", PhoneNumberUtils.formatNumberToE164("650 2910000", "US"));
@@ -500,6 +531,7 @@
     }
 
     @SmallTest
+    @Test
     public void testFormatNumber() {
         assertEquals("(650) 291-0000", PhoneNumberUtils.formatNumber("650 2910000", "US"));
         assertEquals("223-4567", PhoneNumberUtils.formatNumber("2234567", "US"));
@@ -515,6 +547,7 @@
      * current country is not Japan.
      */
     @SmallTest
+    @Test
     public void testFormatJapanInternational() {
         assertEquals("+81 90-6657-1180", PhoneNumberUtils.formatNumber("+819066571180", "US"));
     }
@@ -524,6 +557,7 @@
      * country is Japan.
      */
     @SmallTest
+    @Test
     public void testFormatJapanNational() {
         assertEquals("090-6657-0660", PhoneNumberUtils.formatNumber("09066570660", "JP"));
         assertEquals("090-6657-1180", PhoneNumberUtils.formatNumber("+819066571180", "JP"));
@@ -533,6 +567,7 @@
     }
 
     @SmallTest
+    @Test
     public void testFormatNumber_LeadingStarAndHash() {
         // Numbers with a leading '*' or '#' should be left unchanged.
         assertEquals("*650 2910000", PhoneNumberUtils.formatNumber("*650 2910000", "US"));
@@ -546,6 +581,7 @@
     }
 
     @SmallTest
+    @Test
     public void testNormalizeNumber() {
         assertEquals("6502910000", PhoneNumberUtils.normalizeNumber("650 2910000"));
         assertEquals("1234567", PhoneNumberUtils.normalizeNumber("12,3#4*567"));
@@ -554,6 +590,7 @@
     }
 
     @SmallTest
+    @Test
     public void testFormatDailabeNumber() {
         // Using the phoneNumberE164's country code
         assertEquals("(650) 291-0000",
@@ -585,7 +622,8 @@
     }
 
     @FlakyTest
-    @SmallTest
+    @Test
+    @Ignore
     public void testIsEmergencyNumber() {
         // There are two parallel sets of tests here: one for the
         // regular isEmergencyNumber() method, and the other for
@@ -639,6 +677,7 @@
     }
 
     @SmallTest
+    @Test
     public void testStripSeparators() {
         // Smoke tests which should never fail.
         assertEquals("1234567890", PhoneNumberUtils.stripSeparators("1234567890"));
@@ -654,6 +693,7 @@
     }
 
     @SmallTest
+    @Test
     public void testConvertAndStrip() {
         // Smoke tests which should never fail.
         assertEquals("1234567890", PhoneNumberUtils.convertAndStrip("1234567890"));
@@ -670,6 +710,7 @@
     }
 
     @SmallTest
+    @Test
     public void testConvertSipUriToTelUri1() {
         // Nominal case, a tel Uri came in, so we expect one out.
         Uri source = Uri.fromParts("tel", "+16505551212", null);
@@ -692,7 +733,10 @@
     }
 
     @SmallTest
+    @Test
     public void testIsInternational() {
+        assertFalse(PhoneNumberUtils.isInternationalNumber("", "US"));
+        assertFalse(PhoneNumberUtils.isInternationalNumber(null, "US"));
         assertFalse(PhoneNumberUtils.isInternationalNumber("+16505551212", "US"));
         assertTrue(PhoneNumberUtils.isInternationalNumber("+16505551212", "UK"));
         assertTrue(PhoneNumberUtils.isInternationalNumber("+16505551212", "JP"));
@@ -703,4 +747,21 @@
         assertTrue(PhoneNumberUtils.isInternationalNumber("011-613-966-94916", "US"));
         assertFalse(PhoneNumberUtils.isInternationalNumber("011-613-966-94916", "AU"));
     }
+
+    @SmallTest
+    @Test
+    public void testIsUriNumber() {
+        assertTrue(PhoneNumberUtils.isUriNumber("foo@google.com"));
+        assertTrue(PhoneNumberUtils.isUriNumber("xyz@zzz.org"));
+        assertFalse(PhoneNumberUtils.isUriNumber("+15103331245"));
+        assertFalse(PhoneNumberUtils.isUriNumber("+659231235"));
+    }
+
+    @SmallTest
+    @Test
+    public void testGetUsernameFromUriNumber() {
+        assertEquals("john", PhoneNumberUtils.getUsernameFromUriNumber("john@myorg.com"));
+        assertEquals("tim_123", PhoneNumberUtils.getUsernameFromUriNumber("tim_123@zzz.org"));
+        assertEquals("5103331245", PhoneNumberUtils.getUsernameFromUriNumber("5103331245"));
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index c8d459a..15897d4 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -45,10 +45,12 @@
 import android.os.HandlerThread;
 import android.os.Message;
 import android.os.Parcel;
+import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.support.test.filters.FlakyTest;
+import android.telephony.CarrierConfigManager;
 import android.telephony.CellInfo;
 import android.telephony.CellInfoGsm;
 import android.telephony.ServiceState;
@@ -87,6 +89,7 @@
 
     private ServiceStateTracker sst;
     private ServiceStateTrackerTestHandler mSSTTestHandler;
+    private PersistableBundle mBundle;
 
     private static final int EVENT_REGISTERED_TO_NETWORK = 1;
     private static final int EVENT_SUBSCRIPTION_INFO_READY = 2;
@@ -123,14 +126,12 @@
         mPhone.mDcTracker = mDct;
 
         replaceInstance(ProxyController.class, "sProxyController", null, mProxyController);
+        mBundle = mContextFixture.getCarrierConfigBundle();
+        mBundle.putStringArray(
+                CarrierConfigManager.KEY_ROAMING_OPERATOR_STRING_ARRAY, new String[]{"123456"});
 
-        mContextFixture.putStringArrayResource(
-                com.android.internal.R.array.config_sameNamedOperatorConsideredRoaming,
-                new String[]{"123456"});
-
-        mContextFixture.putStringArrayResource(
-                com.android.internal.R.array.config_operatorConsideredNonRoaming,
-                new String[]{"123456"});
+        mBundle.putStringArray(
+                CarrierConfigManager.KEY_NON_ROAMING_OPERATOR_STRING_ARRAY, new String[]{"123456"});
 
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_HOME);
         mSimulatedCommands.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_HSPA);
@@ -451,7 +452,7 @@
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
         mSimulatedCommands.notifyNetworkStateChanged();
 
-        waitForMs(100);
+        waitForMs(200);
 
         // verify if registered handler has message posted to it
         ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
@@ -473,7 +474,7 @@
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
         mSimulatedCommands.notifyNetworkStateChanged();
 
-        waitForMs(100);
+        waitForMs(200);
 
         // verify that no new message posted to handler
         verify(mTestHandler, times(1)).sendMessageAtTime(any(Message.class), anyLong());
@@ -498,7 +499,7 @@
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_HOME);
         mSimulatedCommands.notifyNetworkStateChanged();
 
-        waitForMs(100);
+        waitForMs(200);
 
         // verify if registered handler has message posted to it
         ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
@@ -537,7 +538,7 @@
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
         mSimulatedCommands.notifyNetworkStateChanged();
 
-        waitForMs(100);
+        waitForMs(200);
 
         // verify if registered handler has message posted to it
         ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
@@ -559,7 +560,7 @@
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
         mSimulatedCommands.notifyNetworkStateChanged();
 
-        waitForMs(100);
+        waitForMs(200);
 
         // verify that no new message posted to handler
         verify(mTestHandler, times(1)).sendMessageAtTime(any(Message.class), anyLong());
@@ -576,7 +577,7 @@
 
         waitForMs(100);
 
-        sst.registerForDataRoamingOff(mTestHandler, EVENT_DATA_ROAMING_OFF, null);
+        sst.registerForDataRoamingOff(mTestHandler, EVENT_DATA_ROAMING_OFF, null, true);
 
         // Disable roaming
         doReturn(true).when(mPhone).isPhoneTypeGsm();
@@ -720,7 +721,7 @@
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
         mSimulatedCommands.notifyNetworkStateChanged();
 
-        waitForMs(100);
+        waitForMs(200);
 
         // verify if registered handler has message posted to it
         ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
@@ -906,7 +907,14 @@
         doReturn(true).when(mPhone).isPhoneTypeGsm();
         doReturn(IccCardApplicationStatus.AppState.APPSTATE_READY).when(
                 mUiccCardApplication3gpp).getState();
-        spySst.updatePhoneType();
+
+        ArgumentCaptor<Integer> intArgumentCaptor = ArgumentCaptor.forClass(Integer.class);
+        verify(mSimulatedCommandsVerifier).setOnRestrictedStateChanged(any(Handler.class),
+                intArgumentCaptor.capture(), eq(null));
+        // Since spy() creates a copy of sst object we need to call
+        // setOnRestrictedStateChanged() explicitly.
+        mSimulatedCommands.setOnRestrictedStateChanged(spySst,
+                intArgumentCaptor.getValue().intValue(), null);
 
         // Combination of restricted state and expected notification type.
         final int CS_ALL[] = {RILConstants.RIL_RESTRICTED_STATE_CS_ALL,
@@ -993,7 +1001,7 @@
 
         waitForMs(200);
 
-        sst.registerForDataRoamingOff(mTestHandler, EVENT_DATA_ROAMING_OFF, null);
+        sst.registerForDataRoamingOff(mTestHandler, EVENT_DATA_ROAMING_OFF, null, true);
         sst.registerForVoiceRoamingOff(mTestHandler, EVENT_VOICE_ROAMING_OFF, null);
         sst.registerForDataConnectionDetached(mTestHandler, EVENT_DATA_CONNECTION_DETACHED, null);
 
@@ -1048,21 +1056,19 @@
     @Test
     @SmallTest
     public void testIsConcurrentVoiceAndDataAllowed() {
-        // Verify all 3 branches in the function isConcurrentVoiceAndDataAllowed
-        doReturn(true).when(mPhone).isPhoneTypeGsm();
-        sst.mSS.setRilVoiceRadioTechnology(sst.mSS.RIL_RADIO_TECHNOLOGY_HSPA);
-        assertEquals(true, sst.isConcurrentVoiceAndDataAllowed());
-
         doReturn(false).when(mPhone).isPhoneTypeGsm();
-        doReturn(true).when(mPhone).isPhoneTypeCdma();
-        assertEquals(false, sst.isConcurrentVoiceAndDataAllowed());
-
-        doReturn(false).when(mPhone).isPhoneTypeGsm();
-        doReturn(false).when(mPhone).isPhoneTypeCdma();
         sst.mSS.setCssIndicator(1);
         assertEquals(true, sst.isConcurrentVoiceAndDataAllowed());
         sst.mSS.setCssIndicator(0);
         assertEquals(false, sst.isConcurrentVoiceAndDataAllowed());
+
+        doReturn(true).when(mPhone).isPhoneTypeGsm();
+        sst.mSS.setRilDataRadioTechnology(sst.mSS.RIL_RADIO_TECHNOLOGY_HSPA);
+        assertEquals(true, sst.isConcurrentVoiceAndDataAllowed());
+        sst.mSS.setRilDataRadioTechnology(sst.mSS.RIL_RADIO_TECHNOLOGY_GPRS);
+        assertEquals(false, sst.isConcurrentVoiceAndDataAllowed());
+        sst.mSS.setCssIndicator(1);
+        assertEquals(true, sst.isConcurrentVoiceAndDataAllowed());
     }
 
     @Test
@@ -1089,14 +1095,14 @@
 
         // Mock sending incorrect nitz str from RIL
         mSimulatedCommands.triggerNITZupdate("38/06/20,00:00:00+0");
-        waitForMs(100);
+        waitForMs(200);
         // AlarmManger.setTime is triggered by SystemClock.setCurrentTimeMillis().
         // Verify system time is not set to incorrect NITZ time
         verify(mAlarmManager, times(0)).setTime(anyLong());
 
         // Mock sending correct nitz str from RIL
         mSimulatedCommands.triggerNITZupdate("15/06/20,00:00:00+0");
-        waitForMs(100);
+        waitForMs(200);
         verify(mAlarmManager, times(1)).setTime(anyLong());
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsNumberUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsNumberUtilsTest.java
index 43d70b5..05c2cb8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SmsNumberUtilsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsNumberUtilsTest.java
@@ -19,6 +19,7 @@
 import android.database.Cursor;
 import android.database.MatrixCursor;
 import android.net.Uri;
+import android.telephony.CarrierConfigManager;
 import android.telephony.TelephonyManager;
 import android.test.mock.MockContentProvider;
 import android.test.mock.MockContentResolver;
@@ -122,9 +123,9 @@
 
         ((MockContentResolver) mContextFixture.getTestDouble().getContentResolver())
                 .addProvider(HbpcdLookup.MccIdd.CONTENT_URI.getAuthority(), mHbpcdContentProvider);
-        mContextFixture.putStringArrayResource(
-                com.android.internal.R.array.config_sms_convert_destination_number_support,
-                new String[]{"true"});
+        mContextFixture.getCarrierConfigBundle().
+                putBoolean(CarrierConfigManager.KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL,
+                        true);
 
         logd("SmsNumberUtilsTest -Setup!");
     }
@@ -243,4 +244,4 @@
         assertEquals("01118582345678",
                 SmsNumberUtils.filterDestAddr(mPhone, "+011-1-858-234-5678"));
     }
-}
\ No newline at end of file
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsUsageMonitorShortCodeTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsUsageMonitorShortCodeTest.java
index fea4303..76fce94 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SmsUsageMonitorShortCodeTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsUsageMonitorShortCodeTest.java
@@ -16,21 +16,24 @@
 
 package com.android.internal.telephony;
 
-import android.os.Looper;
-import android.support.test.filters.FlakyTest;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
 import static com.android.internal.telephony.SmsUsageMonitor.CATEGORY_FREE_SHORT_CODE;
 import static com.android.internal.telephony.SmsUsageMonitor.CATEGORY_NOT_SHORT_CODE;
 import static com.android.internal.telephony.SmsUsageMonitor.CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE;
 import static com.android.internal.telephony.SmsUsageMonitor.CATEGORY_PREMIUM_SHORT_CODE;
 import static com.android.internal.telephony.SmsUsageMonitor.CATEGORY_STANDARD_SHORT_CODE;
 
+import static org.junit.Assert.assertEquals;
+
+import android.os.Looper;
+import android.support.test.filters.FlakyTest;
+
+import org.junit.Ignore;
+
 /**
  * Test cases for SMS short code pattern matching in SmsUsageMonitor.
  */
-public class SmsUsageMonitorShortCodeTest extends AndroidTestCase {
+@Ignore
+public class SmsUsageMonitorShortCodeTest {
 
     private static final class ShortCodeTest {
         final String countryIso;
@@ -458,14 +461,13 @@
     };
 
     @FlakyTest
-    @SmallTest
     public void testSmsUsageMonitor() {
         // InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not.
         // http://b/25897652 .
         if (Looper.myLooper() == null) {
             Looper.prepare();
         }
-        SmsUsageMonitor monitor = new SmsUsageMonitor(getContext());
+        SmsUsageMonitor monitor = new SmsUsageMonitor(TestApplication.getAppContext());
         for (ShortCodeTest test : sShortCodeTests) {
             assertEquals("country: " + test.countryIso + " number: " + test.address,
                     test.category, monitor.checkDestination(test.address, test.countryIso));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
index ea2f17d..feea80e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
@@ -60,8 +60,10 @@
 
 public class SubscriptionInfoUpdaterTest extends TelephonyTest {
 
-    private static final int FAKE_SUB_ID = 1;
-    private static final String FAKE_PLMN = "123456";
+    private static final int FAKE_SUB_ID_1 = 0;
+    private static final int FAKE_SUB_ID_2 = 1;
+    private static final String FAKE_MCC_MNC_1 = "123456";
+    private static final String FAKE_MCC_MNC_2 = "456789";
 
     private SubscriptionInfoUpdaterHandlerThread mSubscriptionInfoUpdaterHandlerThread;
     private IccRecords mIccRecord;
@@ -114,7 +116,8 @@
         doReturn(1).when(mTelephonyManager).getPhoneCount();
 
         doReturn(mUserInfo).when(mIActivityManager).getCurrentUser();
-        doReturn(new int[]{FAKE_SUB_ID}).when(mSubscriptionController).getSubId(0);
+        doReturn(new int[]{FAKE_SUB_ID_1}).when(mSubscriptionController).getSubId(0);
+        doReturn(new int[]{FAKE_SUB_ID_1}).when(mSubscriptionManager).getActiveSubscriptionIdList();
         mContentProvider = new FakeSubscriptionContentProvider();
         ((MockContentResolver) mContext.getContentResolver()).addProvider(
                 SubscriptionManager.CONTENT_URI.getAuthority(),
@@ -136,11 +139,11 @@
     @SmallTest
     public void testSimAbsent() throws Exception {
         doReturn(Arrays.asList(mSubInfo)).when(mSubscriptionController)
-                .getSubInfoUsingSlotIndexWithCheck(eq(0), anyBoolean(), anyString());
+                .getSubInfoUsingSlotIndexWithCheck(eq(FAKE_SUB_ID_1), anyBoolean(), anyString());
         Intent mIntent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
         mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
                 IccCardConstants.INTENT_VALUE_ICC_ABSENT);
-        mIntent.putExtra(PhoneConstants.PHONE_KEY, 0);
+        mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_1);
 
         mContext.sendBroadcast(mIntent);
 
@@ -150,7 +153,7 @@
 
         CarrierConfigManager mConfigManager = (CarrierConfigManager)
                 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        verify(mConfigManager).updateConfigForPhoneId(eq(0),
+        verify(mConfigManager).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
                 eq(IccCardConstants.INTENT_VALUE_ICC_ABSENT));
         verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
     }
@@ -161,7 +164,7 @@
         Intent mIntent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
         mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
                 IccCardConstants.INTENT_VALUE_ICC_UNKNOWN);
-        mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID);
+        mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_1);
 
         mContext.sendBroadcast(mIntent);
 
@@ -169,7 +172,7 @@
         verify(mSubscriptionContent, times(0)).put(anyString(), any());
         CarrierConfigManager mConfigManager = (CarrierConfigManager)
                 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        verify(mConfigManager).updateConfigForPhoneId(eq(1),
+        verify(mConfigManager).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
                 eq(IccCardConstants.INTENT_VALUE_ICC_UNKNOWN));
         verify(mSubscriptionController, times(0)).notifySubscriptionInfoChanged();
     }
@@ -215,14 +218,14 @@
     public void testSimLoaded() throws Exception {
         /* mock new sim got loaded and there is no sim loaded before */
         doReturn(null).when(mSubscriptionController)
-                .getSubInfoUsingSlotIndexWithCheck(eq(0), anyBoolean(), anyString());
+                .getSubInfoUsingSlotIndexWithCheck(eq(FAKE_SUB_ID_1), anyBoolean(), anyString());
         doReturn("89012604200000000000").when(mIccRecord).getIccId();
-        doReturn(FAKE_PLMN).when(mTelephonyManager).getSimOperatorNumericForPhone(0);
+        doReturn(FAKE_MCC_MNC_1).when(mTelephonyManager).getSimOperatorNumeric(FAKE_SUB_ID_1);
         Intent intentInternalSimStateChanged =
                 new Intent(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED);
         intentInternalSimStateChanged.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
                 IccCardConstants.INTENT_VALUE_ICC_LOADED);
-        intentInternalSimStateChanged.putExtra(PhoneConstants.PHONE_KEY, 0);
+        intentInternalSimStateChanged.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_1);
 
         mContext.sendBroadcast(intentInternalSimStateChanged);
         waitForMs(100);
@@ -246,14 +249,14 @@
                 stringArgumentCaptor.getAllValues().get(1)); */
 
         SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
-        verify(mTelephonyManager).getSimOperatorNumericForPhone(0);
+        verify(mTelephonyManager).getSimOperatorNumeric(FAKE_SUB_ID_1);
         verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(
-                eq("89012604200000000000"), eq(0));
+                eq("89012604200000000000"), eq(FAKE_SUB_ID_1));
         verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
-        verify(mSubscriptionController, times(1)).setMccMnc(FAKE_PLMN, FAKE_SUB_ID);
+        verify(mSubscriptionController, times(1)).setMccMnc(FAKE_MCC_MNC_1, FAKE_SUB_ID_1);
         CarrierConfigManager mConfigManager = (CarrierConfigManager)
                 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        verify(mConfigManager, times(1)).updateConfigForPhoneId(eq(0),
+        verify(mConfigManager, times(1)).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
                 eq(IccCardConstants.INTENT_VALUE_ICC_LOADED));
 
         // ACTION_USER_UNLOCKED should trigger another SIM_STATE_CHANGED
@@ -282,26 +285,26 @@
     public void testSimLoadedEmptyOperatorNumeric() throws Exception {
         /* mock new sim got loaded and there is no sim loaded before */
         doReturn(null).when(mSubscriptionController)
-                .getSubInfoUsingSlotIndexWithCheck(eq(0), anyBoolean(), anyString());
+                .getSubInfoUsingSlotIndexWithCheck(eq(FAKE_SUB_ID_1), anyBoolean(), anyString());
         doReturn("89012604200000000000").when(mIccRecord).getIccId();
         // operator numeric is empty
-        doReturn("").when(mTelephonyManager).getSimOperatorNumericForPhone(0);
+        doReturn("").when(mTelephonyManager).getSimOperatorNumeric(FAKE_SUB_ID_1);
         Intent mIntent = new Intent(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED);
         mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
                 IccCardConstants.INTENT_VALUE_ICC_LOADED);
-        mIntent.putExtra(PhoneConstants.PHONE_KEY, 0);
+        mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_1);
 
         mContext.sendBroadcast(mIntent);
         waitForMs(100);
         SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
-        verify(mTelephonyManager).getSimOperatorNumericForPhone(0);
+        verify(mTelephonyManager).getSimOperatorNumeric(FAKE_SUB_ID_1);
         verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(
-                eq("89012604200000000000"), eq(0));
+                eq("89012604200000000000"), eq(FAKE_SUB_ID_1));
         verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
         verify(mSubscriptionController, times(0)).setMccMnc(anyString(), anyInt());
         CarrierConfigManager mConfigManager = (CarrierConfigManager)
                 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        verify(mConfigManager, times(1)).updateConfigForPhoneId(eq(0),
+        verify(mConfigManager, times(1)).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
                 eq(IccCardConstants.INTENT_VALUE_ICC_LOADED));
     }
 
@@ -323,13 +326,13 @@
         }).when(mIccFileHandler).loadEFTransparent(anyInt(), any(Message.class));
 
         doReturn(Arrays.asList(mSubInfo)).when(mSubscriptionController)
-                .getSubInfoUsingSlotIndexWithCheck(eq(0), anyBoolean(), anyString());
+                .getSubInfoUsingSlotIndexWithCheck(eq(FAKE_SUB_ID_1), anyBoolean(), anyString());
 
         Intent mIntent = new Intent(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED);
         mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
                 IccCardConstants.INTENT_VALUE_ICC_LOCKED);
         mIntent.putExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON, "TESTING");
-        mIntent.putExtra(PhoneConstants.PHONE_KEY, 0);
+        mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_1);
 
         mContext.sendBroadcast(mIntent);
         waitForMs(100);
@@ -339,18 +342,75 @@
                 eq(SubscriptionManager.INVALID_SIM_SLOT_INDEX));
         SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
         verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(
-                eq("98106240020000000000"), eq(0));
+                eq("98106240020000000000"), eq(FAKE_SUB_ID_1));
 
         verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
         CarrierConfigManager mConfigManager = (CarrierConfigManager)
                 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        verify(mConfigManager, times(1)).updateConfigForPhoneId(eq(0),
+        verify(mConfigManager, times(1)).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
                 eq(IccCardConstants.INTENT_VALUE_ICC_LOCKED));
     }
 
     @Test
     @SmallTest
-    public void testSimLockWIthIccId() throws Exception {
+    public void testDualSimLoaded() throws Exception {
+        // Mock there is two sim cards
+
+        replaceInstance(SubscriptionInfoUpdater.class, "mIccId", null,
+                new String[]{null, null});
+        replaceInstance(SubscriptionInfoUpdater.class, "PROJECT_SIM_NUM", null, 2);
+        replaceInstance(SubscriptionInfoUpdater.class, "mPhone", null,
+                new Phone[]{mPhone, mPhone});
+        replaceInstance(SubscriptionInfoUpdater.class, "mInsertSimState", null,
+                new int[]{SubscriptionInfoUpdater.SIM_NOT_CHANGE,
+                        SubscriptionInfoUpdater.SIM_NOT_CHANGE});
+
+        doReturn(new int[]{FAKE_SUB_ID_1, FAKE_SUB_ID_2}).when(mSubscriptionManager)
+                .getActiveSubscriptionIdList();
+        doReturn(FAKE_SUB_ID_1).when(mSubscriptionController).getPhoneId(eq(FAKE_SUB_ID_1));
+        doReturn(FAKE_SUB_ID_2).when(mSubscriptionController).getPhoneId(eq(FAKE_SUB_ID_2));
+        doReturn(2).when(mTelephonyManager).getSimCount();
+        doReturn(FAKE_MCC_MNC_1).when(mTelephonyManager).getSimOperatorNumeric(eq(FAKE_SUB_ID_1));
+        doReturn(FAKE_MCC_MNC_2).when(mTelephonyManager).getSimOperatorNumeric(eq(FAKE_SUB_ID_2));
+        // Mock there is no sim inserted before
+        doReturn(null).when(mSubscriptionController)
+                .getSubInfoUsingSlotIndexWithCheck(anyInt(), anyBoolean(), anyString());
+        doReturn("89012604200000000000").when(mIccRecord).getIccId();
+
+        // Mock sending a sim loaded for SIM 1
+        Intent mIntent = new Intent(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED);
+        mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
+                IccCardConstants.INTENT_VALUE_ICC_LOADED);
+        mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_1);
+        mContext.sendBroadcast(mIntent);
+        waitForMs(100);
+
+        SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
+        verify(mSubscriptionManager, times(0)).addSubscriptionInfoRecord(anyString(), anyInt());
+        verify(mSubscriptionController, times(0)).notifySubscriptionInfoChanged();
+        verify(mSubscriptionController, times(0)).setMccMnc(anyString(), anyInt());
+
+        // Mock sending a sim loaded for SIM 2
+        doReturn("89012604200000000001").when(mIccRecord).getIccId();
+        mIntent = new Intent(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED);
+        mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
+                IccCardConstants.INTENT_VALUE_ICC_LOADED);
+        mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_2);
+        mContext.sendBroadcast(mIntent);
+        waitForMs(100);
+
+        verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(eq("89012604200000000000"),
+                eq(FAKE_SUB_ID_1));
+        verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(eq("89012604200000000001"),
+                eq(FAKE_SUB_ID_2));
+        verify(mSubscriptionController, times(1)).setMccMnc(eq(FAKE_MCC_MNC_1), eq(FAKE_SUB_ID_1));
+        verify(mSubscriptionController, times(1)).setMccMnc(eq(FAKE_MCC_MNC_2), eq(FAKE_SUB_ID_2));
+        verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
+    }
+
+    @Test
+    @SmallTest
+    public void testSimLockWithIccId() throws Exception {
         /* no need for IccId query */
 
         replaceInstance(SubscriptionInfoUpdater.class, "mIccId", null,
@@ -361,7 +421,7 @@
         mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
                 IccCardConstants.INTENT_VALUE_ICC_LOCKED);
         mIntent.putExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON, "TESTING");
-        mIntent.putExtra(PhoneConstants.PHONE_KEY, 0);
+        mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_1);
 
         mContext.sendBroadcast(mIntent);
         waitForMs(100);
@@ -369,12 +429,13 @@
         verify(mIccFileHandler, times(0)).loadEFTransparent(anyInt(), any(Message.class));
         SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
         verify(mSubscriptionManager, times(0)).addSubscriptionInfoRecord(
-                anyString(), eq(0));
+                anyString(), eq(FAKE_SUB_ID_1));
         verify(mSubscriptionController, times(0)).notifySubscriptionInfoChanged();
         CarrierConfigManager mConfigManager = (CarrierConfigManager)
                 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
         /* broadcast is done */
-        verify(mConfigManager, times(1)).updateConfigForPhoneId(eq(0),
+        verify(mConfigManager, times(1)).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
                 eq(IccCardConstants.INTENT_VALUE_ICC_LOCKED));
     }
+
 }
\ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index df4f0ee..eaaaa64 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -25,7 +25,7 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 
-import android.app.ActivityManagerNative;
+import android.app.ActivityManager;
 import android.app.IActivityManager;
 import android.content.Context;
 import android.content.IIntentSender;
@@ -41,7 +41,6 @@
 import android.os.RegistrantList;
 import android.os.ServiceManager;
 import android.provider.BlockedNumberContract;
-import android.provider.Settings;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -180,7 +179,11 @@
     @Mock
     protected ImsExternalCallTracker mImsExternalCallTracker;
     @Mock
+    protected AppSmsManager mAppSmsManager;
+    @Mock
     protected DeviceStateMonitor mDeviceStateMonitor;
+    @Mock
+    protected IntentBroadcaster mIntentBroadcaster;
 
     protected TelephonyManager mTelephonyManager;
     protected SubscriptionManager mSubscriptionManager;
@@ -299,7 +302,8 @@
         replaceInstance(ImsManager.class, "sImsManagerInstances", null, mImsManagerInstances);
         replaceInstance(SubscriptionController.class, "sInstance", null, mSubscriptionController);
         replaceInstance(ProxyController.class, "sProxyController", null, mProxyController);
-        replaceInstance(ActivityManagerNative.class, "gDefault", null, mIActivityManagerSingleton);
+        replaceInstance(ActivityManager.class, "IActivityManagerSingleton", null,
+                mIActivityManagerSingleton);
         replaceInstance(CdmaSubscriptionSourceManager.class,
                 "mCdmaSubscriptionSourceChangedRegistrants", mCdmaSSM, mRegistrantList);
         replaceInstance(SimulatedCommandsVerifier.class, "sInstance", null,
@@ -307,6 +311,7 @@
         replaceInstance(Singleton.class, "mInstance", mIActivityManagerSingleton,
                 mIActivityManager);
         replaceInstance(ServiceManager.class, "sCache", null, mServiceManagerMockedServices);
+        replaceInstance(IntentBroadcaster.class, "sIntentBroadcaster", null, mIntentBroadcaster);
 
         mSimulatedCommands = new SimulatedCommands();
         mContextFixture = new ContextFixture();
@@ -359,6 +364,8 @@
                 .getIDeviceIdleController();
         doReturn(mImsExternalCallTracker).when(mTelephonyComponentFactory)
                 .makeImsExternalCallTracker(nullable(ImsPhone.class));
+        doReturn(mAppSmsManager).when(mTelephonyComponentFactory)
+                .makeAppSmsManager(nullable(Context.class));
         doReturn(mCarrierSignalAgent).when(mTelephonyComponentFactory)
                 .makeCarrierSignalAgent(nullable(Phone.class));
         doReturn(mCarrierActionAgent).when(mTelephonyComponentFactory)
@@ -380,6 +387,7 @@
         doReturn(mSST).when(mPhone).getServiceStateTracker();
         doReturn(mCarrierSignalAgent).when(mPhone).getCarrierSignalAgent();
         doReturn(mCarrierActionAgent).when(mPhone).getCarrierActionAgent();
+        doReturn(mAppSmsManager).when(mPhone).getAppSmsManager();
         mPhone.mEriManager = mEriManager;
 
         //mUiccController
@@ -442,9 +450,6 @@
         mSST.mSS = mServiceState;
         mServiceManagerMockedServices.put("connectivity_metrics_logger", mConnMetLoggerBinder);
 
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.ENABLE_CELLULAR_ON_BOOT, 1);
-
         setReady(false);
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/VisualVoicemailSmsFilterTest.java b/tests/telephonytests/src/com/android/internal/telephony/VisualVoicemailSmsFilterTest.java
index 198beab..fab16be 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/VisualVoicemailSmsFilterTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/VisualVoicemailSmsFilterTest.java
@@ -16,40 +16,156 @@
 
 package com.android.internal.telephony;
 
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
 import android.content.Context;
+import android.telecom.PhoneAccountHandle;
 import android.telephony.TelephonyManager;
 import android.telephony.VisualVoicemailSmsFilterSettings;
 
+import com.android.internal.telephony.VisualVoicemailSmsFilter.PhoneAccountHandleConverter;
+
 import junit.framework.TestCase;
 
 import org.mockito.Mockito;
 
+import java.util.Arrays;
+
+/**
+ * Unit test for {@link VisualVoicemailSmsFilter}
+ */
 public class VisualVoicemailSmsFilterTest extends TestCase {
 
     /**
-     * b/29123941 iPhone style notification SMS is neither 3GPP nor 3GPP2, but some plain text
-     * message. {@link android.telephony.SmsMessage.createFromPdu()} will fail to parse it and
-     * return an invalid object, causing {@link NullPointerException} on any operation if not
-     * handled.
+     * PDU for the following message:
+     * <p>originating number: 129
+     * <p>message: //VVM:SYNC:ev=NM;id=143;c=6;t=v;s=11111111111;dt=07/03/2017 18:17 -0800;l=4
+     */
+    private static final byte[][] SYNC_PDU = {{
+            (byte) 0x07, (byte) 0x91, (byte) 0x41, (byte) 0x50, (byte) 0x74, (byte) 0x02,
+            (byte) 0x50, (byte) 0xF5, (byte) 0x44, (byte) 0x03, (byte) 0xC9, (byte) 0x21,
+            (byte) 0xF9, (byte) 0x00, (byte) 0x00, (byte) 0x71, (byte) 0x30, (byte) 0x70,
+            (byte) 0x81, (byte) 0x71, (byte) 0x81, (byte) 0x2B, (byte) 0x53, (byte) 0x06,
+            (byte) 0x05, (byte) 0x04, (byte) 0x07, (byte) 0x10, (byte) 0x01, (byte) 0x01,
+            (byte) 0xAF, (byte) 0x97, (byte) 0xD5, (byte) 0xDA, (byte) 0xD4, (byte) 0x4D,
+            (byte) 0xB3, (byte) 0xCE, (byte) 0xA1, (byte) 0xAE, (byte) 0x6C, (byte) 0xEF,
+            (byte) 0x39, (byte) 0x9B, (byte) 0xBB, (byte) 0x34, (byte) 0xB9, (byte) 0x17,
+            (byte) 0xA3, (byte) 0xCD, (byte) 0x76, (byte) 0xE3, (byte) 0x9E, (byte) 0x6D,
+            (byte) 0x47, (byte) 0xEF, (byte) 0xD9, (byte) 0x77, (byte) 0xF3, (byte) 0x5E,
+            (byte) 0x2C, (byte) 0x16, (byte) 0x8B, (byte) 0xC5, (byte) 0x62, (byte) 0xB1,
+            (byte) 0x58, (byte) 0x2C, (byte) 0x16, (byte) 0xDB, (byte) 0x91, (byte) 0xE9,
+            (byte) 0x3D, (byte) 0xD8, (byte) 0xED, (byte) 0x05, (byte) 0x9B, (byte) 0xBD,
+            (byte) 0x64, (byte) 0xB0, (byte) 0xD8, (byte) 0x0D, (byte) 0x14, (byte) 0xC3,
+            (byte) 0xE9, (byte) 0x62, (byte) 0x37, (byte) 0x50, (byte) 0x0B, (byte) 0x86,
+            (byte) 0x83, (byte) 0xC1, (byte) 0x76, (byte) 0xEC, (byte) 0x1E, (byte) 0x0D}};
+
+    private Context mContext;
+    private TelephonyManager mTelephonyManager;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mContext = Mockito.mock(Context.class);
+        mTelephonyManager = Mockito.mock(TelephonyManager.class);
+        when(mContext.getSystemServiceName(TelephonyManager.class))
+                .thenReturn(Context.TELEPHONY_SERVICE);
+        when(mContext.getSystemService(Context.TELEPHONY_SERVICE))
+                .thenReturn(mTelephonyManager);
+
+        VisualVoicemailSmsFilter.setPhoneAccountHandleConverterForTest(
+                new PhoneAccountHandleConverter() {
+                    @Override
+                    public PhoneAccountHandle fromSubId(int subId) {
+                        return new PhoneAccountHandle(
+                                new ComponentName("com.android.internal.telephony",
+                                        "VisualVoicemailSmsFilterTest"), "foo");
+                    }
+                });
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        VisualVoicemailSmsFilter.setPhoneAccountHandleConverterForTest(null);
+        super.tearDown();
+    }
+
+
+    /**
+     * Notification SMS targeting over devices do not follow 3GPP or 3GPP2 standards, but instead
+     * use a plain text message. {@link android.telephony.SmsMessage#createFromPdu(byte[], String)}
+     * will fail to parse it and return an invalid object, causing {@link NullPointerException} on
+     * any operation if not handled.
      */
     public void testUnsupportedPdu() {
-        Context context = Mockito.mock(Context.class);
-        TelephonyManager telephonyManager = Mockito.mock(TelephonyManager.class);
-        Mockito.when(context.getSystemServiceName(TelephonyManager.class))
-                .thenReturn(Context.TELEPHONY_SERVICE);
-        Mockito.when(context.getSystemService(Mockito.anyString())).thenReturn(telephonyManager);
 
-        VisualVoicemailSmsFilterSettings settings = new VisualVoicemailSmsFilterSettings.Builder()
-                .build();
-
-        Mockito.when(telephonyManager
-                .getVisualVoicemailSmsFilterSettings(Mockito.anyInt()))
-                .thenReturn(settings);
+        setSettings(new VisualVoicemailSmsFilterSettings.Builder().build());
 
         byte[][] pdus = {
                 ("MBOXUPDATE?m=11;server=example.com;"
                         + "port=143;name=1234567890@example.com;pw=CphQJKnYS4jEiDO").getBytes()};
-        VisualVoicemailSmsFilter.filter(context, pdus, SmsConstants.FORMAT_3GPP2, 0, 0);
+        assertFalse(
+                VisualVoicemailSmsFilter.filter(mContext, pdus, SmsConstants.FORMAT_3GPP, 0, 0));
     }
 
+    public void testOriginatingNumber_unspecified_filtered() {
+        setSettings(new VisualVoicemailSmsFilterSettings.Builder().build());
+        assertTrue(VisualVoicemailSmsFilter
+                .filter(mContext, SYNC_PDU, SmsConstants.FORMAT_3GPP, 0, 0));
+    }
+
+    public void testOriginatingNumber_match_filtered() {
+        setSettings(
+                new VisualVoicemailSmsFilterSettings.Builder().setOriginatingNumbers(
+                        Arrays.asList("129")
+                ).build());
+        assertTrue(VisualVoicemailSmsFilter
+                .filter(mContext, SYNC_PDU, SmsConstants.FORMAT_3GPP, 0, 0));
+    }
+
+    public void testOriginatingNumber_mismatch_notFiltered() {
+        setSettings(
+                new VisualVoicemailSmsFilterSettings.Builder().setOriginatingNumbers(
+                        Arrays.asList("128")
+                ).build());
+        assertFalse(VisualVoicemailSmsFilter
+                .filter(mContext, SYNC_PDU, SmsConstants.FORMAT_3GPP, 0, 0));
+    }
+
+    public void testDestinationPort_anyMatch_filtered() {
+        setSettings(new VisualVoicemailSmsFilterSettings.Builder()
+                .setDestinationPort(123).build());
+        assertTrue(VisualVoicemailSmsFilter
+                .filter(mContext, SYNC_PDU, SmsConstants.FORMAT_3GPP, 123, 0));
+    }
+
+    public void testDestinationPort_anyData_filtered() {
+        setSettings(new VisualVoicemailSmsFilterSettings.Builder()
+                .setDestinationPort(VisualVoicemailSmsFilterSettings.DESTINATION_PORT_DATA_SMS)
+                .build());
+        assertTrue(VisualVoicemailSmsFilter
+                .filter(mContext, SYNC_PDU, SmsConstants.FORMAT_3GPP, 456, 0));
+    }
+
+    public void testDestinationPort_anyData_textReceived_notFiltered() {
+        setSettings(new VisualVoicemailSmsFilterSettings.Builder()
+                .setDestinationPort(VisualVoicemailSmsFilterSettings.DESTINATION_PORT_DATA_SMS)
+                .build());
+        assertFalse(VisualVoicemailSmsFilter
+                .filter(mContext, SYNC_PDU, SmsConstants.FORMAT_3GPP, -1, 0));
+    }
+
+
+    public void testDestinationPort_mismatch_notFiltered() {
+        setSettings(new VisualVoicemailSmsFilterSettings.Builder()
+                .setDestinationPort(123).build());
+        assertFalse(VisualVoicemailSmsFilter
+                .filter(mContext, SYNC_PDU, SmsConstants.FORMAT_3GPP, 456, 0));
+    }
+
+    private void setSettings(VisualVoicemailSmsFilterSettings settings) {
+        when(mTelephonyManager.getActiveVisualVoicemailSmsFilterSettings(anyInt()))
+                .thenReturn(settings);
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java
index a26741f..5786a70 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java
@@ -16,6 +16,19 @@
 
 package com.android.internal.telephony.cdma;
 
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
@@ -40,6 +53,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
@@ -47,18 +61,6 @@
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
 public class CdmaInboundSmsHandlerTest extends TelephonyTest {
     @Mock
     private SmsStorageMonitor mSmsStorageMonitor;
@@ -177,6 +179,7 @@
     @FlakyTest
     @Test
     @MediumTest
+    @Ignore
     public void testNewSms() {
         transitionFromStartupToIdle();
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsCbTest.java b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsCbTest.java
index 5833cc6..ab7fd7f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsCbTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsCbTest.java
@@ -486,7 +486,7 @@
     public void testCmasUnsupportedCharSet() throws Exception {
         SmsMessage msg = createCmasSmsMessage(SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT,
                 12345, BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH,
-                UserData.ENCODING_GSM_DCS, EXTREME_ALERT, -1, -1, -1, -1, -1);
+                0x1F, EXTREME_ALERT, -1, -1, -1, -1, -1);
 
         SmsCbMessage cbMessage = msg.parseBroadcastSms();
         assertNull("expected null for unsupported charset", cbMessage);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
index d22fe6f..3624ec1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
@@ -749,4 +749,67 @@
             }
         }
     }
-}
+
+    @Test
+    @SmallTest
+    public void testEqualsRoamingProtocol() throws Exception {
+        ApnSetting apn1 = new ApnSetting(
+                1234,
+                "310260",
+                "",
+                "ims",
+                "",
+                "",
+                "",
+                "",
+                "",
+                "",
+                "",
+                -1,
+                 new String[]{"ims"},
+                "IPV6",
+                "",
+                true,
+                0,
+                131071,
+                0,
+                false,
+                0,
+                0,
+                0,
+                1440,
+                "",
+                "");
+
+        ApnSetting apn2 = new ApnSetting(
+                1235,
+                "310260",
+                "",
+                "ims",
+                "",
+                "",
+                "",
+                "",
+                "",
+                "",
+                "",
+                -1,
+                new String[]{"ims"},
+                "IPV6",
+                "IPV6",
+                true,
+                0,
+                131072,
+                0,
+                false,
+                0,
+                0,
+                0,
+                1440,
+                "",
+                "");
+
+        assertTrue(apn1.equals(apn2, false));
+        assertFalse(apn1.equals(apn2, true));
+    }
+}
\ No newline at end of file
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 1e34910..76380c7 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
@@ -314,4 +314,15 @@
                 .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED));
         assertFalse(getNetworkInfo().isMetered());
     }
-}
\ No newline at end of file
+
+    @SmallTest
+    public void testIsIpAddress() throws Exception {
+        // IPv4
+        assertTrue(DataConnection.isIpAddress("1.2.3.4"));
+        assertTrue(DataConnection.isIpAddress("127.0.0.1"));
+
+        // IPv6
+        assertTrue(DataConnection.isIpAddress("::1"));
+        assertTrue(DataConnection.isIpAddress("2001:4860:800d::68"));
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcFailCauseTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcFailCauseTest.java
index 2f22420..6ef1e39 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcFailCauseTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcFailCauseTest.java
@@ -16,22 +16,22 @@
 
 package com.android.internal.telephony.dataconnection;
 
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.internal.telephony.TelephonyTest;
-import com.android.internal.telephony.dataconnection.ApnSetting;
-
-import junit.framework.TestCase;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
 import java.util.ArrayList;
-import java.util.List;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
 
 public class DcFailCauseTest extends TelephonyTest {
 
@@ -144,12 +144,38 @@
 
     @Test
     @SmallTest
-    public void testPermanentFail() throws Exception {
+    public void testPermanentFailDefault() throws Exception {
         for (DcFailCauseData data : mFailCauseDataList) {
-            assertEquals("cause = " + data.mCause, data.mPermanentFailure,
-                    DcFailCause.fromInt(data.mCause).isPermanentFail());
+            assertEquals("cause = " + data.mCause, data.mPermanentFailure, DcFailCause.fromInt(
+                    data.mCause).isPermanentFailure(mContext, mPhone.getSubId()));
         }
-        assertFalse(DcFailCause.fromInt(123456).isPermanentFail());
+        assertFalse(DcFailCause.fromInt(123456).isPermanentFailure(mContext, mPhone.getSubId()));
+    }
+
+    @Test
+    @SmallTest
+    public void testPermanentFailConfigured() throws Exception {
+
+        doReturn(2).when(mPhone).getSubId();
+        PersistableBundle mBundle = mContextFixture.getCarrierConfigBundle();
+        mBundle.putStringArray(
+                CarrierConfigManager.KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS,
+                new String[]{"SERVICE_OPTION_NOT_SUBSCRIBED", "TETHERED_CALL_ACTIVE"});
+
+        // Run it twice to make sure the cached carrier config is working as expected.
+        for (int i = 0; i < 2; i++) {
+            for (DcFailCauseData data : mFailCauseDataList) {
+                if (DcFailCause.fromInt(data.mCause).equals(
+                        DcFailCause.SERVICE_OPTION_NOT_SUBSCRIBED) ||
+                        DcFailCause.fromInt(data.mCause).equals(DcFailCause.TETHERED_CALL_ACTIVE)) {
+                    assertTrue("cause = " + data.mCause, DcFailCause.fromInt(data.mCause).
+                            isPermanentFailure(mContext, mPhone.getSubId()));
+                } else {
+                    assertFalse("cause = " + data.mCause, DcFailCause.fromInt(data.mCause).
+                            isPermanentFailure(mContext, mPhone.getSubId()));
+                }
+            }
+        }
     }
 
     @Test
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 3a8cf61..1ca9acc 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
@@ -29,7 +29,6 @@
 import static org.mockito.Matchers.anyLong;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.nullable;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
@@ -38,7 +37,6 @@
 
 import android.app.AlarmManager;
 import android.app.PendingIntent;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.database.Cursor;
@@ -70,10 +68,10 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyTest;
-import com.android.internal.telephony.TestApplication;
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
@@ -97,10 +95,14 @@
 
     private final static List<String> sApnTypes = Arrays.asList(
             "default", "mms", "cbs", "fota", "supl", "ia", "emergency", "dun", "hipri", "ims");
-
+    private static final int LTE_BEARER_BITMASK = 1 << (ServiceState.RIL_RADIO_TECHNOLOGY_LTE - 1);
+    private static final int EHRPD_BEARER_BITMASK =
+            1 << (ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD - 1);
     public static final String FAKE_APN1 = "FAKE APN 1";
     public static final String FAKE_APN2 = "FAKE APN 2";
     public static final String FAKE_APN3 = "FAKE APN 3";
+    public static final String FAKE_APN4 = "FAKE APN 4";
+    public static final String FAKE_APN5 = "FAKE APN 5";
     public static final String FAKE_IFNAME = "FAKE IFNAME";
     public static final String FAKE_PCSCF_ADDRESS = "22.33.44.55";
     public static final String FAKE_GATEWAY = "11.22.33.44";
@@ -208,7 +210,7 @@
                             "IP",                   // protocol
                             "IP",                   // roaming_protocol
                             1,                      // carrier_enabled
-                            0,                      // bearer
+                            ServiceState.RIL_RADIO_TECHNOLOGY_LTE, // bearer
                             0,                      // bearer_bitmask
                             0,                      // profile_id
                             0,                      // modem_cognitive
@@ -237,7 +239,7 @@
                             "IP",                   // protocol
                             "IP",                   // roaming_protocol
                             1,                      // carrier_enabled
-                            0,                      // bearer
+                            ServiceState.RIL_RADIO_TECHNOLOGY_LTE, // bearer,
                             0,                      // bearer_bitmask
                             0,                      // profile_id
                             0,                      // modem_cognitive
@@ -278,6 +280,63 @@
                             ""                      // mnvo_match_data
                     });
 
+                    mc.addRow(new Object[]{
+                            2166,                   // id
+                            plmn,                   // numeric
+                            "sp-mode ehrpd",        // name
+                            FAKE_APN4,              // apn
+                            "",                     // proxy
+                            "",                     // port
+                            "",                     // mmsc
+                            "",                     // mmsproxy
+                            "",                     // mmsport
+                            "",                     // user
+                            "",                     // password
+                            -1,                     // authtype
+                            "default,supl",         // types
+                            "IP",                   // protocol
+                            "IP",                   // roaming_protocol
+                            1,                      // carrier_enabled
+                            ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD, // bearer
+                            0,                      // bearer_bitmask
+                            0,                      // profile_id
+                            0,                      // modem_cognitive
+                            0,                      // max_conns
+                            0,                      // wait_time
+                            0,                      // max_conns_time
+                            0,                      // mtu
+                            "",                     // mvno_type
+                            ""                      // mnvo_match_data
+                    });
+
+                    mc.addRow(new Object[]{
+                            2166,                   // id
+                            plmn,                   // numeric
+                            "b-mobile for Nexus",   // name
+                            FAKE_APN5,              // apn
+                            "",                     // proxy
+                            "",                     // port
+                            "",                     // mmsc
+                            "",                     // mmsproxy
+                            "",                     // mmsport
+                            "",                     // user
+                            "",                     // password
+                            -1,                     // authtype
+                            "dun",                  // types
+                            "IP",                   // protocol
+                            "IP",                   // roaming_protocol
+                            1,                      // carrier_enabled
+                            0,                      // bearer
+                            0,                      // bearer_bitmask
+                            0,                      // profile_id
+                            0,                      // modem_cognitive
+                            0,                      // max_conns
+                            0,                      // wait_time
+                            0,                      // max_conns_time
+                            0,                      // mtu
+                            "",                     // mvno_type
+                            ""                      // mnvo_match_data
+                    });
                     return mc;
                 }
             }
@@ -288,15 +347,13 @@
 
     @Before
     public void setUp() throws Exception {
-        // set the lazy cp to the real content provider in order to use the real settings
-        ContentResolver realContentResolver = TestApplication.getAppContext().getContentResolver();
-        Settings.Global.getInt(realContentResolver, Settings.Global.MOBILE_DATA, 1);
-
         logd("DcTrackerTest +Setup!");
         super.setUp(getClass().getSimpleName());
 
         doReturn("fake.action_detached").when(mPhone).getActionDetached();
         doReturn("fake.action_attached").when(mPhone).getActionAttached();
+        doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_LTE).when(mServiceState)
+                .getRilDataRadioTechnology();
         doReturn("44010").when(mSimRecords).getOperatorNumeric();
 
         mContextFixture.putStringArrayResource(com.android.internal.R.array.networkAttributes,
@@ -371,21 +428,21 @@
     }
 
     private void verifyDataProfile(DataProfile dp, String apn, int profileId,
-                                   int supportedApnTypesBitmap) {
+                                   int supportedApnTypesBitmap, int type, int bearerBitmask) {
         assertEquals(profileId, dp.profileId);
         assertEquals(apn, dp.apn);
         assertEquals("IP", dp.protocol);
         assertEquals(0, dp.authType);
         assertEquals("", dp.user);
         assertEquals("", dp.password);
-        assertEquals(0, dp.type);
+        assertEquals(type, dp.type);
         assertEquals(0, dp.maxConnsTime);
         assertEquals(0, dp.maxConns);
         assertEquals(0, dp.waitTime);
         assertTrue(dp.enabled);
         assertEquals(supportedApnTypesBitmap, dp.supportedApnTypesBitmap);
         assertEquals("IP", dp.roamingProtocol);
-        assertEquals(0, dp.bearerBitmap);
+        assertEquals(bearerBitmask, dp.bearerBitmap);
         assertEquals(0, dp.mtu);
         assertEquals("", dp.mvnoType);
         assertEquals("", dp.mvnoMatchData);
@@ -396,7 +453,7 @@
         verify(mPhone, times(1)).notifyDataConnection(eq(Phone.REASON_CONNECTED),
                 eq(PhoneConstants.APN_TYPE_DEFAULT));
 
-        verify(mAlarmManager, times(1)).set(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), anyLong(),
+        verify(mAlarmManager, times(1)).set(eq(AlarmManager.ELAPSED_REALTIME), anyLong(),
                 any(PendingIntent.class));
 
         assertEquals(apnSetting, mDct.getActiveApnString(PhoneConstants.APN_TYPE_DEFAULT));
@@ -480,9 +537,9 @@
         ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
         // Verify if RIL command was sent properly.
         verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS), dpCaptor.capture(),
+                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
                 eq(false), eq(false), any(Message.class));
-        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5);
+        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, LTE_BEARER_BITMASK);
 
         verifyDataConnected(FAKE_APN1);
     }
@@ -546,9 +603,9 @@
         ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
         // Verify if RIL command was sent properly.
         verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS), dpCaptor.capture(),
+                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
                 eq(false), eq(false), any(Message.class));
-        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5);
+        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, LTE_BEARER_BITMASK);
 
         // Make sure we never notify connected because the data call setup is supposed to fail.
         verify(mPhone, never()).notifyDataConnection(eq(Phone.REASON_CONNECTED),
@@ -572,9 +629,9 @@
         dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
         // Verify if RIL command was sent properly.
         verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
-                eq(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS), dpCaptor.capture(),
+                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
                 eq(false), eq(false), any(Message.class));
-        verifyDataProfile(dpCaptor.getValue(), FAKE_APN2, 0, 5);
+        verifyDataProfile(dpCaptor.getValue(), FAKE_APN2, 0, 5, 1, LTE_BEARER_BITMASK);
 
         // Verify connected with APN2 setting.
         verifyDataConnected(FAKE_APN2);
@@ -582,6 +639,8 @@
 
     @Test
     @MediumTest
+    @Ignore
+    @FlakyTest
     public void testUserDisableData() throws Exception {
         //step 1: setup two DataCalls one for Metered: default, another one for Non-metered: IMS
         //set Default and MMS to be metered in the CarrierConfigManager
@@ -605,9 +664,9 @@
         waitForMs(200);
         ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
         verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
-                eq(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS), dpCaptor.capture(),
+                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
                 eq(false), eq(false), any(Message.class));
-        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5);
+        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, LTE_BEARER_BITMASK);
 
         logd("Sending DATA_DISABLED_CMD");
         mDct.setDataEnabled(false);
@@ -656,14 +715,14 @@
         waitForMs(300);
         ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
         verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
-                eq(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS), dpCaptor.capture(),
+                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
                 eq(false), eq(false), any(Message.class));
-        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5);
+        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, LTE_BEARER_BITMASK);
 
         //user is in roaming
         doReturn(true).when(mServiceState).getDataRoaming();
         logd("Sending DISABLE_ROAMING_CMD");
-        mDct.setDataRoamingEnabled(false);
+        mDct.setDataRoamingEnabledByUser(false);
         mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_ROAMING_ON));
         waitForMs(200);
 
@@ -675,7 +734,7 @@
         assertEquals(DctConstants.State.CONNECTED, mDct.getState(PhoneConstants.APN_TYPE_IMS));
 
         // reset roaming settings / data enabled settings at end of this test
-        mDct.setDataRoamingEnabled(roamingEnabled);
+        mDct.setDataRoamingEnabledByUser(roamingEnabled);
         mDct.setDataEnabled(dataEnabled);
         waitForMs(200);
     }
@@ -701,7 +760,7 @@
         mDct.setDataEnabled(true);
 
         logd("Sending DISABLE_ROAMING_CMD");
-        mDct.setDataRoamingEnabled(false);
+        mDct.setDataRoamingEnabledByUser(false);
 
         logd("Sending EVENT_RECORDS_LOADED");
         mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
@@ -712,18 +771,20 @@
         waitForMs(200);
 
         waitForMs(200);
-        verify(mSimulatedCommandsVerifier, times(1)).setInitialAttachApn(any(DataProfile.class),
-                eq(true), nullable(Message.class));
-
         ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
         verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS), dpCaptor.capture(),
+                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
                 eq(false), eq(false), any(Message.class));
-        verifyDataProfile(dpCaptor.getValue(), FAKE_APN3, 2, 64);
+        verifyDataProfile(dpCaptor.getValue(), FAKE_APN3, 2, 64, 0, 0);
 
         assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
         assertEquals(DctConstants.State.IDLE, mDct.getState(PhoneConstants.APN_TYPE_DEFAULT));
         assertEquals(DctConstants.State.CONNECTED, mDct.getState(PhoneConstants.APN_TYPE_IMS));
+
+        // reset roaming settings / data enabled settings at end of this test
+        mDct.setDataRoamingEnabledByUser(roamingEnabled);
+        mDct.setDataEnabled(dataEnabled);
+        waitForMs(200);
     }
 
     // Test the default data switch scenario.
@@ -814,9 +875,9 @@
 
         ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
         verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
-                eq(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS), dpCaptor.capture(),
+                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
                 eq(false), eq(false), any(Message.class));
-        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5);
+        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, LTE_BEARER_BITMASK);
         assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
 
         Message msg = mDct.obtainMessage(DctConstants.EVENT_SET_CARRIER_DATA_ENABLED);
@@ -858,7 +919,7 @@
         waitForMs(200);
 
         verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS), any(DataProfile.class),
+                eq(mServiceState.getRilDataRadioTechnology()), any(DataProfile.class),
                 eq(false), eq(false), any(Message.class));
     }
 
@@ -884,7 +945,7 @@
         waitForMs(200);
 
         verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS), any(DataProfile.class),
+                eq(mServiceState.getRilDataRadioTechnology()), any(DataProfile.class),
                 eq(false), eq(false), any(Message.class));
     }
 
@@ -1018,4 +1079,137 @@
         verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(
                 anyInt(), any(DataProfile.class), eq(false), eq(false), any(Message.class));
     }
+
+    // Test update waiting apn list when on data rat change
+    @Test
+    @SmallTest
+    public void testUpdateWaitingApnListOnDataRatChange() throws Exception {
+        doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD).when(mServiceState)
+                .getRilDataRadioTechnology();
+        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+        mDct.setEnabled(0, true);
+        mDct.setDataEnabled(true);
+        initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
+
+        logd("Sending EVENT_RECORDS_LOADED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
+        waitForMs(200);
+
+        logd("Sending EVENT_DATA_CONNECTION_ATTACHED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null));
+        waitForMs(200);
+
+        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
+        // Verify if RIL command was sent properly.
+        verify(mSimulatedCommandsVerifier).setupDataCall(
+                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
+                eq(false), eq(false), any(Message.class));
+        verifyDataProfile(dpCaptor.getValue(), FAKE_APN4, 0, 5, 2, EHRPD_BEARER_BITMASK);
+        assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
+
+        //data rat change from ehrpd to lte
+        logd("Sending EVENT_DATA_RAT_CHANGED");
+        doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_LTE).when(mServiceState)
+                .getRilDataRadioTechnology();
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_RAT_CHANGED, null));
+        waitForMs(200);
+
+        // Verify the disconnected data call due to rat change and retry manger schedule another
+        // data call setup
+        verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(anyInt(), anyInt(),
+                any(Message.class));
+        verify(mAlarmManager, times(1)).setExact(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+                anyLong(), any(PendingIntent.class));
+
+        // Simulate the timer expires.
+        Intent intent = new Intent("com.android.internal.telephony.data-reconnect.default");
+        intent.putExtra("reconnect_alarm_extra_type", PhoneConstants.APN_TYPE_DEFAULT);
+        intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, 0);
+        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        mContext.sendBroadcast(intent);
+        waitForMs(200);
+
+        // Verify if RIL command was sent properly.
+        verify(mSimulatedCommandsVerifier).setupDataCall(
+                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
+                eq(false), eq(false), any(Message.class));
+        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, LTE_BEARER_BITMASK);
+        assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
+    }
+
+    // Test for fetchDunApn()
+    @Test
+    @SmallTest
+    public void testFetchDunApn() {
+        logd("Sending EVENT_RECORDS_LOADED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
+        waitForMs(200);
+
+        String dunApnString = "[ApnSettingV3]HOT mobile PC,pc.hotm,,,,,,,,,440,10,,DUN,,,true,"
+                + "0,,,,,,,,";
+        ApnSetting dunApnExpected = ApnSetting.fromString(dunApnString);
+
+        Settings.Global.putString(mContext.getContentResolver(),
+                Settings.Global.TETHER_DUN_APN, dunApnString);
+        // should return APN from Setting
+        ApnSetting dunApn = mDct.fetchDunApn();
+        assertTrue(dunApnExpected.equals(dunApn));
+
+        Settings.Global.putString(mContext.getContentResolver(),
+                Settings.Global.TETHER_DUN_APN, null);
+        // should return APN from db
+        dunApn = mDct.fetchDunApn();
+        assertEquals(FAKE_APN5, dunApn.apn);
+    }
+
+    // Test oos
+    @Test
+    @SmallTest
+    public void testDataRatChangeOOS() throws Exception {
+        doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD).when(mServiceState)
+                .getRilDataRadioTechnology();
+        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+        mDct.setEnabled(0, true);
+        mDct.setDataEnabled(true);
+        initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
+
+        logd("Sending EVENT_RECORDS_LOADED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
+        waitForMs(200);
+
+        logd("Sending EVENT_DATA_CONNECTION_ATTACHED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null));
+        waitForMs(200);
+
+        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
+        // Verify if RIL command was sent properly.
+        verify(mSimulatedCommandsVerifier).setupDataCall(
+                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
+                eq(false), eq(false), any(Message.class));
+        verifyDataProfile(dpCaptor.getValue(), FAKE_APN4, 0, 5, 2, EHRPD_BEARER_BITMASK);
+        assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
+
+        // Data rat change from ehrpd to unknown due to OOS
+        logd("Sending EVENT_DATA_RAT_CHANGED");
+        doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN).when(mServiceState)
+                .getRilDataRadioTechnology();
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_RAT_CHANGED, null));
+        waitForMs(200);
+
+        // Verify data connection is on
+        verify(mSimulatedCommandsVerifier, times(0)).deactivateDataCall(anyInt(), anyInt(),
+                any(Message.class));
+
+        // Data rat resume from unknown to ehrpd
+        doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD).when(mServiceState)
+                .getRilDataRadioTechnology();
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_RAT_CHANGED, null));
+        waitForMs(200);
+
+        // Verify the same data connection
+        assertEquals(FAKE_APN4, mDct.getActiveApnString(PhoneConstants.APN_TYPE_DEFAULT));
+        assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmCellBroadcastHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmCellBroadcastHandlerTest.java
index 2575cac..b117eb1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmCellBroadcastHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmCellBroadcastHandlerTest.java
@@ -39,7 +39,6 @@
 import android.provider.Settings;
 import android.provider.Telephony;
 import android.support.test.filters.FlakyTest;
-import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.internal.telephony.SmsStorageMonitor;
 import com.android.internal.telephony.TelephonyTest;
@@ -47,12 +46,13 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Test;
+import org.junit.Ignore;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
 import java.util.List;
 
+@Ignore
 public class GsmCellBroadcastHandlerTest extends TelephonyTest {
     @Mock
     private SmsStorageMonitor mSmsStorageMonitor;
@@ -100,7 +100,6 @@
     }
 
     @FlakyTest
-    @Test @SmallTest
     public void testBroadcastSms() {
         mContextFixture.putResource(
                 com.android.internal.R.string.config_defaultCellBroadcastReceiverPkg,
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
index 92f2bf6..e9a16bf 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
@@ -64,6 +64,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
@@ -225,6 +226,8 @@
         assertEquals("IdleState", getCurrentState().getName());
     }
 
+    @FlakyTest
+    @Ignore
     @Test
     @MediumTest
     public void testNewSms() {
@@ -304,7 +307,7 @@
 
         mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_INJECT_SMS, new AsyncResult(null,
                 mSmsMessage, null));
-        waitForMs(100);
+        waitForMs(200);
 
         verifySmsIntentBroadcasts(0);
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
index 6da7ca9..335634d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
@@ -16,33 +16,54 @@
 
 package com.android.internal.telephony.gsm;
 
+import static android.telephony.SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED;
+
+import static com.android.internal.telephony.SmsUsageMonitor.CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE;
+import static com.android.internal.telephony.SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW;
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.ActivityManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.location.Country;
 import android.location.CountryDetector;
 import android.os.HandlerThread;
 import android.os.Message;
 import android.os.SystemProperties;
+import android.provider.Settings;
 import android.provider.Telephony;
 import android.support.test.filters.FlakyTest;
+import android.telephony.SmsManager;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Singleton;
 
+import com.android.internal.telephony.ContextFixture;
 import com.android.internal.telephony.ISub;
 import com.android.internal.telephony.ImsSMSDispatcher;
+import com.android.internal.telephony.SMSDispatcher;
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.TelephonyTestUtils;
+import com.android.internal.telephony.TestApplication;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
+import java.util.HashMap;
+
 public class GsmSmsDispatcherTest extends TelephonyTest {
     @Mock
     private android.telephony.SmsMessage mSmsMessage;
@@ -55,7 +76,21 @@
     @Mock
     private CountryDetector mCountryDetector;
     @Mock
+    private SMSDispatcher.SmsTracker mSmsTracker;
+    @Mock
     private ISub.Stub mISubStub;
+    private Object mLock = new Object();
+    private boolean mReceivedTestIntent = false;
+    private static final String TEST_INTENT = "com.android.internal.telephony.TEST_INTENT";
+    private BroadcastReceiver mTestReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            logd("onReceive");
+            synchronized (mLock) {
+                mReceivedTestIntent = true;
+            }
+        }
+    };
 
     private GsmSMSDispatcher mGsmSmsDispatcher;
     private GsmSmsDispatcherTestHandler mGsmSmsDispatcherTestHandler;
@@ -147,4 +182,57 @@
             return systemEmergencyNumbers.split(",")[0];
         }
     }
+
+    @Test @SmallTest
+    public void testSendTextWithInvalidDestAddr() throws Exception {
+        // unmock ActivityManager to be able to register receiver, create real PendingIntent and
+        // receive TEST_INTENT
+        restoreInstance(Singleton.class, "mInstance", mIActivityManagerSingleton);
+        restoreInstance(ActivityManager.class, "IActivityManagerSingleton", null);
+        Context realContext = TestApplication.getAppContext();
+        realContext.registerReceiver(mTestReceiver, new IntentFilter(TEST_INTENT));
+        PendingIntent pendingIntent = PendingIntent.getBroadcast(realContext, 0,
+                new Intent(TEST_INTENT), 0);
+        // send invalid dest address: +
+        mGsmSmsDispatcher.sendText("+", "222" /*scAddr*/, TAG,
+                pendingIntent, null, null, null, false);
+        waitForMs(500);
+        verify(mSimulatedCommandsVerifier, times(0)).sendSMS(anyString(), anyString(),
+                any(Message.class));
+        synchronized (mLock) {
+            assertEquals(true, mReceivedTestIntent);
+            assertEquals(SmsManager.RESULT_ERROR_NULL_PDU, mTestReceiver.getResultCode());
+        }
+    }
+
+    @Test
+    public void testSendRawPduWithEventStopSending() throws Exception {
+        setupMockPackagePermissionChecks();
+        mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
+
+        // return a fake value to pass getData()
+        HashMap data = new HashMap<String, String>();
+        data.put("pdu", new byte[1]);
+        when(mSmsTracker.getData()).thenReturn(data);
+
+        // Set values to return to simulate EVENT_STOP_SENDING
+        when(mSmsUsageMonitor.checkDestination(any(), any()))
+                .thenReturn(CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE);
+        when(mSmsUsageMonitor.getPremiumSmsPermission(any()))
+                .thenReturn(PREMIUM_SMS_PERMISSION_NEVER_ALLOW);
+        when(mSmsTracker.getAppPackageName()).thenReturn("");
+
+        // Settings.Global.DEVICE_PROVISIONED to 1
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.DEVICE_PROVISIONED, 1);
+
+        mGsmSmsDispatcher.sendRawPdu(mSmsTracker);
+
+        verify(mSmsUsageMonitor, times(1)).checkDestination(any(), any());
+        verify(mSmsUsageMonitor, times(1)).getPremiumSmsPermission(any());
+        ArgumentCaptor<Integer> argumentCaptor = ArgumentCaptor
+                .forClass(Integer.class);
+        verify(mSmsTracker, times(1)).onFailed(any(), argumentCaptor.capture(), anyInt());
+        assertEquals(RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED, (int) argumentCaptor.getValue());
+    }
 }
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 28855ff..7b831a0 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
@@ -16,33 +16,10 @@
 
 package com.android.internal.telephony.ims;
 
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Pair;
-
-import com.android.ims.internal.IImsServiceFeatureListener;
-
-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.Spy;
-
-import java.util.HashSet;
-
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
+
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
@@ -51,10 +28,36 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.support.test.filters.FlakyTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Pair;
+
+import com.android.ims.internal.IImsServiceFeatureListener;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Spy;
+
+import java.util.HashSet;
+
 /**
  * Unit tests for ImsServiceController
  */
 @RunWith(AndroidJUnit4.class)
+@Ignore
 public class ImsServiceControllerTest extends ImsTestBase {
 
     private static final int RETRY_TIMEOUT = 50; // ms
@@ -91,8 +94,8 @@
     /**
      * Tests that Context.bindService is called with the correct parameters when we call bind.
      */
+    @FlakyTest
     @Test
-    @SmallTest
     public void testBindService() {
         HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
         testFeatures.add(new Pair<>(1, 1));
@@ -113,8 +116,8 @@
     /**
      * Verify that if bind is called multiple times, we only call bindService once.
      */
+    @FlakyTest
     @Test
-    @SmallTest
     public void testBindFailureWhenBound() {
         HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
         testFeatures.add(new Pair<>(1, 1));
@@ -130,8 +133,8 @@
      * Tests ImsServiceController callbacks are properly called when an ImsService is bound and
      * connected.
      */
+    @FlakyTest
     @Test
-    @SmallTest
     public void testBindServiceAndConnected() throws RemoteException {
         HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
         testFeatures.add(new Pair<>(1, 1));
@@ -157,8 +160,8 @@
      * Tests ImsServiceController callbacks are properly called when an ImsService is bound and
      * connected.
      */
+    @FlakyTest
     @Test
-    @SmallTest
     public void testBindServiceAndConnectedDisconnected() throws RemoteException {
         HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
         testFeatures.add(new Pair<>(1, 1));
@@ -183,8 +186,8 @@
      * Tests ImsServiceController callbacks are properly called when an ImsService is bound and
      * connected.
      */
+    @FlakyTest
     @Test
-    @SmallTest
     public void testBindServiceBindUnbind() throws RemoteException {
         HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
         testFeatures.add(new Pair<>(1, 1));
@@ -209,8 +212,8 @@
     /**
      * Ensures that imsServiceFeatureRemoved is called when the binder dies in another process.
      */
+    @FlakyTest
     @Test
-    @SmallTest
     public void testBindServiceAndBinderDied() throws RemoteException {
         HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
         testFeatures.add(new Pair<>(1, 1));
@@ -234,8 +237,8 @@
     /**
      * Ensures ImsService and ImsResolver are notified when a feature is added.
      */
+    @FlakyTest
     @Test
-    @SmallTest
     public void testBindServiceAndAddFeature() throws RemoteException {
         HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
         testFeatures.add(new Pair<>(1, 1));
@@ -259,8 +262,8 @@
     /**
      * Ensures ImsService and ImsResolver are notified when a feature is added.
      */
+    @FlakyTest
     @Test
-    @SmallTest
     public void testBindServiceAndRemoveFeature() throws RemoteException {
         HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
         testFeatures.add(new Pair<>(1, 1));
@@ -289,8 +292,8 @@
     /**
      * Ensures ImsService and ImsResolver are notified when all features are removed.
      */
+    @FlakyTest
     @Test
-    @SmallTest
     public void testBindServiceAndRemoveAllFeatures() throws RemoteException {
         HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
         testFeatures.add(new Pair<>(1, 1));
@@ -321,8 +324,8 @@
     /**
      * Verifies that nothing is notified of a feature change if the service is not bound.
      */
+    @FlakyTest
     @Test
-    @SmallTest
     public void testBindUnbindServiceAndAddFeature() throws RemoteException {
         HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
         testFeatures.add(new Pair<>(1, 1));
@@ -344,8 +347,8 @@
      * Verifies that the ImsServiceController automatically tries to bind again after an untimely
      * binder death.
      */
+    @FlakyTest
     @Test
-    @SmallTest
     public void testAutoBindAfterBinderDied() throws RemoteException {
         HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
         testFeatures.add(new Pair<>(1, 1));
@@ -364,8 +367,8 @@
     /**
      * Ensure that bindService has only been called once before automatic rebind occurs.
      */
+    @FlakyTest
     @Test
-    @SmallTest
     public void testNoAutoBindBeforeTimeout() throws RemoteException {
         HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
         testFeatures.add(new Pair<>(1, 1));
@@ -382,8 +385,8 @@
     /**
      * Ensure that calling unbind stops automatic rebind of the ImsService from occuring.
      */
+    @FlakyTest
     @Test
-    @SmallTest
     public void testUnbindCauseAutoBindCancelAfterBinderDied() throws RemoteException {
         HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
         testFeatures.add(new Pair<>(1, 1));
@@ -404,8 +407,8 @@
      * Ensure that calling bind causes the automatic rebinding to be cancelled or not cause another
      * call to bindService.
      */
+    @FlakyTest
     @Test
-    @SmallTest
     public void testBindCauseAutoBindCancelAfterBinderDied() throws RemoteException {
         HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
         testFeatures.add(new Pair<>(1, 1));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsVideoProviderWrapperTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsVideoProviderWrapperTest.java
new file mode 100644
index 0000000..338e8a4
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsVideoProviderWrapperTest.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.ims;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.when;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.telecom.VideoProfile;
+
+import com.android.ims.internal.ImsVideoCallProviderWrapper;
+import com.android.ims.internal.VideoPauseTracker;
+
+import junit.framework.TestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for the {@link com.android.ims.internal.VideoPauseTracker} class.
+ */
+@RunWith(AndroidJUnit4.class)
+public class ImsVideoProviderWrapperTest extends TestCase {
+    private ImsVideoCallProviderWrapper mImsVideoCallProviderWrapper;
+    @Mock
+    VideoPauseTracker mVideoPauseTracker;
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        MockitoAnnotations.initMocks(this);
+        mImsVideoCallProviderWrapper = new ImsVideoCallProviderWrapper(null, mVideoPauseTracker);
+        when(mVideoPauseTracker.shouldPauseVideoFor(anyInt())).thenReturn(true);
+        when(mVideoPauseTracker.shouldResumeVideoFor(anyInt())).thenReturn(true);
+    }
+
+    @SmallTest
+    @Test
+    public void testIsPause() {
+        assertTrue(ImsVideoCallProviderWrapper.isPauseRequest(VideoProfile.STATE_BIDIRECTIONAL,
+                VideoProfile.STATE_BIDIRECTIONAL | VideoProfile.STATE_PAUSED));
+        assertTrue(ImsVideoCallProviderWrapper.isPauseRequest(VideoProfile.STATE_BIDIRECTIONAL,
+                VideoProfile.STATE_PAUSED));
+        assertFalse(ImsVideoCallProviderWrapper.isPauseRequest(VideoProfile.STATE_PAUSED,
+                VideoProfile.STATE_PAUSED));
+        assertFalse(ImsVideoCallProviderWrapper.isPauseRequest(VideoProfile.STATE_AUDIO_ONLY,
+                VideoProfile.STATE_AUDIO_ONLY));
+    }
+
+    @SmallTest
+    @Test
+    public void testIsResume() {
+        assertTrue(ImsVideoCallProviderWrapper.isResumeRequest(
+                VideoProfile.STATE_BIDIRECTIONAL | VideoProfile.STATE_PAUSED,
+                VideoProfile.STATE_BIDIRECTIONAL));
+        assertTrue(ImsVideoCallProviderWrapper.isResumeRequest(VideoProfile.STATE_PAUSED,
+                VideoProfile.STATE_AUDIO_ONLY));
+        assertFalse(ImsVideoCallProviderWrapper.isResumeRequest(
+                VideoProfile.STATE_BIDIRECTIONAL | VideoProfile.STATE_PAUSED,
+                VideoProfile.STATE_BIDIRECTIONAL | VideoProfile.STATE_PAUSED));
+        assertFalse(ImsVideoCallProviderWrapper.isResumeRequest(VideoProfile.STATE_PAUSED,
+                VideoProfile.STATE_AUDIO_ONLY | VideoProfile.STATE_PAUSED));
+    }
+
+    @SmallTest
+    @Test
+    public void testIsTurnOffCameraRequest() {
+        assertTrue(ImsVideoCallProviderWrapper.isTurnOffCameraRequest(
+                VideoProfile.STATE_BIDIRECTIONAL, VideoProfile.STATE_RX_ENABLED));
+        assertTrue(ImsVideoCallProviderWrapper.isTurnOffCameraRequest(
+                VideoProfile.STATE_BIDIRECTIONAL | VideoProfile.STATE_PAUSED,
+                VideoProfile.STATE_RX_ENABLED));
+        assertFalse(ImsVideoCallProviderWrapper.isTurnOffCameraRequest(
+                VideoProfile.STATE_BIDIRECTIONAL | VideoProfile.STATE_PAUSED,
+                VideoProfile.STATE_BIDIRECTIONAL));
+    }
+
+    @SmallTest
+    @Test
+    public void testIsTurnOnCameraRequest() {
+        assertTrue(ImsVideoCallProviderWrapper.isTurnOnCameraRequest(
+                VideoProfile.STATE_RX_ENABLED, VideoProfile.STATE_BIDIRECTIONAL));
+        assertTrue(ImsVideoCallProviderWrapper.isTurnOnCameraRequest(
+                VideoProfile.STATE_RX_ENABLED,
+                VideoProfile.STATE_BIDIRECTIONAL | VideoProfile.STATE_PAUSED));
+        assertFalse(ImsVideoCallProviderWrapper.isTurnOnCameraRequest(
+                VideoProfile.STATE_BIDIRECTIONAL,
+                VideoProfile.STATE_BIDIRECTIONAL | VideoProfile.STATE_PAUSED));
+    }
+
+    /**
+     * Verifies that the to profile is not changed when a request to turn off the camera is sent
+     * using the broken vendor-format request.
+     */
+    @SmallTest
+    @Test
+    public void testNoFilterWhenDisablingCamera() {
+        mImsVideoCallProviderWrapper.setUseVideoPauseWorkaround(true);
+        VideoProfile fromProfile = new VideoProfile(
+                VideoProfile.STATE_BIDIRECTIONAL | VideoProfile.STATE_PAUSED);
+        VideoProfile toProfile = new VideoProfile(VideoProfile.STATE_RX_ENABLED);
+
+        VideoProfile filteredTo = mImsVideoCallProviderWrapper.maybeFilterPauseResume(fromProfile,
+                toProfile, VideoPauseTracker.SOURCE_INCALL);
+        assertEquals(filteredTo.getVideoState(), toProfile.getVideoState());
+    }
+
+    /**
+     * Verifies that the to profile is not changed when a request to turn on the camera is sent
+     * using the broken vendor-format request.
+     */
+    @SmallTest
+    @Test
+    public void testNoFilterWhenEnablingCamera() {
+        mImsVideoCallProviderWrapper.setUseVideoPauseWorkaround(true);
+        VideoProfile fromProfile = new VideoProfile(
+                VideoProfile.STATE_RX_ENABLED | VideoProfile.STATE_PAUSED);
+        VideoProfile toProfile = new VideoProfile(VideoProfile.STATE_BIDIRECTIONAL);
+
+        VideoProfile filteredTo = mImsVideoCallProviderWrapper.maybeFilterPauseResume(fromProfile,
+                toProfile, VideoPauseTracker.SOURCE_INCALL);
+        assertEquals(filteredTo.getVideoState(), toProfile.getVideoState());
+    }
+
+    /**
+     * Verifies normal operation of filtering of pause request.
+     */
+    @SmallTest
+    @Test
+    public void testNoFilteringOnPause() {
+        VideoProfile fromProfile = new VideoProfile(VideoProfile.STATE_BIDIRECTIONAL);
+        VideoProfile toProfile = new VideoProfile(
+                VideoProfile.STATE_BIDIRECTIONAL | VideoProfile.STATE_PAUSED);
+
+        VideoProfile filteredTo = mImsVideoCallProviderWrapper.maybeFilterPauseResume(fromProfile,
+                toProfile, VideoPauseTracker.SOURCE_INCALL);
+        assertEquals(filteredTo.getVideoState(), toProfile.getVideoState());
+    }
+
+    /**
+     * Verifies normal operation of filtering of pause request.
+     */
+    @SmallTest
+    @Test
+    public void testNoFilteringOnResume() {
+        VideoProfile fromProfile = new VideoProfile(
+                VideoProfile.STATE_BIDIRECTIONAL | VideoProfile.STATE_PAUSED);
+        VideoProfile toProfile = new VideoProfile(VideoProfile.STATE_BIDIRECTIONAL);
+
+        VideoProfile filteredTo = mImsVideoCallProviderWrapper.maybeFilterPauseResume(fromProfile,
+                toProfile, VideoPauseTracker.SOURCE_INCALL);
+        assertEquals(filteredTo.getVideoState(), toProfile.getVideoState());
+    }
+}
+
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 db1dcf1..be8d6f6 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
@@ -37,11 +37,14 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Message;
 import android.support.test.filters.FlakyTest;
+import android.telephony.DisconnectCause;
+import android.telecom.VideoProfile;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.ims.feature.ImsFeature;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -56,6 +59,8 @@
 import com.android.ims.ImsServiceClass;
 import com.android.ims.internal.ImsCallSession;
 import com.android.internal.telephony.Call;
+import com.android.internal.telephony.CallStateException;
+import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyTest;
@@ -65,6 +70,7 @@
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
@@ -80,6 +86,8 @@
     private int mServiceId;
     @Mock
     private ImsCallSession mImsCallSession;
+    @Mock
+    private SharedPreferences mSharedPreferences;
     private Handler mCTHander;
 
     private class ImsCTHandlerThread extends HandlerThread {
@@ -224,6 +232,7 @@
         mImsConnectionStateListener.onFeatureCapabilityChanged(ImsServiceClass.MMTEL,
                 featureEnableArray,
                 featureDisableArray);
+        waitForHandlerAction(mCTHander, 1000);
         assertTrue(mCTUT.isVolteEnabled());
         assertFalse(mCTUT.isVideoCallEnabled());
         // video call not enabled
@@ -235,6 +244,7 @@
         mImsConnectionStateListener.onFeatureCapabilityChanged(ImsServiceClass.MMTEL,
                 featureEnableArray,
                 featureDisableArray);
+        waitForHandlerAction(mCTHander, 1000);
         assertTrue(mCTUT.isVideoCallEnabled());
         verify(mImsPhone, times(1)).notifyForVideoCapabilityChanged(eq(true));
     }
@@ -345,6 +355,31 @@
         assertEquals(Call.State.HOLDING, mCTUT.mBackgroundCall.getState());
     }
 
+    /**
+     * Ensures that the dial method will perform a shared preferences lookup using the correct
+     * shared preference key to determine the CLIR mode.
+     */
+    @Test
+    @SmallTest
+    public void testDialClirMode() {
+        mCTUT.setSharedPreferenceProxy((Context context) -> {
+            return mSharedPreferences;
+        });
+        ArgumentCaptor<String> mStringCaptor = ArgumentCaptor.forClass(String.class);
+        doReturn(CommandsInterface.CLIR_INVOCATION).when(mSharedPreferences).getInt(
+                mStringCaptor.capture(), anyInt());
+
+        try {
+            mCTUT.dial("+17005554141", VideoProfile.STATE_AUDIO_ONLY, null);
+        } catch (CallStateException cse) {
+            cse.printStackTrace();
+            Assert.fail("unexpected exception thrown" + cse.getMessage());
+        }
+
+        // Ensure that the correct key was queried from the shared prefs.
+        assertEquals("clir_key0", mStringCaptor.getValue());
+    }
+
     @FlakyTest
     @Test
     @SmallTest
@@ -494,4 +529,22 @@
         verify(mImsManager, times(2)).open(anyInt(), nullable(PendingIntent.class),
                 nullable(ImsConnectionStateListener.class));
     }
+
+    @Test
+    @SmallTest
+    public void testLowBatteryDisconnectMidCall() {
+        assertEquals(DisconnectCause.LOW_BATTERY, mCTUT.getDisconnectCauseFromReasonInfo(
+                new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_LOW_BATTERY, 0), Call.State.ACTIVE));
+        assertEquals(DisconnectCause.LOW_BATTERY, mCTUT.getDisconnectCauseFromReasonInfo(
+                new ImsReasonInfo(ImsReasonInfo.CODE_LOW_BATTERY, 0), Call.State.ACTIVE));
+    }
+
+    @Test
+    @SmallTest
+    public void testLowBatteryDisconnectDialing() {
+        assertEquals(DisconnectCause.DIAL_LOW_BATTERY, mCTUT.getDisconnectCauseFromReasonInfo(
+                new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_LOW_BATTERY, 0), Call.State.DIALING));
+        assertEquals(DisconnectCause.DIAL_LOW_BATTERY, mCTUT.getDisconnectCauseFromReasonInfo(
+                new ImsReasonInfo(ImsReasonInfo.CODE_LOW_BATTERY, 0), Call.State.DIALING));
+    }
 }
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 fc3c296..3b357d7 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
@@ -66,6 +66,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
@@ -450,7 +451,7 @@
 
     @FlakyTest
     @Test
-    @SmallTest
+    @Ignore
     public void testCallForwardingOption() throws Exception {
         Message msg = mTestHandler.obtainMessage();
         mImsPhoneUT.getCallForwardingOption(CF_REASON_UNCONDITIONAL, msg);
@@ -507,8 +508,9 @@
         assertEquals(msg, messageArgumentCaptor.getValue().obj);
     }
 
+    @FlakyTest
     @Test
-    @SmallTest
+    @Ignore
     public void testEcbm() throws Exception {
         ImsEcbmStateListener imsEcbmStateListener = mImsPhoneUT.getImsEcbmStateListener();
 
@@ -577,6 +579,7 @@
     @FlakyTest
     @Test
     @SmallTest
+    @Ignore
     public void testProcessDisconnectReason() throws Exception {
         // set up CarrierConfig
         PersistableBundle bundle = mContextFixture.getCarrierConfigBundle();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/InProgressCallSessionTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/InProgressCallSessionTest.java
index 612cae7..0e04da8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/InProgressCallSessionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/InProgressCallSessionTest.java
@@ -18,8 +18,8 @@
 
 import android.test.suitebuilder.annotation.SmallTest;
 
-import com.android.internal.telephony.TelephonyProto;
 import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.nano.TelephonyProto;
 
 import org.junit.After;
 import org.junit.Before;
@@ -72,6 +72,6 @@
         }
 
         assertTrue(mCallSession.isEventsDropped());
-        assertEquals(2, mCallSession.events.getFirst().getRilRequestId());
+        assertEquals(2, mCallSession.events.getFirst().rilRequestId);
     }
-}
\ No newline at end of file
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/InProgressSmsSessionTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/InProgressSmsSessionTest.java
index 710f003..b2b9ca5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/InProgressSmsSessionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/InProgressSmsSessionTest.java
@@ -18,8 +18,8 @@
 
 import android.test.suitebuilder.annotation.SmallTest;
 
-import com.android.internal.telephony.TelephonyProto;
 import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.nano.TelephonyProto;
 
 import org.junit.After;
 import org.junit.Before;
@@ -68,7 +68,7 @@
         }
 
         assertTrue(mSmsSession.isEventsDropped());
-        assertEquals(6, mSmsSession.events.getFirst().getRilRequestId());
+        assertEquals(6, mSmsSession.events.getFirst().rilRequestId);
     }
 
     // Test dropped event scenario
@@ -92,4 +92,4 @@
 
         assertEquals(50, mSmsSession.getNumExpectedResponses());
     }
-}
\ No newline at end of file
+}
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 b72b79b..bd5785d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
@@ -22,12 +22,12 @@
 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;
-import static com.android.internal.telephony.TelephonyProto.PdpType.PDP_TYPE_IPV4V6;
 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 com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_IPV4V6;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -35,6 +35,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.doReturn;
 
+import android.support.test.filters.FlakyTest;
 import android.telephony.ServiceState;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Base64;
@@ -46,21 +47,21 @@
 import com.android.internal.telephony.GsmCdmaConnection;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.SmsResponse;
-import com.android.internal.telephony.TelephonyProto;
-import com.android.internal.telephony.TelephonyProto.ImsConnectionState;
-import com.android.internal.telephony.TelephonyProto.RadioAccessTechnology;
-import com.android.internal.telephony.TelephonyProto.SmsSession;
-import com.android.internal.telephony.TelephonyProto.TelephonyCallSession;
-import com.android.internal.telephony.TelephonyProto.TelephonyCallSession.Event.CallState;
-import com.android.internal.telephony.TelephonyProto.TelephonyCallSession.Event.ImsCommand;
-import com.android.internal.telephony.TelephonyProto.TelephonyCallSession.Event.RilCall;
-import com.android.internal.telephony.TelephonyProto.TelephonyEvent;
-import com.android.internal.telephony.TelephonyProto.TelephonyLog;
-import com.android.internal.telephony.TelephonyProto.TelephonyServiceState;
-import com.android.internal.telephony.TelephonyProto.TelephonyServiceState.RoamingType;
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.UUSInfo;
 import com.android.internal.telephony.dataconnection.DataCallResponse;
+import com.android.internal.telephony.nano.TelephonyProto;
+import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState;
+import com.android.internal.telephony.nano.TelephonyProto.RadioAccessTechnology;
+import com.android.internal.telephony.nano.TelephonyProto.SmsSession;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.CallState;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.ImsCommand;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.RilCall;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyLog;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyServiceState;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyServiceState.RoamingType;
 
 import org.junit.After;
 import org.junit.Before;
@@ -145,9 +146,8 @@
         assertEquals(1000, log.events.length);
         assertEquals(0, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
-        assertTrue(log.hasEventsDropped());
-        assertTrue(log.getEventsDropped());
-        assertEquals(1, log.events[0].getDataStallAction());
+        assertTrue(log.eventsDropped);
+        assertEquals(1, log.events[0].dataStallAction);
     }
 
     // Test write data stall event
@@ -160,9 +160,8 @@
         assertEquals(1, log.events.length);
         assertEquals(0, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
-        assertTrue(log.events[0].hasPhoneId());
-        assertEquals(mPhone.getPhoneId(), log.events[0].getPhoneId());
-        assertEquals(3, log.events[0].getDataStallAction());
+        assertEquals(mPhone.getPhoneId(), log.events[0].phoneId);
+        assertEquals(3, log.events[0].dataStallAction);
     }
 
     // Test write modem restart event
@@ -175,9 +174,9 @@
         assertEquals(1, log.events.length);
         assertEquals(0, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
-        assertTrue(log.events[0].hasPhoneId());
-        assertEquals(mPhone.getPhoneId(), log.events[0].getPhoneId());
-        assertEquals("Test", log.events[0].modemRestart.getReason());
+
+        assertEquals(mPhone.getPhoneId(), log.events[0].phoneId);
+        assertEquals("Test", log.events[0].modemRestart.reason);
     }
 
     // Test write on IMS call start
@@ -191,16 +190,36 @@
         assertEquals(0, log.events.length);
         assertEquals(1, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
-        assertTrue(log.callSessions[0].hasPhoneId());
-        assertEquals(mPhone.getPhoneId(), log.callSessions[0].getPhoneId());
-        assertTrue(log.callSessions[0].hasEventsDropped());
-        assertFalse(log.callSessions[0].getEventsDropped());
-        assertTrue(log.callSessions[0].hasStartTimeMinutes());
+
+        assertEquals(mPhone.getPhoneId(), log.callSessions[0].phoneId);
+
+        assertFalse(log.callSessions[0].eventsDropped);
+
         assertEquals(1, log.callSessions[0].events.length);
-        assertTrue(log.callSessions[0].events[0].hasCallIndex());
-        assertEquals(123, log.callSessions[0].events[0].getCallIndex());
-        assertTrue(log.callSessions[0].events[0].hasImsCommand());
-        assertEquals(ImsCommand.IMS_CMD_START, log.callSessions[0].events[0].getImsCommand());
+
+        assertEquals(123, log.callSessions[0].events[0].callIndex);
+
+        assertEquals(ImsCommand.IMS_CMD_START, log.callSessions[0].events[0].imsCommand);
+    }
+
+    // Test write on IMS call received
+    @Test
+    @SmallTest
+    public void testWriteOnImsCallReceive() throws Exception {
+        mMetrics.writeOnImsCallReceive(mPhone.getPhoneId(), mImsCallSession);
+        mMetrics.writePhoneState(mPhone.getPhoneId(), PhoneConstants.State.IDLE);
+        TelephonyLog log = buildProto();
+
+        assertEquals(0, log.events.length);
+        assertEquals(1, log.callSessions.length);
+        assertEquals(0, log.smsSessions.length);
+        assertEquals(mPhone.getPhoneId(), log.callSessions[0].phoneId);
+
+        assertFalse(log.callSessions[0].eventsDropped);
+
+        assertEquals(1, log.callSessions[0].events.length);
+
+        assertEquals(123, log.callSessions[0].events[0].callIndex);
     }
 
     // Test write ims call state
@@ -216,12 +235,11 @@
         assertEquals(1, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
         assertEquals(2, log.callSessions[0].events.length);
-        assertTrue(log.callSessions[0].hasEventsDropped());
-        assertFalse(log.callSessions[0].getEventsDropped());
-        assertTrue(log.callSessions[0].events[1].hasCallIndex());
-        assertEquals(123, log.callSessions[0].events[1].getCallIndex());
-        assertTrue(log.callSessions[0].events[1].hasCallState());
-        assertEquals(CallState.CALL_ACTIVE, log.callSessions[0].events[1].getCallState());
+        assertFalse(log.callSessions[0].eventsDropped);
+
+        assertEquals(123, log.callSessions[0].events[1].callIndex);
+
+        assertEquals(CallState.CALL_ACTIVE, log.callSessions[0].events[1].callState);
     }
 
     // Test write ims set feature value
@@ -231,6 +249,8 @@
         mMetrics.writeOnImsCallStart(mPhone.getPhoneId(), mImsCallSession);
         mMetrics.writeImsSetFeatureValue(mPhone.getPhoneId(),
                 ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE, 0, 1, 0);
+        mMetrics.writeImsSetFeatureValue(mPhone.getPhoneId(),
+                ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE, 0, 1, 0);
         mMetrics.writePhoneState(mPhone.getPhoneId(), PhoneConstants.State.IDLE);
         TelephonyLog log = buildProto();
 
@@ -238,10 +258,8 @@
         assertEquals(1, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
         assertEquals(2, log.callSessions[0].events.length);
-        assertTrue(log.callSessions[0].hasEventsDropped());
-        assertFalse(log.callSessions[0].getEventsDropped());
-        assertTrue(log.callSessions[0].events[1].settings.hasIsEnhanced4GLteModeEnabled());
-        assertTrue(log.callSessions[0].events[1].settings.getIsEnhanced4GLteModeEnabled());
+        assertFalse(log.callSessions[0].eventsDropped);
+        assertTrue(log.callSessions[0].events[1].settings.isEnhanced4GLteModeEnabled);
     }
 
     // Test write on ims call handover event
@@ -259,24 +277,16 @@
         assertEquals(1, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
         assertEquals(2, log.callSessions[0].events.length);
-        assertTrue(log.callSessions[0].hasEventsDropped());
-        assertFalse(log.callSessions[0].getEventsDropped());
-        assertTrue(log.callSessions[0].events[1].hasType());
+        assertFalse(log.callSessions[0].eventsDropped);
         assertEquals(TelephonyCallSession.Event.Type.IMS_CALL_HANDOVER,
-                log.callSessions[0].events[1].getType());
-        assertTrue(log.callSessions[0].events[1].hasCallIndex());
-        assertEquals(123, log.callSessions[0].events[1].getCallIndex());
-        assertTrue(log.callSessions[0].events[1].hasSrcAccessTech());
-        assertEquals(5, log.callSessions[0].events[1].getSrcAccessTech());
-        assertTrue(log.callSessions[0].events[1].hasTargetAccessTech());
-        assertEquals(6, log.callSessions[0].events[1].getTargetAccessTech());
+                log.callSessions[0].events[1].type);
+        assertEquals(123, log.callSessions[0].events[1].callIndex);
+        assertEquals(5, log.callSessions[0].events[1].srcAccessTech);
+        assertEquals(6, log.callSessions[0].events[1].targetAccessTech);
 
-        assertTrue(log.callSessions[0].events[1].reasonInfo.hasExtraMessage());
-        assertEquals("extramessage", log.callSessions[0].events[1].reasonInfo.getExtraMessage());
-        assertTrue(log.callSessions[0].events[1].reasonInfo.hasExtraCode());
-        assertEquals(456, log.callSessions[0].events[1].reasonInfo.getExtraCode());
-        assertTrue(log.callSessions[0].events[1].reasonInfo.hasReasonCode());
-        assertEquals(123, log.callSessions[0].events[1].reasonInfo.getReasonCode());
+        assertEquals("extramessage", log.callSessions[0].events[1].reasonInfo.extraMessage);
+        assertEquals(456, log.callSessions[0].events[1].reasonInfo.extraCode);
+        assertEquals(123, log.callSessions[0].events[1].reasonInfo.reasonCode);
     }
 
     // Test write on ims command
@@ -292,15 +302,15 @@
         assertEquals(1, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
         assertEquals(2, log.callSessions[0].events.length);
-        assertTrue(log.callSessions[0].hasEventsDropped());
-        assertFalse(log.callSessions[0].getEventsDropped());
-        assertTrue(log.callSessions[0].events[1].hasType());
+
+        assertFalse(log.callSessions[0].eventsDropped);
+
         assertEquals(TelephonyCallSession.Event.Type.IMS_COMMAND,
-                log.callSessions[0].events[1].getType());
-        assertTrue(log.callSessions[0].events[1].hasImsCommand());
-        assertEquals(123, log.callSessions[0].events[1].getImsCommand());
-        assertTrue(log.callSessions[0].events[1].hasCallIndex());
-        assertEquals(123, log.callSessions[0].events[1].getCallIndex());
+                log.callSessions[0].events[1].type);
+
+        assertEquals(123, log.callSessions[0].events[1].imsCommand);
+
+        assertEquals(123, log.callSessions[0].events[1].callIndex);
     }
 
     // Test write on ims connection state
@@ -310,6 +320,8 @@
         mMetrics.writeOnImsCallStart(mPhone.getPhoneId(), mImsCallSession);
         mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
                 ImsConnectionState.State.CONNECTED, mImsReasonInfo);
+        mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
+                ImsConnectionState.State.CONNECTED, mImsReasonInfo);
         mMetrics.writePhoneState(mPhone.getPhoneId(), PhoneConstants.State.IDLE);
         TelephonyLog log = buildProto();
 
@@ -317,27 +329,18 @@
         assertEquals(1, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
         assertEquals(2, log.callSessions[0].events.length);
-        assertTrue(log.hasEventsDropped());
-        assertFalse(log.getEventsDropped());
-        assertTrue(log.events[0].hasType());
-        assertEquals(TelephonyEvent.Type.IMS_CONNECTION_STATE_CHANGED, log.events[0].getType());
-        assertTrue(log.events[0].imsConnectionState.hasState());
+        assertFalse(log.eventsDropped);
+        assertEquals(TelephonyEvent.Type.IMS_CONNECTION_STATE_CHANGED, log.events[0].type);
         assertEquals(ImsConnectionState.State.CONNECTED,
-                log.events[0].imsConnectionState.getState());
-        assertTrue(log.events[0].imsConnectionState.reasonInfo.hasReasonCode());
-        assertEquals(123, log.events[0].imsConnectionState.reasonInfo.getReasonCode());
-        assertTrue(log.events[0].imsConnectionState.reasonInfo.hasExtraCode());
-        assertEquals(456, log.events[0].imsConnectionState.reasonInfo.getExtraCode());
-        assertTrue(log.events[0].imsConnectionState.reasonInfo.hasExtraMessage());
-        assertEquals("extramessage", log.events[0].imsConnectionState.reasonInfo.getExtraMessage());
-        assertTrue(log.callSessions[0].hasEventsDropped());
-        assertFalse(log.callSessions[0].getEventsDropped());
-        assertTrue(log.callSessions[0].events[1].hasType());
+                log.events[0].imsConnectionState.state);
+        assertEquals(123, log.events[0].imsConnectionState.reasonInfo.reasonCode);
+        assertEquals(456, log.events[0].imsConnectionState.reasonInfo.extraCode);
+        assertEquals("extramessage", log.events[0].imsConnectionState.reasonInfo.extraMessage);
+        assertFalse(log.callSessions[0].eventsDropped);
         assertEquals(TelephonyCallSession.Event.Type.IMS_CONNECTION_STATE_CHANGED,
-                log.callSessions[0].events[1].getType());
-        assertTrue(log.callSessions[0].events[1].imsConnectionState.hasState());
+                log.callSessions[0].events[1].type);
         assertEquals(ImsConnectionState.State.CONNECTED,
-                log.callSessions[0].events[1].imsConnectionState.getState());
+                log.callSessions[0].events[1].imsConnectionState.state);
     }
 
     // Test write on setup data call response
@@ -354,21 +357,15 @@
         assertEquals(1, log.events.length);
         assertEquals(0, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
-        assertTrue(log.hasEventsDropped());
-        assertFalse(log.getEventsDropped());
+        assertFalse(log.eventsDropped);
 
         TelephonyEvent.RilSetupDataCallResponse respProto = log.events[0].setupDataCallResponse;
 
-        assertTrue(respProto.hasStatus());
-        assertEquals(5, respProto.getStatus());
-        assertTrue(respProto.hasSuggestedRetryTimeMillis());
-        assertEquals(6, respProto.getSuggestedRetryTimeMillis());
-        assertTrue(respProto.call.hasCid());
-        assertEquals(7, respProto.call.getCid());
-        assertTrue(respProto.call.hasType());
-        assertEquals(PDP_TYPE_IPV4V6, respProto.call.getType());
-        assertTrue(respProto.call.hasIframe());
-        assertEquals(FAKE_IFNAME, respProto.call.getIframe());
+        assertEquals(5, respProto.status);
+        assertEquals(6, respProto.suggestedRetryTimeMillis);
+        assertEquals(7, respProto.call.cid);
+        assertEquals(PDP_TYPE_IPV4V6, respProto.call.type);
+        assertEquals(FAKE_IFNAME, respProto.call.iframe);
     }
 
     // Test write on deactivate data call response
@@ -382,13 +379,10 @@
         assertEquals(1, log.events.length);
         assertEquals(0, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
-        assertTrue(log.hasEventsDropped());
-        assertFalse(log.getEventsDropped());
+        assertFalse(log.eventsDropped);
 
-        assertTrue(log.events[0].hasType());
-        assertEquals(TelephonyEvent.Type.DATA_CALL_DEACTIVATE_RESPONSE, log.events[0].getType());
-        assertTrue(log.events[0].hasError());
-        assertEquals(4, log.events[0].getError());
+        assertEquals(TelephonyEvent.Type.DATA_CALL_DEACTIVATE_RESPONSE, log.events[0].type);
+        assertEquals(4, log.events[0].error);
     }
 
     // Test write RIL send SMS
@@ -410,46 +404,29 @@
         assertEquals(0, log.events.length);
         assertEquals(0, log.callSessions.length);
         assertEquals(1, log.smsSessions.length);
-        assertTrue(log.hasEventsDropped());
-        assertFalse(log.getEventsDropped());
+        assertFalse(log.eventsDropped);
 
         SmsSession.Event[] events = log.smsSessions[0].events;
         assertEquals(4, events.length);
-        assertTrue(events[0].hasType());
-        assertEquals(SmsSession.Event.Type.SMS_SEND, events[0].getType());
-        assertTrue(events[0].hasRilRequestId());
-        assertEquals(1, events[0].getRilRequestId());
-        assertTrue(events[0].hasTech());
-        assertEquals(2, events[0].getTech());
-        assertTrue(events[0].hasFormat());
-        assertEquals(1, events[0].getFormat());
+        assertEquals(SmsSession.Event.Type.SMS_SEND, events[0].type);
+        assertEquals(1, events[0].rilRequestId);
+        assertEquals(2, events[0].tech);
+        assertEquals(1, events[0].format);
 
-        assertTrue(events[1].hasType());
-        assertEquals(SmsSession.Event.Type.SMS_SEND, events[1].getType());
-        assertTrue(events[1].hasRilRequestId());
-        assertEquals(4, events[1].getRilRequestId());
-        assertTrue(events[1].hasTech());
-        assertEquals(5, events[1].getTech());
-        assertTrue(events[1].hasFormat());
-        assertEquals(2, events[1].getFormat());
+        assertEquals(SmsSession.Event.Type.SMS_SEND, events[1].type);
+        assertEquals(4, events[1].rilRequestId);
+        assertEquals(5, events[1].tech);
+        assertEquals(2, events[1].format);
 
-        assertTrue(events[2].hasType());
-        assertEquals(SmsSession.Event.Type.SMS_SEND_RESULT, events[2].getType());
-        assertTrue(events[2].hasRilRequestId());
-        assertEquals(1, events[2].getRilRequestId());
-        assertTrue(events[2].hasError());
-        assertEquals(0, events[2].getError());
-        assertTrue(events[2].hasErrorCode());
-        assertEquals(123, events[2].getErrorCode());
+        assertEquals(SmsSession.Event.Type.SMS_SEND_RESULT, events[2].type);
+        assertEquals(1, events[2].rilRequestId);
+        assertEquals(1, events[2].error);
+        assertEquals(123, events[2].errorCode);
 
-        assertTrue(events[3].hasType());
-        assertEquals(SmsSession.Event.Type.SMS_SEND_RESULT, events[3].getType());
-        assertTrue(events[3].hasRilRequestId());
-        assertEquals(4, events[3].getRilRequestId());
-        assertTrue(events[3].hasError());
-        assertEquals(0, events[3].getError());
-        assertTrue(events[3].hasErrorCode());
-        assertEquals(456, events[3].getErrorCode());
+        assertEquals(SmsSession.Event.Type.SMS_SEND_RESULT, events[3].type);
+        assertEquals(4, events[3].rilRequestId);
+        assertEquals(1, events[3].error);
+        assertEquals(456, events[3].errorCode);
     }
 
     // Test write phone state
@@ -464,18 +441,14 @@
         assertEquals(0, log.events.length);
         assertEquals(1, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
-        assertTrue(log.hasEventsDropped());
-        assertFalse(log.getEventsDropped());
+        assertFalse(log.eventsDropped);
 
-        assertTrue(log.callSessions[0].hasPhoneId());
-        assertEquals(mPhone.getPhoneId(), log.callSessions[0].getPhoneId());
+        assertEquals(mPhone.getPhoneId(), log.callSessions[0].phoneId);
         assertEquals(2, log.callSessions[0].events.length);
-        assertTrue(log.callSessions[0].events[1].hasType());
         assertEquals(TelephonyCallSession.Event.Type.PHONE_STATE_CHANGED,
-                log.callSessions[0].events[1].getType());
-        assertTrue(log.callSessions[0].events[1].hasPhoneState());
+                log.callSessions[0].events[1].type);
         assertEquals(TelephonyCallSession.Event.PhoneState.STATE_OFFHOOK,
-                log.callSessions[0].events[1].getPhoneState());
+                log.callSessions[0].events[1].phoneState);
     }
 
     // Test write RIL dial and hangup
@@ -492,29 +465,23 @@
         assertEquals(0, log.events.length);
         assertEquals(1, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
-        assertTrue(log.hasEventsDropped());
-        assertFalse(log.getEventsDropped());
+        assertFalse(log.eventsDropped);
 
         TelephonyCallSession.Event[] events = log.callSessions[0].events;
 
         assertEquals(2, events.length);
-        assertTrue(events[0].hasType());
-        assertEquals(TelephonyCallSession.Event.Type.RIL_REQUEST, events[0].getType());
-        assertTrue(events[0].hasRilRequest());
-        assertEquals(TelephonyCallSession.Event.RilRequest.RIL_REQUEST_DIAL,
-                events[0].getRilRequest());
-        RilCall[] calls = events[0].calls;
-        assertEquals(CallState.CALL_DIALING, calls[0].getState());
+        assertEquals(TelephonyCallSession.Event.Type.RIL_REQUEST, events[0].type);
 
-        assertTrue(events[1].hasType());
-        assertEquals(TelephonyCallSession.Event.Type.RIL_REQUEST, events[1].getType());
-        assertTrue(events[1].hasRilRequest());
+        assertEquals(TelephonyCallSession.Event.RilRequest.RIL_REQUEST_DIAL,
+                events[0].rilRequest);
+        RilCall[] calls = events[0].calls;
+        assertEquals(CallState.CALL_DIALING, calls[0].state);
+
         assertEquals(TelephonyCallSession.Event.RilRequest.RIL_REQUEST_HANGUP,
-                events[1].getRilRequest());
+                events[1].rilRequest);
         calls = events[1].calls;
-        assertTrue(calls[0].hasIndex());
-        assertEquals(3, calls[0].getIndex());
-        assertEquals(CallState.CALL_DISCONNECTED, calls[0].getState());
+        assertEquals(3, calls[0].index);
+        assertEquals(CallState.CALL_DISCONNECTED, calls[0].state);
     }
 
     // Test write RIL setup data call
@@ -529,21 +496,21 @@
         assertEquals(1, log.events.length);
         assertEquals(0, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
-        assertTrue(log.hasEventsDropped());
-        assertFalse(log.getEventsDropped());
 
-        assertTrue(log.events[0].hasType());
-        assertEquals(TelephonyEvent.Type.DATA_CALL_SETUP, log.events[0].getType());
+        assertFalse(log.eventsDropped);
+
+
+        assertEquals(TelephonyEvent.Type.DATA_CALL_SETUP, log.events[0].type);
 
         TelephonyEvent.RilSetupDataCall setupDataCall = log.events[0].setupDataCall;
-        assertTrue(setupDataCall.hasApn());
-        assertEquals("apn", setupDataCall.getApn());
-        assertTrue(setupDataCall.hasRat());
-        assertEquals(14, setupDataCall.getRat());
-        assertTrue(setupDataCall.hasDataProfile());
-        assertEquals(4, setupDataCall.getDataProfile());
-        assertTrue(setupDataCall.hasType());
-        assertEquals(PDP_TYPE_IPV4V6, setupDataCall.getType());
+
+        assertEquals("apn", setupDataCall.apn);
+
+        assertEquals(14, setupDataCall.rat);
+
+        assertEquals(4, setupDataCall.dataProfile);
+
+        assertEquals(PDP_TYPE_IPV4V6, setupDataCall.type);
     }
 
     // Test write service state changed
@@ -551,39 +518,40 @@
     @SmallTest
     public void testWriteServiceStateChanged() throws Exception {
         mMetrics.writeServiceStateChanged(mPhone.getPhoneId(), mServiceState);
+        mMetrics.writeServiceStateChanged(mPhone.getPhoneId(), mServiceState);
         TelephonyLog log = buildProto();
 
         assertEquals(1, log.events.length);
         assertEquals(0, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
-        assertTrue(log.hasEventsDropped());
-        assertFalse(log.getEventsDropped());
+
+        assertFalse(log.eventsDropped);
 
         TelephonyEvent event = log.events[0];
-        assertTrue(event.hasType());
-        assertEquals(TelephonyEvent.Type.RIL_SERVICE_STATE_CHANGED, event.getType());
+
+        assertEquals(TelephonyEvent.Type.RIL_SERVICE_STATE_CHANGED, event.type);
 
         TelephonyServiceState state = event.serviceState;
-        assertTrue(state.hasVoiceRat());
-        assertEquals(RadioAccessTechnology.RAT_LTE, state.getVoiceRat());
-        assertTrue(state.hasDataRat());
-        assertEquals(RadioAccessTechnology.RAT_LTE, state.getDataRat());
-        assertTrue(state.hasVoiceRoamingType());
-        assertEquals(RoamingType.ROAMING_TYPE_DOMESTIC, state.getVoiceRoamingType());
-        assertTrue(state.hasDataRoamingType());
-        assertEquals(RoamingType.ROAMING_TYPE_DOMESTIC, state.getDataRoamingType());
-        assertTrue(state.voiceOperator.hasAlphaLong());
-        assertEquals("voicelong", state.voiceOperator.getAlphaLong());
-        assertTrue(state.voiceOperator.hasAlphaShort());
-        assertEquals("voiceshort", state.voiceOperator.getAlphaShort());
-        assertTrue(state.voiceOperator.hasNumeric());
-        assertEquals("123456", state.voiceOperator.getNumeric());
-        assertTrue(state.dataOperator.hasAlphaLong());
-        assertEquals("datalong", state.dataOperator.getAlphaLong());
-        assertTrue(state.dataOperator.hasAlphaShort());
-        assertEquals("datashort", state.dataOperator.getAlphaShort());
-        assertTrue(state.dataOperator.hasNumeric());
-        assertEquals("123456", state.dataOperator.getNumeric());
+
+        assertEquals(RadioAccessTechnology.RAT_LTE, state.voiceRat);
+
+        assertEquals(RadioAccessTechnology.RAT_LTE, state.dataRat);
+
+        assertEquals(RoamingType.ROAMING_TYPE_DOMESTIC, state.voiceRoamingType);
+
+        assertEquals(RoamingType.ROAMING_TYPE_DOMESTIC, state.dataRoamingType);
+
+        assertEquals("voicelong", state.voiceOperator.alphaLong);
+
+        assertEquals("voiceshort", state.voiceOperator.alphaShort);
+
+        assertEquals("123456", state.voiceOperator.numeric);
+
+        assertEquals("datalong", state.dataOperator.alphaLong);
+
+        assertEquals("datashort", state.dataOperator.alphaShort);
+
+        assertEquals("123456", state.dataOperator.numeric);
     }
 
     // Test reset scenario
@@ -597,34 +565,34 @@
         assertEquals(1, log.events.length);
         assertEquals(0, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
-        assertTrue(log.hasEventsDropped());
-        assertFalse(log.getEventsDropped());
+
+        assertFalse(log.eventsDropped);
 
         TelephonyEvent event = log.events[0];
-        assertTrue(event.hasType());
-        assertEquals(TelephonyEvent.Type.RIL_SERVICE_STATE_CHANGED, event.getType());
+
+        assertEquals(TelephonyEvent.Type.RIL_SERVICE_STATE_CHANGED, event.type);
 
         TelephonyServiceState state = event.serviceState;
-        assertTrue(state.hasVoiceRat());
-        assertEquals(RadioAccessTechnology.RAT_LTE, state.getVoiceRat());
-        assertTrue(state.hasDataRat());
-        assertEquals(RadioAccessTechnology.RAT_LTE, state.getDataRat());
-        assertTrue(state.hasVoiceRoamingType());
-        assertEquals(RoamingType.ROAMING_TYPE_DOMESTIC, state.getVoiceRoamingType());
-        assertTrue(state.hasDataRoamingType());
-        assertEquals(RoamingType.ROAMING_TYPE_DOMESTIC, state.getDataRoamingType());
-        assertTrue(state.voiceOperator.hasAlphaLong());
-        assertEquals("voicelong", state.voiceOperator.getAlphaLong());
-        assertTrue(state.voiceOperator.hasAlphaShort());
-        assertEquals("voiceshort", state.voiceOperator.getAlphaShort());
-        assertTrue(state.voiceOperator.hasNumeric());
-        assertEquals("123456", state.voiceOperator.getNumeric());
-        assertTrue(state.dataOperator.hasAlphaLong());
-        assertEquals("datalong", state.dataOperator.getAlphaLong());
-        assertTrue(state.dataOperator.hasAlphaShort());
-        assertEquals("datashort", state.dataOperator.getAlphaShort());
-        assertTrue(state.dataOperator.hasNumeric());
-        assertEquals("123456", state.dataOperator.getNumeric());
+
+        assertEquals(RadioAccessTechnology.RAT_LTE, state.voiceRat);
+
+        assertEquals(RadioAccessTechnology.RAT_LTE, state.dataRat);
+
+        assertEquals(RoamingType.ROAMING_TYPE_DOMESTIC, state.voiceRoamingType);
+
+        assertEquals(RoamingType.ROAMING_TYPE_DOMESTIC, state.dataRoamingType);
+
+        assertEquals("voicelong", state.voiceOperator.alphaLong);
+
+        assertEquals("voiceshort", state.voiceOperator.alphaShort);
+
+        assertEquals("123456", state.voiceOperator.numeric);
+
+        assertEquals("datalong", state.dataOperator.alphaLong);
+
+        assertEquals("datashort", state.dataOperator.alphaShort);
+
+        assertEquals("123456", state.dataOperator.numeric);
     }
 
     // Test Proto Encoding/Decoding
@@ -638,4 +606,42 @@
         byte[] decodedString = Base64.decode(encodedString, Base64.DEFAULT);
         assertArrayEquals(TelephonyProto.TelephonyLog.toByteArray(log), decodedString);
     }
+
+    // Test write ims capabilities changed
+    @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};
+        // 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);
+        TelephonyLog log = buildProto();
+
+        assertEquals(2, log.events.length);
+        assertEquals(0, log.callSessions.length);
+        assertEquals(0, log.smsSessions.length);
+
+        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);
+
+        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);
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/ConnectivityServiceMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/ConnectivityServiceMock.java
index 43763ec..9210a08 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/ConnectivityServiceMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/ConnectivityServiceMock.java
@@ -574,7 +574,7 @@
     }
 
     @Override
-    public boolean isTetheringSupported() {
+    public boolean isTetheringSupported(String callerPkg) {
         throw new RuntimeException("not implemented");
     }
 
@@ -651,6 +651,11 @@
     }
 
     @Override
+    public boolean isAlwaysOnVpnPackageSupported(int userId, String packageName) {
+        throw new RuntimeException("not implemented");
+    }
+
+    @Override
     public boolean setAlwaysOnVpnPackage(int userId, String packageName, boolean lockdownEnabled) {
         throw new RuntimeException("not implemented");
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/DcTrackerMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/DcTrackerMock.java
index 6d0e327..504b4e1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/DcTrackerMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/DcTrackerMock.java
@@ -97,7 +97,7 @@
         throw new RuntimeException("Not Implemented");
     }
     @Override
-    public void setDataRoamingEnabled(boolean enabled) {
+    public void setDataRoamingEnabledByUser(boolean enabled) {
         throw new RuntimeException("Not Implemented");
     }
     @Override
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/OWNERS b/tests/telephonytests/src/com/android/internal/telephony/mocks/OWNERS
new file mode 100644
index 0000000..909c9bb
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/OWNERS
@@ -0,0 +1,13 @@
+# Re-list all owners appearing in the root OWNERS file for this project
+# This allows adding new owners for specific files using the per-file rule.
+per-file *=amitmahajan@google.com
+per-file *=breadley@google.com
+per-file *=fionaxu@google.com
+per-file *=jackyu@google.com
+per-file *=jsh@google.com
+per-file *=rgreenwalt@google.com
+per-file *=tgunn@google.com
+
+per-file ConnectivityServiceMock.java=ek@google.com
+per-file ConnectivityServiceMock.java=hugobenichi@google.com
+per-file ConnectivityServiceMock.java=lorenzo@google.com
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java
index 6cecec7..0d19f47 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java
@@ -31,6 +31,7 @@
 import android.telephony.CellInfo;
 import android.telephony.CellLocation;
 import android.telephony.DataConnectionRealTimeInfo;
+import android.telephony.NetworkScanRequest;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.VoLteServiceState;
@@ -1235,7 +1236,7 @@
         throw new RuntimeException("not implemented");
     }
 
-    public void startNetworkScan(Message response) {
+    public void startNetworkScan(NetworkScanRequest nsr, Message response) {
         throw new RuntimeException("not implemented");
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/SubscriptionControllerMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/SubscriptionControllerMock.java
index 3a89927..60995a1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/SubscriptionControllerMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/SubscriptionControllerMock.java
@@ -243,12 +243,8 @@
         throw new RuntimeException("not implemented");
     }
     @Override
-    public int[] getSubIdUsingSlotIndex(int slotIndex) {
-        return getSubId(slotIndex);
-    }
-    @Override
-    public List<SubscriptionInfo> getSubInfoUsingSlotIndexWithCheck(int slotIndex, boolean needCheck,
-            String callingPackage) {
+    public List<SubscriptionInfo> getSubInfoUsingSlotIndexWithCheck(int slotId, boolean needCheck,
+                                                                    String callingPackage) {
         throw new RuntimeException("not implemented");
     }
     @Override
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java
index e090c25..a7c3c48 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java
@@ -15,26 +15,33 @@
  */
 package com.android.internal.telephony.uicc;
 
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Message;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.android.internal.telephony.TelephonyTest;
-import com.android.internal.telephony.cat.CatService;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import static org.mockito.Mockito.*;
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.isA;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.cat.CatService;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
 
 public class UiccCardTest extends TelephonyTest {
     private UiccCard mUicccard;
@@ -69,7 +76,7 @@
         @Override
         public void onLooperPrepared() {
             mUicccard = new UiccCard(mContextFixture.getTestDouble(),
-                                     mSimulatedCommands, mIccCardStatus);
+                                     mSimulatedCommands, mIccCardStatus, 0 /* phoneId */);
             /* create a custom handler for the Handler Thread */
             mHandler = new Handler(mTestHandlerThread.getLooper()) {
                 @Override
@@ -201,9 +208,9 @@
         waitForMs(50);
 
         assertTrue(mUicccard.areCarrierPriviligeRulesLoaded());
-        verify(mSimulatedCommandsVerifier, times(1)).iccOpenLogicalChannel(isA(String.class),
+        verify(mSimulatedCommandsVerifier, times(2)).iccOpenLogicalChannel(isA(String.class),
                 anyInt(), isA(Message.class));
-        verify(mSimulatedCommandsVerifier, times(1)).iccTransmitApduLogicalChannel(
+        verify(mSimulatedCommandsVerifier, times(2)).iccTransmitApduLogicalChannel(
                 eq(mChannelId), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyString(),
                 isA(Message.class)
         );
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java
new file mode 100644
index 0000000..8bb5d8c
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java
@@ -0,0 +1,620 @@
+/*
+ * 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.uicc;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doAnswer;
+
+import android.content.pm.Signature;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+public class UiccCarrierPrivilegeRulesTest extends TelephonyTest {
+    private UiccCarrierPrivilegeRules mUiccCarrierPrivilegeRules;
+    public UiccCarrierPrivilegeRulesTest() {
+        super();
+    }
+    private UiccCarrierPrivilegeRulesHandlerThread mTestHandlerThread;
+    private Handler mHandler;
+
+    private static final int EVENT_OPEN_LOGICAL_CHANNEL_DONE = 1;
+    private static final int EVENT_TEST_DONE = 2;
+
+    @Mock
+    private UiccCard mUiccCard;
+
+    private class UiccCarrierPrivilegeRulesHandlerThread extends HandlerThread {
+
+        private UiccCarrierPrivilegeRulesHandlerThread(String name) {
+            super(name);
+        }
+
+        @Override
+        public void onLooperPrepared() {
+            /* create a custom handler for the Handler Thread */
+            mHandler = new Handler(mTestHandlerThread.getLooper()) {
+                @Override
+                public void handleMessage(Message msg) {
+                    switch (msg.what) {
+                        case EVENT_OPEN_LOGICAL_CHANNEL_DONE:
+                            /* Upon handling this event, new CarrierPrivilegeRule
+                            will be created with the looper of HandlerThread */
+                            mUiccCarrierPrivilegeRules = new UiccCarrierPrivilegeRules(
+                                    mUiccCard, mHandler.obtainMessage(EVENT_TEST_DONE));
+                            break;
+                        case EVENT_TEST_DONE:
+                            setReady(true);
+                            break;
+                        default:
+                            logd("Unknown Event " + msg.what);
+                    }
+                }
+            };
+            setReady(true);
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp(getClass().getSimpleName());
+        mTestHandlerThread = new UiccCarrierPrivilegeRulesHandlerThread(TAG);
+        mTestHandlerThread.start();
+
+        waitUntilReady();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mTestHandlerThread.quit();
+        super.tearDown();
+        mUiccCarrierPrivilegeRules = null;
+    }
+
+    private void testHelper(String hexString) {
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Message message = (Message) invocation.getArguments()[2];
+                AsyncResult ar = new AsyncResult(null, new int[]{0}, null);
+                message.obj = ar;
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
+
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Message message = (Message) invocation.getArguments()[7];
+                IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(hexString));
+                AsyncResult ar = new AsyncResult(null, iir, null);
+                message.obj = ar;
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
+                anyInt(), anyInt(), anyString(), any(Message.class));
+
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Message message = (Message) invocation.getArguments()[1];
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccCloseLogicalChannel(anyInt(), any(Message.class));
+
+        Message mCardOpenLogicalChannel = mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE);
+        setReady(false);
+        mCardOpenLogicalChannel.sendToTarget();
+        waitUntilReady();
+    }
+
+    @Test
+    @SmallTest
+    public void testParseRule_Normal() {
+        /**
+         * FF40 45
+         *   E2 43
+         *      E1 35
+         *         C1 14 ABCD92CBB156B280FA4E1429A6ECEEB6E5C1BFE4
+         *         CA 1D 636F6D2E676F6F676C652E616E64726F69642E617070732E6D79617070
+         *      E3 0A
+         *         DB 08 0000000000000001
+         */
+        final String hexString =
+                "FF4045E243E135C114ABCD92CBB156B280FA4E1429A6ECEEB6E5C1BFE4CA1D636F6D2E676F6F676"
+                        + "C652E616E64726F69642E617070732E6D79617070E30ADB080000000000000001";
+
+        testHelper(hexString);
+
+        assertTrue(mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+        assertEquals(2, mUiccCarrierPrivilegeRules.getPackageNames().size());
+        assertEquals("com.google.android.apps.myapp",
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0));
+        Signature signature = new Signature("abcd92cbb156b280fa4e1429a6eceeb6e5c1bfe4");
+        assertEquals(0, mUiccCarrierPrivilegeRules.getCarrierPrivilegeStatus(signature,
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0)));
+    }
+
+    @Test
+    @SmallTest
+    public void testParseRule_With4FD0D1() {
+        /**
+         * FF40 34
+         *   E2 32
+         *      E1 1E
+         *         4F 06 FF FF FF FF FF FF
+         *         C1 14 B6 1B E3 4A D2 C2 0D 7A FE D8 49 3C 31 3A 13 7F 89 FA 27 65
+         *      E3 10
+         *         D0 01 01
+         *         D1 01 01
+         *         DB 08 00 00 00 00 00 00 00 01
+         */
+        final String hexString = "FF4034E232E11E4F06FFFFFFFFFFFFC114B61BE34AD2C20D7AFED84"
+                + "93C313A137F89FA2765E310D00101D10101DB080000000000000001";
+
+        testHelper(hexString);
+
+        assertTrue(mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+        assertEquals(0, mUiccCarrierPrivilegeRules.getPackageNames().size());
+    }
+
+    @Test
+    @SmallTest
+    public void testParseRule_With4FD0() {
+        /**
+         * FF40 31
+         *   E2 2F
+         *      E1 1E
+         *         4F 06 FF FF FF FF FF FF
+         *         C1 14 B6 1B E3 4A D2 C2 0D 7A FE D8 49 3C 31 3A 13 7F 89 FA 27 65
+         *      E3 0D
+         *         D0 01 01
+         *         DB 08 00 00 00 00 00 00 00 01
+         */
+        final String hexString = "FF4031E22FE11E4F06FFFFFFFFFFFFC114B61BE34AD2C20D7AFED8493C313A"
+                + "137F89FA2765E30DD00101DB080000000000000001";
+
+        testHelper(hexString);
+
+        assertTrue(mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+        assertEquals(0, mUiccCarrierPrivilegeRules.getPackageNames().size());
+    }
+
+    @Test
+    @SmallTest
+    public void testParseRule_TwoMessages() {
+        /**
+         * FF40 68
+         *   E2 39
+         *      E1 2B
+         *         4F 06 FFFFFFFFFFFF
+         *         C1 02 B61B
+         *         CA 1D 636F6D2E676F6F676C652E616E64726F69642E617070732E6D79617070
+         *      E3 0A
+         *         D0 01 01
+         *         D1 01 01
+         *         DB 02 0001
+         *   E2 2B
+         *      E1 23
+         *         C1 02 ABCD
+         *         CA 1D 636F6D2E676F6F676C652E616E64726F69642E617070732E6D79617070
+         *      E3 04
+         *         DB 02 0001
+         */
+        final String hexString =
+                "FF4068E239E12B4F06FFFFFFFFFFFFC102B61BCA1D636F6D2E676F6F676C652E616E64726F69642"
+                        + "E617070732E6D79617070E30AD00101D10101DB020001E22BE123C102ABCDCA1D636F"
+                        + "6D2E676F6F676C652E616E64726F69642E617070732E6D79617070E304DB020001";
+
+        testHelper(hexString);
+
+        assertTrue(mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+        assertEquals(4, mUiccCarrierPrivilegeRules.getPackageNames().size());
+        assertEquals("com.google.android.apps.myapp",
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0));
+        Signature signature1 = new Signature("b61b");
+        assertEquals(0, mUiccCarrierPrivilegeRules.getCarrierPrivilegeStatus(signature1,
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0)));
+
+        assertEquals("com.google.android.apps.myapp",
+                mUiccCarrierPrivilegeRules.getPackageNames().get(1));
+        Signature signature2 = new Signature("abcd");
+        assertEquals(0, mUiccCarrierPrivilegeRules.getCarrierPrivilegeStatus(signature2,
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0)));
+    }
+
+    @Test
+    @SmallTest
+    public void testParseRule_InvalidRulesWith4F00() {
+        /**
+         * FF40 24
+         *   E2 22
+         *      E1 18
+         *         4F 00
+         *         C1 14 75C073AFD219AEB221948E828F066E778ADFDF23
+         *      E3 06
+         *         D0 01 01
+         *         D1 01 01
+         */
+        final String hexString = "FF4024E222E1184F00C11475C073AFD219AEB221948E828F066E778ADFDF23"
+                + "E306D00101D10101";
+
+        testHelper(hexString);
+
+        assertTrue(!mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+        assertEquals(0, mUiccCarrierPrivilegeRules.getPackageNames().size());
+    }
+
+    @Test
+    @SmallTest
+    public void testParseRule_InvalidRulesWithoutDB() {
+        /**
+         * FF40 2A
+         *   E2 28
+         *      E1 1E
+         *         4F 06 FF FF FF FF FF FF
+         *         C1 14 B6 1B E3 4A D2 C2 0D 7A FE D8 49 3C 31 3A 13 7F 89 FA 27 65
+         *      E3 06
+         *         D0 01 01
+         *         D1 01 01
+         */
+        final String hexString = "FF402AE228E11E4F06FFFFFFFFFFFFC114B61BE34AD2C20D7AFED8493C313A"
+                + "137F89FA2765E306D00101D10101";
+
+        testHelper(hexString);
+
+        assertTrue(!mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+        assertEquals(0, mUiccCarrierPrivilegeRules.getPackageNames().size());
+    }
+
+    private static final String ARAM = "A00000015141434C00";
+    private static final String ARAD = "A00000015144414300";
+    private static final String PKCS15_AID = "A000000063504B43532D3135";
+
+    @Test
+    @SmallTest
+    public void testAID_OnlyARAM() {
+        final String hexString =
+                "FF4045E243E135C114ABCD92CBB156B280FA4E1429A6ECEEB6E5C1BFE4CA1D636F6D2E676F6F676"
+                        + "C652E616E64726F69642E617070732E6D79617070E30ADB080000000000000001";
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                String aid = (String) invocation.getArguments()[0];
+                Message message = (Message) invocation.getArguments()[2];
+                if (aid.equals(ARAM)) {
+                    AsyncResult ar = new AsyncResult(null, new int[]{0}, null);
+                    message.obj = ar;
+                    message.arg2 = 1;
+                    message.sendToTarget();
+                } else {
+                    AsyncResult ar = new AsyncResult(null, null, null);
+                    message.obj = ar;
+                    message.sendToTarget();
+                }
+                return null;
+            }
+        }).when(mUiccCard).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
+
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Message message = (Message) invocation.getArguments()[7];
+                IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(hexString));
+                AsyncResult ar = new AsyncResult(null, iir, null);
+                message.obj = ar;
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
+                anyInt(), anyInt(), anyString(), any(Message.class));
+
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Message message = (Message) invocation.getArguments()[1];
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccCloseLogicalChannel(anyInt(), any(Message.class));
+
+
+        Message mCardOpenLogicalChannel = mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE);
+        setReady(false);
+        mCardOpenLogicalChannel.sendToTarget();
+        waitUntilReady();
+
+        assertTrue(mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+        assertEquals(1, mUiccCarrierPrivilegeRules.getPackageNames().size());
+        assertEquals("com.google.android.apps.myapp",
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0));
+        Signature signature = new Signature("abcd92cbb156b280fa4e1429a6eceeb6e5c1bfe4");
+        assertEquals(0, mUiccCarrierPrivilegeRules.getCarrierPrivilegeStatus(signature,
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0)));
+    }
+
+    @Test
+    @SmallTest
+    public void testAID_OnlyARAD() {
+        final String hexString =
+                "FF4045E243E135C114ABCD92CBB156B280FA4E1429A6ECEEB6E5C1BFE4CA1D636F6D2E676F6F676"
+                        + "C652E616E64726F69642E617070732E6D79617070E30ADB080000000000000001";
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                String aid = (String) invocation.getArguments()[0];
+                Message message = (Message) invocation.getArguments()[2];
+                if (aid.equals(ARAD)) {
+                    AsyncResult ar = new AsyncResult(null, new int[]{0}, null);
+                    message.obj = ar;
+                    message.arg2 = 0;
+                    message.sendToTarget();
+                } else {
+                    AsyncResult ar = new AsyncResult(null, null, null);
+                    message.obj = ar;
+                    message.arg2 = 1;
+                    message.sendToTarget();
+                }
+                return null;
+            }
+        }).when(mUiccCard).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
+
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Message message = (Message) invocation.getArguments()[7];
+                IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(hexString));
+                AsyncResult ar = new AsyncResult(null, iir, null);
+                message.obj = ar;
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
+                anyInt(), anyInt(), anyString(), any(Message.class));
+
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Message message = (Message) invocation.getArguments()[1];
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccCloseLogicalChannel(anyInt(), any(Message.class));
+
+
+        Message mCardOpenLogicalChannel = mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE);
+        setReady(false);
+        mCardOpenLogicalChannel.sendToTarget();
+        waitUntilReady();
+
+        assertTrue(mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+        assertEquals(1, mUiccCarrierPrivilegeRules.getPackageNames().size());
+        assertEquals("com.google.android.apps.myapp",
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0));
+        Signature signature = new Signature("abcd92cbb156b280fa4e1429a6eceeb6e5c1bfe4");
+        assertEquals(0, mUiccCarrierPrivilegeRules.getCarrierPrivilegeStatus(signature,
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0)));
+    }
+
+    @Test
+    @SmallTest
+    public void testAID_BothARAMandARAD() {
+        final String hexString =
+                "FF4045E243E135C114ABCD92CBB156B280FA4E1429A6ECEEB6E5C1BFE4CA1D636F6D2E676F6F676"
+                        + "C652E616E64726F69642E617070732E6D79617070E30ADB080000000000000001";
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                String aid = (String) invocation.getArguments()[0];
+                Message message = (Message) invocation.getArguments()[2];
+                AsyncResult ar = new AsyncResult(null, new int[]{0}, null);
+                message.obj = ar;
+                if (aid.equals(ARAD)) {
+                    message.arg2 = 0;
+                } else {
+                    message.arg2 = 1;
+                }
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
+
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Message message = (Message) invocation.getArguments()[7];
+                IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(hexString));
+                AsyncResult ar = new AsyncResult(null, iir, null);
+                message.obj = ar;
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
+                anyInt(), anyInt(), anyString(), any(Message.class));
+
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Message message = (Message) invocation.getArguments()[1];
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccCloseLogicalChannel(anyInt(), any(Message.class));
+
+
+        Message mCardOpenLogicalChannel = mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE);
+        setReady(false);
+        mCardOpenLogicalChannel.sendToTarget();
+        waitUntilReady();
+
+        Signature signature = new Signature("abcd92cbb156b280fa4e1429a6eceeb6e5c1bfe4");
+        assertTrue(mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+        assertEquals(2, mUiccCarrierPrivilegeRules.getPackageNames().size());
+        assertEquals("com.google.android.apps.myapp",
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0));
+        assertEquals(0, mUiccCarrierPrivilegeRules.getCarrierPrivilegeStatus(signature,
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0)));
+        assertEquals("com.google.android.apps.myapp",
+                mUiccCarrierPrivilegeRules.getPackageNames().get(1));
+        assertEquals(0, mUiccCarrierPrivilegeRules.getCarrierPrivilegeStatus(signature,
+                mUiccCarrierPrivilegeRules.getPackageNames().get(1)));
+    }
+
+    @Test
+    @SmallTest
+    public void testAID_NeitherARAMorARAD() {
+        final String hexString =
+                "FF4045E243E135C114ABCD92CBB156B280FA4E1429A6ECEEB6E5C1BFE4CA1D636F6D2E676F6F676"
+                        + "C652E616E64726F69642E617070732E6D79617070E30ADB080000000000000001";
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                String aid = (String) invocation.getArguments()[0];
+                Message message = (Message) invocation.getArguments()[2];
+                AsyncResult ar = new AsyncResult(null, null, null);
+                if (aid.equals(ARAM)) {
+                    message.arg2 = 1;
+                } else if (aid.equals(ARAD)) {
+                    message.arg2 = 0;
+                } else {
+                    // PKCS15
+                    ar = new AsyncResult(null, null, new Throwable());
+                }
+                message.obj = ar;
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
+
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Message message = (Message) invocation.getArguments()[1];
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccCloseLogicalChannel(anyInt(), any(Message.class));
+
+        Message mCardOpenLogicalChannel = mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE);
+        setReady(false);
+        mCardOpenLogicalChannel.sendToTarget();
+        waitUntilReady();
+
+        assertTrue(!mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+    }
+
+    private static final int P2 = 0x40;
+    private static final int P2_EXTENDED_DATA = 0x60;
+    @Test
+    @SmallTest
+    public void testAID_RetransmitLogicalChannel() {
+        final String hexString1 =
+                "FF4045E243E135C114ABCD92CBB156B280FA4E1429A6ECEEB6E5C1BFE4CA1D636F6D2E676F6F676"
+                        + "C652E616E64726F69642E617070732E6D79617070E30A";
+
+        final String hexString2 =
+                "DB080000000000000001";
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                String aid = (String) invocation.getArguments()[0];
+                Message message = (Message) invocation.getArguments()[2];
+                if (aid.equals(ARAD)) {
+                    AsyncResult ar = new AsyncResult(null, new int[]{0}, null);
+                    message.obj = ar;
+                    message.arg2 = 0;
+                    message.sendToTarget();
+                } else {
+                    AsyncResult ar = new AsyncResult(null, null, null);
+                    message.obj = ar;
+                    message.arg2 = 1;
+                    message.sendToTarget();
+                }
+                return null;
+            }
+        }).when(mUiccCard).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
+
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Message message = (Message) invocation.getArguments()[7];
+                IccIoResult iir = new IccIoResult(0x90, 0x00,
+                        IccUtils.hexStringToBytes(hexString1));
+                AsyncResult ar = new AsyncResult(null, iir, null);
+                message.obj = ar;
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
+                eq(P2), anyInt(), anyString(), any(Message.class));
+
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Message message = (Message) invocation.getArguments()[7];
+                IccIoResult iir = new IccIoResult(0x90, 0x00,
+                        IccUtils.hexStringToBytes(hexString2));
+                AsyncResult ar = new AsyncResult(null, iir, null);
+                message.obj = ar;
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
+                eq(P2_EXTENDED_DATA), anyInt(), anyString(), any(Message.class));
+
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Message message = (Message) invocation.getArguments()[1];
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccCloseLogicalChannel(anyInt(), any(Message.class));
+
+
+        Message mCardOpenLogicalChannel = mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE);
+        setReady(false);
+        mCardOpenLogicalChannel.sendToTarget();
+        waitUntilReady();
+
+        assertTrue(mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+        assertEquals(1, mUiccCarrierPrivilegeRules.getPackageNames().size());
+        assertEquals("com.google.android.apps.myapp",
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0));
+        Signature signature = new Signature("abcd92cbb156b280fa4e1429a6eceeb6e5c1bfe4");
+        assertEquals(0, mUiccCarrierPrivilegeRules.getCarrierPrivilegeStatus(signature,
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0)));
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
index fb48619..2f44b0a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
@@ -16,6 +16,16 @@
 
 package com.android.internal.telephony.uicc;
 
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyObject;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
@@ -35,15 +45,6 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyObject;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 public class UiccStateChangedLauncherTest extends TelephonyTest {
     private static final String TAG = UiccStateChangedLauncherTest.class.getName();
@@ -101,7 +102,7 @@
         // The first broadcast should be sent after initialization.
         UiccCard[] cards = new UiccCard[CARD_COUNT];
         cards[0] = new UiccCard(mContext, mSimulatedCommands,
-                makeCardStatus(CardState.CARDSTATE_PRESENT));
+                makeCardStatus(CardState.CARDSTATE_PRESENT), 0 /* phoneId */);
         when(UiccController.getInstance().getUiccCards()).thenReturn(cards);
         uiccLauncher.handleMessage(msg);