Merge "Sync the wfc roaming logic with Settings" into qt-dev
diff --git a/Android.mk b/Android.mk
index 775d795..8aff191 100644
--- a/Android.mk
+++ b/Android.mk
@@ -25,7 +25,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
         guava \
-        volley
+        volley \
+        android-support-annotations
 
 LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
 LOCAL_SRC_FILES += $(call all-proto-files-under, ecc/proto)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index eca7648..938d714 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -469,6 +469,7 @@
             privileged processes can start them.
         -->
         <activity android:name=".euicc.EuiccPrivilegedActionUiDispatcherActivity"
+                  android:theme="@android:style/Theme.NoDisplay"
                   android:permission="android.permission.CALL_PRIVILEGED">
             <!-- Max out priority to ensure nobody else will handle these intents. -->
             <intent-filter android:priority="1000">
@@ -606,6 +607,14 @@
             android:name="com.android.internal.telephony.uicc.ShowInstallAppNotificationReceiver"
             android:exported="false"/>
 
+        <activity
+            android:name="com.android.phone.settings.PickSmsSubscriptionActivity"
+            android:exported="false"
+            android:excludeFromRecents="true"
+            android:launchMode="singleTop"
+            android:configChanges="orientation|screenSize|keyboardHidden"
+            android:theme="@style/Theme.Transparent"/>
+
         <service
             android:name="com.android.phone.vvm.RemoteVvmTaskManager"
             android:exported="false"/>
diff --git a/ecc/input/eccdata.txt b/ecc/input/eccdata.txt
index 403cad7..bf5d92c 100644
--- a/ecc/input/eccdata.txt
+++ b/ecc/input/eccdata.txt
@@ -600,8 +600,11 @@
 countries {
   iso_code: "DE"
   eccs {
-    phone_number: "112"
+    phone_number: "110"
     types: POLICE
+  }
+  eccs {
+    phone_number: "112"
     types: AMBULANCE
     types: FIRE
   }
diff --git a/ecc/output/eccdata b/ecc/output/eccdata
index 679bc36..2b9007a 100644
--- a/ecc/output/eccdata
+++ b/ecc/output/eccdata
Binary files differ
diff --git a/res/layout/emergency_information.xml b/res/layout/emergency_information.xml
index e925479..524387f 100644
--- a/res/layout/emergency_information.xml
+++ b/res/layout/emergency_information.xml
@@ -53,7 +53,7 @@
                     android:maxLines="1"
                     android:ellipsize="end"
                     android:lineHeight="@dimen/emergency_info_name_line_height"
-                    android:fontFamily="google-sans"
+                    android:fontFamily="@*android:string/config_headlineFontFamily"
                     android:textAppearance="@style/HeadlineTextAppearance"/>
                 <TextView
                     android:id="@+id/emergency_info_hint"
@@ -99,7 +99,7 @@
                 android:maxLines="2"
                 android:ellipsize="end"
                 android:lineHeight="@dimen/confirmed_emergency_info_line_height"
-                android:fontFamily="google-sans"
+                android:fontFamily="@*android:string/config_headlineFontFamily"
                 android:textAppearance="@style/PhoneCallHintTextAppearance"
                 android:text="@string/emergency_information_confirm_hint"/>
         </LinearLayout>
diff --git a/res/layout/emergency_shortcut_button.xml b/res/layout/emergency_shortcut_button.xml
index 2828926..3c5eaa2 100644
--- a/res/layout/emergency_shortcut_button.xml
+++ b/res/layout/emergency_shortcut_button.xml
@@ -57,7 +57,7 @@
                     android:maxLines="1"
                     android:ellipsize="end"
                     android:lineHeight="@dimen/phone_number_line_height"
-                    android:fontFamily="google-sans"
+                    android:fontFamily="@*android:string/config_headlineFontFamily"
                     android:textAppearance="@style/PhoneNumberTextAppearance"/>
                 <TextView
                     android:id="@+id/phone_number_description"
@@ -124,7 +124,7 @@
                     android:maxLines="2"
                     android:ellipsize="end"
                     android:lineHeight="@dimen/phone_call_hint_line_height"
-                    android:fontFamily="google-sans"
+                    android:fontFamily="@*android:string/config_headlineFontFamily"
                     android:textAppearance="@style/PhoneCallHintTextAppearance"/>
             </FrameLayout>
         </LinearLayout>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index dbc0af4..39c6d75 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -622,7 +622,7 @@
     <string name="preference_category_ringtone" msgid="5197960752529332721">"रिंगटोन और कंपन"</string>
     <string name="pstn_connection_service_label" msgid="1743245930577325900">"अंतर्निहित सिम कार्ड"</string>
     <string name="enable_video_calling_title" msgid="7237253660669000899">"वीडियो कॉलिंग चालू करें"</string>
-    <string name="enable_video_calling_dialog_msg" msgid="8948186136957417948">"वीडियो कॉलिंग चालू करने के लिए, आपको नेटवर्क सेटिंग में उन्‍नत 4G LTE मोड सक्षम करना होगा."</string>
+    <string name="enable_video_calling_dialog_msg" msgid="8948186136957417948">"वीडियो कॉलिंग चालू करने के लिए, आपको नेटवर्क सेटिंग में बेहतर 4G LTE मोड सक्षम करना होगा."</string>
     <string name="enable_video_calling_dialog_settings" msgid="576528473599603249">"नेटवर्क सेटिंग"</string>
     <string name="enable_video_calling_dialog_close" msgid="7411471282167927991">"बंद करें"</string>
     <string name="sim_label_emergency_calls" msgid="4847699229529306397">"आपातकालीन कॉल"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 020f3da..5400ad4 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -592,12 +592,12 @@
     <string name="phone_in_ecm_call_notification_text" msgid="4611608947314729773">"डेटा कनेक्शन अक्षम केले"</string>
     <string name="phone_in_ecm_notification_complete_time" msgid="7730376844178948351">"<xliff:g id="COMPLETETIME">%s</xliff:g> पर्यंत कोणतेही डेटा कनेक्‍शन नाही"</string>
     <plurals name="alert_dialog_exit_ecm" formatted="false" msgid="7179911675595441201">
-      <item quantity="one">फोन <xliff:g id="COUNT_1">%s</xliff:g> मिनिटासाठी आणीबाणी कॉलबॅक मोडमध्ये राहील. या मोडमध्ये असताना डेटा कनेक्शन वापरणारे कोणतेही अ‍ॅप्लिकेशन वापरले जाऊ शकत नाहीत. तुम्ही आता बाहेर पडू इच्छिता?</item>
       <item quantity="other">फोन <xliff:g id="COUNT_1">%s</xliff:g> मिनिटांसाठी आणीबाणी कॉलबॅक मोडमध्ये राहील. या मोडमध्ये असताना डेटा कनेक्शन वापरणारे कोणतेही अ‍ॅप्लिकेशन वापरले जाऊ शकत नाहीत. तुम्ही आता बाहेर पडू इच्छिता?</item>
+      <item quantity="one">फोन <xliff:g id="COUNT_0">%s</xliff:g> मिनिटासाठी आणीबाणी कॉलबॅक मोडमध्ये राहील. या मोडमध्ये असताना डेटा कनेक्शन वापरणारे कोणतेही अ‍ॅप वापरले जाऊ शकत नाहीत. तुम्ही आता बाहेर पडू इच्छिता?</item>
     </plurals>
     <plurals name="alert_dialog_not_avaialble_in_ecm" formatted="false" msgid="8042973425225093895">
-      <item quantity="one">आणीबाणी कॉलबॅक मोडमध्ये असताना निवडलेली क्रिया उपलब्ध नसते. फोन <xliff:g id="COUNT_1">%s</xliff:g> मिनिटासाठी या मोडमध्ये राहील. तुम्ही आता बाहेर पडू इच्छिता?</item>
       <item quantity="other">आणीबाणी कॉलबॅक मोडमध्ये असताना निवडलेली क्रिया उपलब्ध नसते. फोन <xliff:g id="COUNT_1">%s</xliff:g> मिनिटांसाठी या मोडमध्ये राहील. तुम्ही आता बाहेर पडू इच्छिता?</item>
+      <item quantity="one">आणीबाणी कॉलबॅक मोडमध्ये असताना निवडलेली क्रिया उपलब्ध नसते. फोन <xliff:g id="COUNT_0">%s</xliff:g> मिनिटासाठी या मोडमध्ये राहील. तुम्ही आता बाहेर पडू इच्छिता?</item>
     </plurals>
     <string name="alert_dialog_in_ecm_call" msgid="1886723687211887104">"आणीबाणीच्‍या कॉलमध्‍ये असताना निवडलेली क्रिया उपलब्‍ध नसते."</string>
     <string name="progress_dialog_exiting_ecm" msgid="4835734101617817074">"आणीबाणी कॉलबॅक मोडमधून बाहेर पडत आहे"</string>
@@ -762,8 +762,8 @@
     <string name="supp_service_outgoing_calls_barred" msgid="1962644621292054081">"जाणारे कॉल अवरोधित केले."</string>
     <string name="supp_service_call_forwarding_active" msgid="1253134771682248735">"कॉल फॉरवर्ड होत आहे."</string>
     <string name="supp_service_additional_call_forwarded" msgid="5228624725214727315">"अतिरिक्‍त कॉल फॉरवर्ड केला."</string>
-    <string name="supp_service_additional_ect_connected" msgid="6396964292513707102">"भडक कॉल ट्रांसफर करणे पूर्ण झाले."</string>
-    <string name="supp_service_additional_ect_connecting" msgid="5443373059716058480">"भडक कॉल ट्रांसफर होत आहे."</string>
+    <string name="supp_service_additional_ect_connected" msgid="6396964292513707102">"भडक कॉल ट्रान्सफर करणे पूर्ण झाले."</string>
+    <string name="supp_service_additional_ect_connecting" msgid="5443373059716058480">"भडक कॉल ट्रान्सफर होत आहे."</string>
     <string name="supp_service_call_on_hold" msgid="1478976782361795422">"कॉल होल्डवर आहे."</string>
     <string name="supp_service_call_resumed" msgid="1656475289958070674">"कॉल पुन्‍‍‍‍हा सुरू झाला."</string>
     <string name="supp_service_deflected_call" msgid="138970419873492166">"कॉल वळवला."</string>
diff --git a/res/xml/carrier_ss_string.xml b/res/xml/carrier_ss_string.xml
index 96bcfa4..826f0ea 100644
--- a/res/xml/carrier_ss_string.xml
+++ b/res/xml/carrier_ss_string.xml
@@ -74,10 +74,10 @@
 <resources>
     <!-- This is a parser format by Regular Expression.
          "response_format"'s position follow below index.
-         ((\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*))?)?)?)?)?)?)?#)
-         1 2        3   4       5   6       7   8       9   10      11  12      13  14      15  16
+         ((\d{2,3})(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*))?)?)?)?)?)?)?#)
+         1 2       3  4       5  6       7  8       9  10      11 12     13  14      15 16
     -->
-    <regular_parser>((\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*))?)?)?)?)?)?)?#)</regular_parser>
+    <regular_parser>((\d{2,3})(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*))?)?)?)?)?)?)?#)</regular_parser>
     <feature name="callforwarding">
         <command name="query"><!--For example: *#120*1#-->
             <service_code>120</service_code>
diff --git a/res/xml/carrier_ss_string_850.xml b/res/xml/carrier_ss_string_850.xml
index 53611d4..86d5743 100644
--- a/res/xml/carrier_ss_string_850.xml
+++ b/res/xml/carrier_ss_string_850.xml
@@ -22,10 +22,10 @@
 <resources>
     <!-- This is a parser format by Regular Expression.
          "response_format"'s position follow below index.
-         ((\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*))?)?)?)?)?)?)?#)
-         1 2        3   4       5   6       7   8       9   10      11  12      13  14      15  16
+         ((\d{2,3})(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*))?)?)?)?)?)?)?#)
+         1 2       3  4       5  6       7  8       9  10      11 12     13  14      15 16
     -->
-    <regular_parser>((\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*))?)?)?)?)?)?)?#)</regular_parser>
+    <regular_parser>((\d{2,3})(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*))?)?)?)?)?)?)?#)</regular_parser>
     <feature name="callforwarding">
         <command name="query"><!--For example: *#120*1#-->
             <service_code>120</service_code>
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index f1db076..f2438d9 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -407,7 +407,8 @@
             prefSet.removePreference(mEnableVideoCalling);
         }
 
-        final PhoneAccountHandle simCallManager = mTelecomManager.getSimCallManager();
+        final PhoneAccountHandle simCallManager = mTelecomManager.getSimCallManagerForSubscription(
+                mPhone.getSubId());
         if (simCallManager != null) {
             Intent intent = PhoneAccountSettingsFragment.buildPhoneAccountConfigureIntent(
                     this, simCallManager);
diff --git a/src/com/android/phone/CarrierXmlParser.java b/src/com/android/phone/CarrierXmlParser.java
index 4cf7e35..18602c9 100644
--- a/src/com/android/phone/CarrierXmlParser.java
+++ b/src/com/android/phone/CarrierXmlParser.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.res.XmlResourceParser;
+import android.support.annotation.VisibleForTesting;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
@@ -95,7 +96,9 @@
     private static final String ATTR_DEFINITION_KEY = "definition";
 
     HashMap<String, SsFeature> mFeatureMaps;
-    private static String sParserFormat = "";
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    static String sParserFormat = "";
 
     // TAG_ENTRY_NUMBER and TAG_ENTRY_TIME is xml's entry value.
     // This is mapping user's input value. For example: number,time ...
@@ -155,6 +158,8 @@
                         mParserStr.add("");
                     }
                 }
+            } else {
+                Log.d(LOG_TAG, "no match");
             }
         }
 
diff --git a/src/com/android/phone/MobileNetworkSettings.java b/src/com/android/phone/MobileNetworkSettings.java
index c123e66..e67758d 100644
--- a/src/com/android/phone/MobileNetworkSettings.java
+++ b/src/com/android/phone/MobileNetworkSettings.java
@@ -149,12 +149,17 @@
 
 
     /**
-     * Returns true if Wifi calling is enabled for at least one phone.
+     * Returns true if Wifi calling is enabled for at least one subscription.
      */
     public static boolean isWifiCallingEnabled(Context context) {
-        int phoneCount = TelephonyManager.from(context).getPhoneCount();
-        for (int i = 0; i < phoneCount; i++) {
-            if (isWifiCallingEnabled(context, i)) {
+        SubscriptionManager subManager = context.getSystemService(SubscriptionManager.class);
+        if (subManager == null) {
+            Log.e(MobileNetworkFragment.LOG_TAG,
+                    "isWifiCallingEnabled: couldn't get system service.");
+            return false;
+        }
+        for (int subId : subManager.getActiveSubscriptionIdList()) {
+            if (isWifiCallingEnabled(context, subId)) {
                 return true;
             }
         }
@@ -162,11 +167,12 @@
     }
 
     /**
-     * Returns true if Wifi calling is enabled for the specific phone with id {@code phoneId}.
+     * Returns true if Wifi calling is enabled for the specific subscription with id {@code subId}.
      */
-    public static boolean isWifiCallingEnabled(Context context, int phoneId) {
+    public static boolean isWifiCallingEnabled(Context context, int subId) {
         final PhoneAccountHandle simCallManager =
-                TelecomManager.from(context).getSimCallManager();
+                TelecomManager.from(context).getSimCallManagerForSubscription(subId);
+        final int phoneId = SubscriptionManager.getSlotIndex(subId);
 
         boolean isWifiCallingEnabled;
         if (simCallManager != null) {
@@ -1890,14 +1896,14 @@
             }
 
             // Removes the preference if the wifi calling is disabled.
-            if (!isWifiCallingEnabled(getContext(), SubscriptionManager.getPhoneId(mSubId))) {
+            if (!isWifiCallingEnabled(getContext(), mSubId)) {
                 mCallingCategory.removePreference(mWiFiCallingPref);
                 return;
             }
 
             // See what Telecom thinks the SIM call manager is.
             final PhoneAccountHandle simCallManager =
-                    TelecomManager.from(getContext()).getSimCallManager();
+                    TelecomManager.from(getContext()).getSimCallManagerForSubscription(mSubId);
 
             // Check which SIM call manager is for the current sub ID.
             PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId);
diff --git a/src/com/android/phone/NotificationMgr.java b/src/com/android/phone/NotificationMgr.java
index de3fffc..942c1e2 100644
--- a/src/com/android/phone/NotificationMgr.java
+++ b/src/com/android/phone/NotificationMgr.java
@@ -569,9 +569,8 @@
         if (DBG) log("showDataDisconnectedRoaming()...");
 
         // "Mobile network settings" screen / dialog
-        Intent intent = new Intent(mContext, com.android.phone.MobileNetworkSettings.class);
+        Intent intent = new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS);
         intent.putExtra(Settings.EXTRA_SUB_ID, subId);
-        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
         PendingIntent contentIntent = PendingIntent.getActivity(mContext, subId, intent, 0);
 
         final CharSequence contentText = mContext.getText(R.string.roaming_reenable_message);
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index df589fd..97f62e8 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -79,7 +79,6 @@
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
-import android.telephony.SmsManager;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyHistogram;
@@ -91,6 +90,7 @@
 import android.telephony.VisualVoicemailSmsFilterSettings;
 import android.telephony.cdma.CdmaCellLocation;
 import android.telephony.data.ApnSetting;
+import android.telephony.data.ApnSetting.ApnType;
 import android.telephony.emergency.EmergencyNumber;
 import android.telephony.gsm.GsmCellLocation;
 import android.telephony.ims.ProvisioningManager;
@@ -121,6 +121,7 @@
 import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.DefaultPhoneNotifier;
 import com.android.internal.telephony.HalVersion;
+import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.INumberVerificationCallback;
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.IccCard;
@@ -139,6 +140,8 @@
 import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.SmsApplication;
 import com.android.internal.telephony.SmsApplication.SmsApplicationData;
+import com.android.internal.telephony.SmsController;
+import com.android.internal.telephony.SmsPermissions;
 import com.android.internal.telephony.SubscriptionController;
 import com.android.internal.telephony.TelephonyPermissions;
 import com.android.internal.telephony.dataconnection.ApnSettingUtils;
@@ -156,6 +159,7 @@
 import com.android.internal.telephony.uicc.UiccSlot;
 import com.android.internal.telephony.util.VoicemailNotificationSettingsUtil;
 import com.android.internal.util.HexDump;
+import com.android.phone.settings.PickSmsSubscriptionActivity;
 import com.android.phone.vvm.PhoneAccountHandleConverter;
 import com.android.phone.vvm.RemoteVvmTaskManager;
 import com.android.phone.vvm.VisualVoicemailSettingsUtil;
@@ -163,7 +167,6 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -172,6 +175,7 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.NoSuchElementException;
 import java.util.Set;
 
 /**
@@ -248,6 +252,8 @@
     private static final int EVENT_REQUEST_CELL_INFO_UPDATE_DONE = 67;
     private static final int CMD_REQUEST_ENABLE_MODEM = 68;
     private static final int EVENT_ENABLE_MODEM_DONE = 69;
+    private static final int CMD_GET_MODEM_STATUS = 70;
+    private static final int EVENT_GET_MODEM_STATUS_DONE = 71;
 
     // Parameters of select command.
     private static final int SELECT_COMMAND = 0xA4;
@@ -1117,10 +1123,42 @@
                     ar = (AsyncResult) msg.obj;
                     request = (MainThreadRequest) ar.userObj;
                     request.result = (ar.exception == null);
+                    int phoneId = request.phone.getPhoneId();
                     //update the cache as modem status has changed
-                    mPhoneConfigurationManager.addToPhoneStatusCache(
-                            request.phone.getPhoneId(), msg.arg1 == 1);
-                    updateModemStateMetrics();
+                    if ((boolean) request.result) {
+                        mPhoneConfigurationManager.addToPhoneStatusCache(phoneId, msg.arg1 == 1);
+                        updateModemStateMetrics();
+                    } else {
+                        Log.e(LOG_TAG, msg.what + " failure. Not updating modem status."
+                                + ar.exception);
+                    }
+                    notifyRequester(request);
+                    break;
+                case CMD_GET_MODEM_STATUS:
+                    request = (MainThreadRequest) msg.obj;
+                    onCompleted = obtainMessage(EVENT_GET_MODEM_STATUS_DONE, request);
+                    PhoneConfigurationManager.getInstance()
+                            .getPhoneStatusFromModem(request.phone, onCompleted);
+                    break;
+                case EVENT_GET_MODEM_STATUS_DONE:
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    int id = request.phone.getPhoneId();
+                    if (ar.exception == null && ar.result != null) {
+                        request.result = ar.result;
+                        //update the cache as modem status has changed
+                        mPhoneConfigurationManager.addToPhoneStatusCache(id,
+                                (boolean) request.result);
+                    } else {
+                        // Return true if modem status cannot be retrieved. For most cases,
+                        // modem status is on. And for older version modems, GET_MODEM_STATUS
+                        // and disable modem are not supported. Modem is always on.
+                        // TODO: this should be fixed in R to support a third
+                        // status UNKNOWN b/131631629
+                        request.result = true;
+                        Log.e(LOG_TAG, msg.what + " failure. Not updating modem status."
+                                + ar.exception);
+                    }
                     notifyRequester(request);
                     break;
                 default:
@@ -2101,10 +2139,16 @@
             case DENIED_HARD:
                 throw new SecurityException("Not allowed to access cell info");
             case DENIED_SOFT:
+                try {
+                    cb.onCellInfo(new ArrayList<CellInfo>());
+                } catch (RemoteException re) {
+                    // Drop without consequences
+                }
                 return;
         }
 
-        final Phone phone = getPhone(subId);
+
+        final Phone phone = getPhoneFromSubId(subId);
         if (phone == null) throw new IllegalArgumentException("Invalid Subscription Id: " + subId);
 
         sendRequestAsync(CMD_REQUEST_CELL_INFO_UPDATE, cb, phone, workSource);
@@ -2613,22 +2657,11 @@
         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
         enforceVisualVoicemailPackage(callingPackage, subId);
         enforceSendSmsPermission();
-        // Make the calls as the phone process.
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            SmsManager smsManager = SmsManager.getSmsManagerForSubscriptionId(subId);
-            if (port == 0) {
-                smsManager.sendTextMessageWithSelfPermissions(number, null, text,
-                        sentIntent, null, false);
-            } else {
-                byte[] data = text.getBytes(StandardCharsets.UTF_8);
-                smsManager.sendDataMessageWithSelfPermissions(number, null,
-                        (short) port, data, sentIntent, null);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
+        SmsController smsController = PhoneFactory.getSmsController();
+        smsController.sendVisualVoicemailSmsForSubscriber(callingPackage, subId, number, port, text,
+                sentIntent);
     }
+
     /**
      * Sets the voice activation state of a given subId.
      */
@@ -3032,10 +3065,8 @@
         final long identity = Binder.clearCallingIdentity();
         try {
             // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
-            boolean isRoaming = TelephonyManager.from(
-                    getPhone(subId).getContext()).isNetworkRoaming(subId);
             ImsManager.getInstance(mApp,
-                    getSlotIndexOrException(subId)).setWfcNonPersistent(isCapable, mode, isRoaming);
+                    getSlotIndexOrException(subId)).setWfcNonPersistent(isCapable, mode);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -3813,7 +3844,7 @@
             int command, int p1, int p2, int p3, String data) {
         final long identity = Binder.clearCallingIdentity();
         try {
-            if (channel < 0) {
+            if (channel <= 0) {
                 return "";
             }
 
@@ -4750,17 +4781,19 @@
     }
 
     @Override
-    public int checkCarrierPrivilegesForPackage(String pkgName) {
-        final Phone defaultPhone = getDefaultPhone();
-        if (TextUtils.isEmpty(pkgName))
+    public int checkCarrierPrivilegesForPackage(int subId, String pkgName) {
+        if (TextUtils.isEmpty(pkgName)) {
             return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
-        UiccCard card = UiccController.getInstance().getUiccCard(defaultPhone.getPhoneId());
+        }
+
+        int phoneId = SubscriptionManager.getPhoneId(subId);
+        UiccCard card = UiccController.getInstance().getUiccCard(phoneId);
         if (card == null) {
-            loge("checkCarrierPrivilegesForPackage: No UICC");
+            loge("checkCarrierPrivilegesForPackage: No UICC on subId " + subId);
             return TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
         }
-        return card.getCarrierPrivilegeStatus(defaultPhone.getContext().getPackageManager(),
-                pkgName);
+
+        return card.getCarrierPrivilegeStatus(mApp.getPackageManager(), pkgName);
     }
 
     @Override
@@ -4799,29 +4832,26 @@
     }
 
     @Override
-    public List<String> getPackagesWithCarrierPrivileges() {
+    public List<String> getPackagesWithCarrierPrivileges(int phoneId) {
         PackageManager pm = mApp.getPackageManager();
         List<String> privilegedPackages = new ArrayList<>();
         List<PackageInfo> packages = null;
-        for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) {
-            UiccCard card = UiccController.getInstance().getUiccCard(i);
-            if (card == null) {
-                // No UICC in that slot.
-                continue;
-            }
+        UiccCard card = UiccController.getInstance().getUiccCard(phoneId);
+        // has UICC in that slot.
+        if (card != null) {
             if (card.hasCarrierPrivilegeRules()) {
                 if (packages == null) {
                     // Only check packages in user 0 for now
                     packages = pm.getInstalledPackagesAsUser(
                             PackageManager.MATCH_DISABLED_COMPONENTS
-                            | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
-                            | PackageManager.GET_SIGNATURES, UserHandle.USER_SYSTEM);
+                                    | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                                    | PackageManager.GET_SIGNATURES, UserHandle.USER_SYSTEM);
                 }
                 for (int p = packages.size() - 1; p >= 0; p--) {
                     PackageInfo pkgInfo = packages.get(p);
                     if (pkgInfo != null && pkgInfo.packageName != null
                             && card.getCarrierPrivilegeStatus(pkgInfo)
-                                == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+                            == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
                         privilegedPackages.add(pkgInfo.packageName);
                     }
                 }
@@ -4830,6 +4860,15 @@
         return privilegedPackages;
     }
 
+    @Override
+    public List<String> getPackagesWithCarrierPrivilegesForAllPhones() {
+        List<String> privilegedPackages = new ArrayList<>();
+        for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) {
+           privilegedPackages.addAll(getPackagesWithCarrierPrivileges(i));
+        }
+        return privilegedPackages;
+    }
+
     private String getIccId(int subId) {
         final Phone phone = getPhone(subId);
         UiccCard card = phone == null ? null : phone.getUiccCard();
@@ -4945,7 +4984,7 @@
     }
 
     @Override
-    public String[] getMergedSubscriberIds(String callingPackage) {
+    public String[] getMergedSubscriberIds(int subId, String callingPackage) {
         // This API isn't public, so no need to provide a valid subscription ID - we're not worried
         // about carrier-privileged callers not having access.
         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
@@ -4954,6 +4993,9 @@
             return null;
         }
 
+        // Clear calling identity, when calling TelephonyManager, because callerUid must be
+        // the process, where TelephonyManager was instantiated.
+        // Otherwise AppOps check will fail.
         final long identity  = Binder.clearCallingIdentity();
         try {
             final Context context = mApp;
@@ -4962,12 +5004,10 @@
 
             // Figure out what subscribers are currently active
             final ArraySet<String> activeSubscriberIds = new ArraySet<>();
-            // Clear calling identity, when calling TelephonyManager, because callerUid must be
-            // the process, where TelephonyManager was instantiated.
-            // Otherwise AppOps check will fail.
 
-            final int[] subIds = sub.getActiveSubscriptionIdList();
-            for (int subId : subIds) {
+            // Only consider subs which match the current subId
+            // This logic can be simplified. See b/131189269 for progress.
+            if (isActiveSubscription(subId)) {
                 activeSubscriberIds.add(tele.getSubscriberId(subId));
             }
 
@@ -6365,11 +6405,11 @@
                     continue;
                 }
 
-                String cardId = null;
+                String cardId;
                 UiccCard card = slot.getUiccCard();
                 if (card != null) {
                     cardId = card.getCardId();
-                } else if (!slot.isEuicc()) {
+                } else {
                     cardId = slot.getIccId();
                 }
 
@@ -6821,7 +6861,11 @@
 
         final long identity = Binder.clearCallingIdentity();
         try {
-            return PhoneConfigurationManager.getInstance().getPhoneStatus(phone);
+            try {
+                return mPhoneConfigurationManager.getPhoneStatusFromCache(phone.getPhoneId());
+            } catch (NoSuchElementException ex) {
+                return (Boolean) sendRequest(CMD_GET_MODEM_STATUS, null, phone, null);
+            }
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -6998,8 +7042,7 @@
             Phone phone = getPhone(subId);
             if (phone == null) return false;
 
-            boolean isMetered = ApnSettingUtils.isMeteredApnType(ApnSetting.getApnTypeString(
-                    apnType), phone);
+            boolean isMetered = ApnSettingUtils.isMeteredApnType(apnType, phone);
             return !isMetered || phone.getDataEnabledSettings().isDataEnabled(apnType);
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -7007,7 +7050,7 @@
     }
 
     @Override
-    public boolean isApnMetered(int apnType, int subId) {
+    public boolean isApnMetered(@ApnType int apnType, int subId) {
         enforceReadPrivilegedPermission("isApnMetered");
 
         // Now that all security checks passes, perform the operation as ourselves.
@@ -7016,10 +7059,26 @@
             Phone phone = getPhone(subId);
             if (phone == null) return true; // By default return true.
 
-            return ApnSettingUtils.isMeteredApnType(ApnSetting.getApnTypeString(
-                    apnType), phone);
+            return ApnSettingUtils.isMeteredApnType(apnType, phone);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
     }
+
+    @Override
+    public void enqueueSmsPickResult(String callingPackage, IIntegerConsumer pendingSubIdResult) {
+        SmsPermissions permissions = new SmsPermissions(getDefaultPhone(), mApp,
+                (AppOpsManager) mApp.getSystemService(Context.APP_OPS_SERVICE));
+        if (!permissions.checkCallingCanSendSms(callingPackage, "Sending message")) {
+            throw new SecurityException("Requires SEND_SMS permission to perform this operation");
+        }
+        PickSmsSubscriptionActivity.addPendingResult(pendingSubIdResult);
+        Intent intent = new Intent();
+        intent.setClass(mApp, PickSmsSubscriptionActivity.class);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        // Bring up choose default SMS subscription dialog right now
+        intent.putExtra(PickSmsSubscriptionActivity.DIALOG_TYPE_KEY,
+                PickSmsSubscriptionActivity.SMS_PICK_FOR_MESSAGE);
+        mApp.startActivity(intent);
+    }
 }
diff --git a/src/com/android/phone/settings/PickSmsSubscriptionActivity.java b/src/com/android/phone/settings/PickSmsSubscriptionActivity.java
new file mode 100644
index 0000000..cfbce28
--- /dev/null
+++ b/src/com/android/phone/settings/PickSmsSubscriptionActivity.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2019 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.phone.settings;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.telephony.SubscriptionManager;
+import android.util.Log;
+
+import com.android.internal.telephony.IIntegerConsumer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Trampolines a request to Settings to get the SMS subscription associated with an SmsManager
+ * operation.
+ *
+ * Since a Service can not start an Activity with
+ * {@link Activity#startActivityForResult(Intent, int)} and get a response (only Activities can
+ * handle the results), we have to "Trampoline" this operation by creating an empty Activity whose
+ * only job is to call startActivityForResult with the correct Intent and handle the result.
+ */
+// TODO: SmsManager should be constructed with an activity context so it can start as part of its
+// task and fall back to PickSmsSubscriptionActivity being called in PhoneInterfaceManager if not
+// called from an activity context.
+public class PickSmsSubscriptionActivity extends Activity {
+
+    private static final String LOG_TAG = "PickSmsSubActivity";
+
+    // Defined in Settings SimDialogActivity
+    private static final String RESULT_SUB_ID = "result_sub_id";
+    public static final String DIALOG_TYPE_KEY = "dialog_type";
+    public static final int SMS_PICK_FOR_MESSAGE = 4;
+
+    private static final ComponentName SETTINGS_SUB_PICK_ACTIVITY = new ComponentName(
+            "com.android.settings", "com.android.settings.sim.SimDialogActivity");
+
+    private static final List<IIntegerConsumer> sSmsPickPendingList = new ArrayList<>();
+
+    private static final int REQUEST_GET_SMS_SUB_ID = 1;
+
+    /**
+     * Adds a consumer to the list of pending results that will be accepted once the activity
+     * completes.
+     */
+    public static void addPendingResult(IIntegerConsumer consumer) {
+        synchronized (sSmsPickPendingList) {
+            sSmsPickPendingList.add(consumer);
+        }
+        Log.i(LOG_TAG, "queue pending result, token: " + consumer);
+    }
+
+    private static void sendResultAndClear(int resultId) {
+        // If the calling process died, just ignore callback.
+        synchronized (sSmsPickPendingList) {
+            for (IIntegerConsumer c : sSmsPickPendingList) {
+                try {
+                    c.accept(resultId);
+                    Log.i(LOG_TAG, "Result received, token: " + c + ", result: " + resultId);
+                } catch (RemoteException e) {
+                    // The calling process died, skip this one.
+                }
+            }
+            sSmsPickPendingList.clear();
+        }
+    }
+
+    // Keep track if this activity has been stopped (i.e. user navigated away, power screen off,...)
+    // if so, treat it as the user navigating away and end the task if it is restarted without an
+    // onCreate/onNewIntent.
+    private boolean mPreviouslyStopped = false;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mPreviouslyStopped = false;
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        mPreviouslyStopped = false;
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        // This is cause a little jank with the recents display, but there is no other way to handle
+        // the case where activity has stopped and we want to dismiss the dialog. We use the
+        // tag "excludeFromRecents", but in the cases where it is still shown, kill it in onResume.
+        if (mPreviouslyStopped) {
+            finishAndRemoveTask();
+        } else {
+            launchSmsPicker(new Intent(getIntent()));
+        }
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        // User navigated away from dialog, send invalid sub id result.
+        mPreviouslyStopped = true;
+        sendResultAndClear(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        // triggers cancelled result for onActivityResult
+        finishActivity(REQUEST_GET_SMS_SUB_ID);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == REQUEST_GET_SMS_SUB_ID) {
+            int result = data == null ? SubscriptionManager.INVALID_SUBSCRIPTION_ID :
+                    data.getIntExtra(RESULT_SUB_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+            if (resultCode == Activity.RESULT_OK) {
+                sendResultAndClear(result);
+            } else {
+                sendResultAndClear(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+            }
+        }
+        // This will be handled in onResume - we do not want to call this all the time here because
+        // we need to be able to restart if stopped and a new intent comes in via onNewIntent.
+        if (!mPreviouslyStopped) {
+            finishAndRemoveTask();
+        }
+    }
+
+    private void launchSmsPicker(Intent trampolineIntent) {
+        trampolineIntent.setComponent(SETTINGS_SUB_PICK_ACTIVITY);
+        // Remove this flag if it exists, we want the settings activity to be part of this task.
+        trampolineIntent.removeFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        startActivityForResult(trampolineIntent, REQUEST_GET_SMS_SUB_ID);
+    }
+}
diff --git a/testapps/SmsManagerTestApp/Android.mk b/testapps/SmsManagerTestApp/Android.mk
new file mode 100644
index 0000000..307366b
--- /dev/null
+++ b/testapps/SmsManagerTestApp/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+src_dirs := src
+res_dirs := res
+
+LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
+LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
+
+LOCAL_PACKAGE_NAME := SmsManagerTestApp
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
diff --git a/testapps/SmsManagerTestApp/AndroidManifest.xml b/testapps/SmsManagerTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..c5f4621
--- /dev/null
+++ b/testapps/SmsManagerTestApp/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.phone.testapps.smsmanagertestapp">
+    <uses-sdk android:minSdkVersion="24" android:targetSdkVersion="29" />
+    <uses-permission android:name="android.permission.SEND_SMS"/>
+    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+    <application android:label="SmsManagerTestApp">
+        <activity
+            android:name=".SmsManagerTestApp"
+            android:label="SmsManagerTestApp">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <service android:name=".SmsManagerTestService" android:exported="false" />
+        <receiver android:name=".SendStatusReceiver"
+                  android:exported="false">
+            <intent-filter>
+                <action android:name="com.android.phone.testapps.smsmanagertestapp.message_sent_action" />
+                <data android:scheme="content" />
+            </intent-filter>
+        </receiver>
+
+    </application>
+</manifest>
+
diff --git a/testapps/SmsManagerTestApp/res/layout/activity_main.xml b/testapps/SmsManagerTestApp/res/layout/activity_main.xml
new file mode 100644
index 0000000..39fb6c6
--- /dev/null
+++ b/testapps/SmsManagerTestApp/res/layout/activity_main.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:paddingLeft="4dp">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical" >
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" android:layout_weight="0"
+            android:text="Outgoing SMS Phone Number"/>
+
+        <EditText
+            android:id="@+id/phone_number_text"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:inputType="phone" android:text="5555551212"/>
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height= "1dp"
+            android:paddingRight="4dp"
+            android:background="?android:attr/listDivider" />
+
+        <Button
+            android:id="@+id/send_text_button"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/send_text_button"/>
+        <Button
+            android:id="@+id/send_text_button_service"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/send_text_service_button"/>
+        <Button
+            android:id="@+id/get_sub_for_result_button"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/get_sub_for_result_button"/>
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/testapps/SmsManagerTestApp/res/values/donottranslate_strings.xml b/testapps/SmsManagerTestApp/res/values/donottranslate_strings.xml
new file mode 100644
index 0000000..d6497a3
--- /dev/null
+++ b/testapps/SmsManagerTestApp/res/values/donottranslate_strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+
+<resources>
+    <string name="send_text_button">Send Outgoing Text Now.</string>
+    <string name="send_text_service_button">Send Outgoing Text after 5 sec.</string>
+    <string name="get_sub_for_result_button">Ask user for sub id.</string>
+</resources>
\ No newline at end of file
diff --git a/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SendStatusReceiver.java b/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SendStatusReceiver.java
new file mode 100644
index 0000000..03709d1
--- /dev/null
+++ b/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SendStatusReceiver.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2019 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.phone.testapps.smsmanagertestapp;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.widget.Toast;
+
+/**
+ * Handles the PendingIntent result from SMS messages send to Telephony. Reports the results of
+ * those messages using Toasts.
+ */
+public class SendStatusReceiver extends BroadcastReceiver {
+
+    public static final String MESSAGE_SENT_ACTION =
+            "com.android.phone.testapps.smsmanagertestapp.message_sent_action";
+
+    // Defined by platform, but no constant provided. See docs for SmsManager.sendTextMessage.
+    private static final String EXTRA_ERROR_CODE = "errorCode";
+    private static final String EXTRA_NO_DEFAULT = "noDefault";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final int resultCode = getResultCode();
+        if (MESSAGE_SENT_ACTION.equals(intent.getAction())) {
+            int errorCode = intent.getIntExtra(EXTRA_ERROR_CODE, -1);
+            boolean userCancel = intent.getBooleanExtra(EXTRA_NO_DEFAULT, false);
+            if (userCancel) {
+                Toast.makeText(context, "SMS not sent, user cancelled.", Toast.LENGTH_LONG).show();
+            } else {
+                Toast.makeText(context, "SMS result=" + resultCode + ", error extra=" + errorCode,
+                        Toast.LENGTH_LONG).show();
+            }
+        }
+    }
+}
diff --git a/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SmsManagerTestApp.java b/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SmsManagerTestApp.java
new file mode 100644
index 0000000..75536f3
--- /dev/null
+++ b/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SmsManagerTestApp.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2019 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.phone.testapps.smsmanagertestapp;
+
+import android.Manifest;
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.telephony.SmsManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * Supports sending an SMS immediately and offloading the sending of the SMS to a background task.
+ */
+public class SmsManagerTestApp extends Activity {
+
+    private static final int REQUEST_PERMISSION_READ_STATE = 1;
+    private static final int REQUEST_GET_SMS_SUB_ID = 2;
+
+    private static final ComponentName SETTINGS_SUB_PICK_ACTIVITY = new ComponentName(
+            "com.android.settings", "com.android.settings.sim.SimDialogActivity");
+
+    /*
+     * Forwarded constants from SimDialogActivity.
+     */
+    private static final String DIALOG_TYPE_KEY = "dialog_type";
+    public static final String RESULT_SUB_ID = "result_sub_id";
+    private static final int SMS_PICK = 2;
+
+    private static int sMessageId = 0;
+    private boolean mIsReadPhoneStateGranted = false;
+
+    private EditText mPhoneNumber;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.activity_main);
+
+        findViewById(R.id.send_text_button).setOnClickListener(this::sendOutgoingSms);
+        findViewById(R.id.send_text_button_service)
+                .setOnClickListener(this::sendOutgoingSmsService);
+        findViewById(R.id.get_sub_for_result_button).setOnClickListener(this::getSubIdForResult);
+        mPhoneNumber = (EditText) findViewById(R.id.phone_number_text);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        if (checkSelfPermission(Manifest.permission.READ_PHONE_STATE)
+                != PackageManager.PERMISSION_GRANTED
+                || checkSelfPermission(Manifest.permission.SEND_SMS)
+                        != PackageManager.PERMISSION_GRANTED) {
+            mIsReadPhoneStateGranted = false;
+            requestPermissions(new String[]{Manifest.permission.READ_PHONE_STATE,
+                    Manifest.permission.SEND_SMS}, REQUEST_PERMISSION_READ_STATE);
+        } else {
+            mIsReadPhoneStateGranted = true;
+        }
+        if (mIsReadPhoneStateGranted) {
+            mPhoneNumber.setText(getPhoneNumber(), TextView.BufferType.NORMAL);
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        stopService(new Intent(this, SmsManagerTestService.class));
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, String[] permissions,
+            int[] grantResults) {
+        switch (requestCode) {
+            case REQUEST_PERMISSION_READ_STATE: {
+                if (grantResults.length > 0
+                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+                    mIsReadPhoneStateGranted = true;
+                } else {
+                    // permission denied
+                    Toast.makeText(this, "read_phone_state denied.", Toast.LENGTH_SHORT).show();
+                }
+            }
+
+        }
+
+        if (mIsReadPhoneStateGranted) {
+            mPhoneNumber.setText(getPhoneNumber(), TextView.BufferType.NORMAL);
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        switch (requestCode) {
+            case (REQUEST_GET_SMS_SUB_ID) : {
+                int resultSubId;
+                if (resultCode == RESULT_OK) {
+                    resultSubId = data == null ? -1 : data.getIntExtra(RESULT_SUB_ID,
+                            SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+                    Toast.makeText(this, "User picked sub id = " + resultSubId,
+                            Toast.LENGTH_LONG).show();
+                } else {
+                    Toast.makeText(this, "User cancelled dialog.",
+                            Toast.LENGTH_SHORT).show();
+                }
+                break;
+            }
+        }
+    }
+
+
+    private void sendOutgoingSms(View view) {
+        String phoneNumber = mPhoneNumber.getText().toString();
+        if (TextUtils.isEmpty(phoneNumber)) {
+            Toast.makeText(this, "Couldn't get phone number from view! Ignoring request...",
+                    Toast.LENGTH_SHORT).show();
+            return;
+        }
+        if (mIsReadPhoneStateGranted) {
+            SmsManager m = SmsManager.getDefault();
+            m.sendTextMessage(phoneNumber, null, "Test",
+                    PendingIntent.getBroadcast(this, sMessageId, getSendStatusIntent(), 0),
+                    null);
+            sMessageId++;
+        }
+    }
+
+    private void sendOutgoingSmsService(View view) {
+        String phoneNumber = mPhoneNumber.getText().toString();
+        if (TextUtils.isEmpty(phoneNumber)) {
+            Toast.makeText(this, "Couldn't get phone number from view! Ignoring request...",
+                    Toast.LENGTH_SHORT).show();
+            return;
+        }
+        if (mIsReadPhoneStateGranted) {
+            Intent sendSmsIntent = new Intent(SmsManagerTestService.SEND_SMS);
+            sendSmsIntent.putExtra(SmsManagerTestService.EXTRA_SEND_TEXT, "Text");
+            sendSmsIntent.putExtra(SmsManagerTestService.EXTRA_SEND_NUMBER, phoneNumber);
+            sendSmsIntent.putExtra(SmsManagerTestService.EXTRA_SEND_INTENT,
+                    PendingIntent.getBroadcast(this, sMessageId, getSendStatusIntent(), 0));
+            sendSmsIntent.setComponent(new ComponentName(this, SmsManagerTestService.class));
+            startService(sendSmsIntent);
+            sMessageId++;
+        }
+    }
+    private void getSubIdForResult(View view) {
+        // ask the user for a default SMS SIM.
+        Intent intent = new Intent();
+        intent.setComponent(SETTINGS_SUB_PICK_ACTIVITY);
+        intent.putExtra(DIALOG_TYPE_KEY, SMS_PICK);
+        try {
+            startActivity(intent, null);
+        } catch (ActivityNotFoundException anfe) {
+            // If Settings is not installed, only log the error as we do not want to break
+            // legacy applications.
+            Toast.makeText(this, "Unable to launch Settings application.",
+                    Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    private Intent getSendStatusIntent() {
+        // Encode requestId in intent data
+        return new Intent(SendStatusReceiver.MESSAGE_SENT_ACTION, null, this,
+                SendStatusReceiver.class);
+    }
+
+    private String getPhoneNumber() {
+        String result = "6505551212";
+        int defaultSmsSub = SubscriptionManager.getDefaultSmsSubscriptionId();
+        if (mIsReadPhoneStateGranted) {
+            TelephonyManager tm = getSystemService(TelephonyManager.class);
+            if (tm != null) {
+                tm = tm.createForSubscriptionId(defaultSmsSub);
+                String line1Number = tm.getLine1Number();
+                if (!TextUtils.isEmpty(line1Number)) {
+                    return line1Number;
+                }
+            }
+        } else {
+            Toast.makeText(this, "Couldn't resolve line 1 due to permissions error.",
+                    Toast.LENGTH_LONG).show();
+        }
+        return result;
+    }
+}
diff --git a/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SmsManagerTestService.java b/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SmsManagerTestService.java
new file mode 100644
index 0000000..fcf4a67
--- /dev/null
+++ b/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SmsManagerTestService.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2019 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.phone.testapps.smsmanagertestapp;
+
+import android.app.IntentService;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.telephony.SmsManager;
+import android.util.Log;
+
+/**
+ * IntentService whose purpose is to handle outgoing SMS intents for this application and schedule
+ * them onto a AsyncTask to sleep for 5 seconds. This allows us to simulate SMS messages being sent
+ * from background services.
+ */
+public class SmsManagerTestService extends IntentService {
+
+    private static final String LOG_TAG = "smsmanagertestservice";
+
+    private static class SendSmsJob extends AsyncTask<Intent, Void, Void> {
+
+        @Override
+        protected Void doInBackground(Intent... intents) {
+            Intent intent = intents[0];
+            try {
+                Thread.sleep(5000);
+            } catch (InterruptedException e) {
+                // testing
+            }
+
+            String text = intent.getStringExtra(EXTRA_SEND_TEXT);
+            String phoneNumber = intent.getStringExtra(EXTRA_SEND_NUMBER);
+            PendingIntent sendIntent = intent.getParcelableExtra(EXTRA_SEND_INTENT);
+            sendSms(phoneNumber, text, sendIntent);
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Void aVoid) {
+            Log.i(LOG_TAG, "SMS sent");
+        }
+
+    }
+
+    public static final String SEND_SMS = "com.android.phone.testapps.smsmanagertestapp.send_sms";
+    public static final String EXTRA_SEND_TEXT = "text";
+    public static final String EXTRA_SEND_NUMBER = "number";
+    public static final String EXTRA_SEND_INTENT = "sendIntent";
+
+    public SmsManagerTestService() {
+        super("SmsManagerTestService");
+    }
+
+
+    @Override
+    protected void onHandleIntent(Intent intent) {
+        switch (intent.getAction()) {
+            case SEND_SMS : {
+                new SendSmsJob().execute(intent);
+                break;
+            }
+        }
+    }
+
+    private static void sendSms(String phoneNumber, String text, PendingIntent sendIntent) {
+        SmsManager m = SmsManager.getDefault();
+        m.sendTextMessage(phoneNumber, null, text, sendIntent, null);
+    }
+}
diff --git a/tests/src/com/android/phone/CarrierXmlParserTest.java b/tests/src/com/android/phone/CarrierXmlParserTest.java
new file mode 100644
index 0000000..92a8ba7
--- /dev/null
+++ b/tests/src/com/android/phone/CarrierXmlParserTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2019 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.phone;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+import java.util.Vector;
+
+@RunWith(AndroidJUnit4.class)
+public class CarrierXmlParserTest {
+    private CarrierXmlParser mCarrierXmlParser;
+    private Context mContext;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+    }
+
+    @Test
+    public void verifyParserFormat_shouldSameAsXml() {
+        String expected =
+                "((\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*"
+                        + "([^*#]*)(\\*([^*#]*))?)?)?)?)?)?)?#)";
+
+        mCarrierXmlParser = new CarrierXmlParser(mContext, -1);
+
+        assertEquals(expected, mCarrierXmlParser.sParserFormat);
+    }
+
+    @Test
+    public void verifyUssdParser_shouldMatchText() {
+        String parserFormat =
+                "((\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*"
+                        + "([^*#]*)(\\*([^*#]*))?)?)?)?)?)?)?#)";
+        String text = "120*1*7*123456789*20*1*0*0#";
+        String[] expectedContent = {"120*1*7*123456789*20*1*0*0#",
+                "120*1*7*123456789*20*1*0*0#",
+                "120",
+                "*1*7*123456789*20*1*0*0",
+                "1",
+                "*7*123456789*20*1*0*0",
+                "7",
+                "*123456789*20*1*0*0",
+                "123456789",
+                "*20*1*0*0",
+                "20",
+                "*1*0*0",
+                "1",
+                "*0*0",
+                "0",
+                "*0",
+                "0"};
+        Vector<String> expected = new Vector<>();
+        Collections.addAll(expected, expectedContent);
+
+        CarrierXmlParser.UssdParser ussdParser = new CarrierXmlParser.UssdParser(parserFormat);
+        ussdParser.newFromResponseString(text);
+
+        assertEquals(expected, ussdParser.getResult());
+    }
+}