Merge "Release the N/W request when SIM is removed"
diff --git a/Android.bp b/Android.bp
index a7a533c..e7e78ae 100644
--- a/Android.bp
+++ b/Android.bp
@@ -15,6 +15,10 @@
 //
 // Mms service
 //
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 android_app {
     name: "MmsService",
     platform_apis: true,
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index e033528..87e7947 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -25,8 +25,6 @@
     <uses-permission android:name="android.permission.WRITE_SMS"/>
     <uses-permission android:name="android.permission.BROADCAST_WAP_PUSH"/>
     <uses-permission android:name="android.permission.BIND_CARRIER_SERVICES"/>
-    <!-- BIND_CARRIER_MESSAGING_SERVICE has been deprecated in favor of BIND_CARRIER_SERVICES. -->
-    <uses-permission android:name="android.permission.BIND_CARRIER_MESSAGING_SERVICE"/>
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
 
     <protected-broadcast android:name="android.settings.ENABLE_MMS_DATA_REQUEST"/>
diff --git a/OWNERS b/OWNERS
index e2a657c..cc5e6a3 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,3 +1,7 @@
 amitmahajan@google.com
 rgreenwalt@google.com
-sanketpadawe@google.com
+tomtaylor@google.com
+afurtado@google.com
+jianxiangp@google.com
+stephshi@google.com
+bellavicevh@google.com
diff --git a/src/com/android/mms/service/ApnSettings.java b/src/com/android/mms/service/ApnSettings.java
index 48383c1..42d6427 100644
--- a/src/com/android/mms/service/ApnSettings.java
+++ b/src/com/android/mms/service/ApnSettings.java
@@ -18,14 +18,13 @@
 
 import android.content.Context;
 import android.database.Cursor;
-import android.database.sqlite.SqliteWrapper;
-import android.net.NetworkUtils;
 import android.net.Uri;
 import android.provider.Telephony;
+import android.telephony.data.ApnSetting;
 import android.text.TextUtils;
 
-import com.android.internal.telephony.PhoneConstants;
 import com.android.mms.service.exception.ApnException;
+import com.android.net.module.util.Inet4AddressUtils;
 
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -34,6 +33,7 @@
  * APN settings used for MMS transactions
  */
 public class ApnSettings {
+
     // MMSC URL
     private final String mServiceCenter;
     // MMSC proxy address
@@ -62,29 +62,29 @@
             Telephony.Carriers.USER,
             Telephony.Carriers.PASSWORD,
     };
-    private static final int COLUMN_TYPE         = 0;
-    private static final int COLUMN_MMSC         = 1;
-    private static final int COLUMN_MMSPROXY     = 2;
-    private static final int COLUMN_MMSPORT      = 3;
-    private static final int COLUMN_NAME         = 4;
-    private static final int COLUMN_APN          = 5;
-    private static final int COLUMN_BEARER       = 6;
-    private static final int COLUMN_PROTOCOL     = 7;
+    private static final int COLUMN_TYPE = 0;
+    private static final int COLUMN_MMSC = 1;
+    private static final int COLUMN_MMSPROXY = 2;
+    private static final int COLUMN_MMSPORT = 3;
+    private static final int COLUMN_NAME = 4;
+    private static final int COLUMN_APN = 5;
+    private static final int COLUMN_BEARER = 6;
+    private static final int COLUMN_PROTOCOL = 7;
     private static final int COLUMN_ROAMING_PROTOCOL = 8;
-    private static final int COLUMN_AUTH_TYPE    = 9;
-    private static final int COLUMN_MVNO_TYPE    = 10;
+    private static final int COLUMN_AUTH_TYPE = 9;
+    private static final int COLUMN_MVNO_TYPE = 10;
     private static final int COLUMN_MVNO_MATCH_DATA = 11;
-    private static final int COLUMN_PROXY        = 12;
-    private static final int COLUMN_PORT         = 13;
-    private static final int COLUMN_SERVER       = 14;
-    private static final int COLUMN_USER         = 15;
-    private static final int COLUMN_PASSWORD     = 16;
+    private static final int COLUMN_PROXY = 12;
+    private static final int COLUMN_PORT = 13;
+    private static final int COLUMN_SERVER = 14;
+    private static final int COLUMN_USER = 15;
+    private static final int COLUMN_PASSWORD = 16;
 
 
     /**
      * Load APN settings from system
-     *  @param context
-     * @param apnName the optional APN name to match
+     *
+     * @param apnName   the optional APN name to match
      * @param requestId the request ID for logging
      */
     public static ApnSettings load(Context context, String apnName, int subId, String requestId)
@@ -98,64 +98,66 @@
         if (!TextUtils.isEmpty(apnName)) {
             //selection += " AND " + Telephony.Carriers.APN + "=?";
             selection = Telephony.Carriers.APN + "=?";
-            selectionArgs = new String[]{ apnName };
+            selectionArgs = new String[]{apnName};
         }
-        Cursor cursor = null;
-        try {
-            cursor = SqliteWrapper.query(
-                    context,
-                    context.getContentResolver(),
-                    Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "/subId/" + subId),
+
+        try (Cursor cursor = context.getContentResolver().query(
+                    Uri.withAppendedPath(Telephony.Carriers.SIM_APN_URI, String.valueOf(subId)),
                     APN_PROJECTION,
                     selection,
                     selectionArgs,
-                    null/*sortOrder*/);
-            if (cursor != null) {
-                String mmscUrl = null;
-                String proxyAddress = null;
-                // Default proxy port to 80
-                int proxyPort = 80;
-                while (cursor.moveToNext()) {
-                    // Read values from APN settings
-                    if (isValidApnType(
-                            cursor.getString(COLUMN_TYPE), PhoneConstants.APN_TYPE_MMS)) {
-                        mmscUrl = trimWithNullCheck(cursor.getString(COLUMN_MMSC));
-                        if (TextUtils.isEmpty(mmscUrl)) {
-                            continue;
-                        }
-                        mmscUrl = NetworkUtils.trimV4AddrZeros(mmscUrl);
-                        try {
-                            new URI(mmscUrl);
-                        } catch (URISyntaxException e) {
-                            throw new ApnException("Invalid MMSC url " + mmscUrl);
-                        }
-                        proxyAddress = trimWithNullCheck(cursor.getString(COLUMN_MMSPROXY));
-                        if (!TextUtils.isEmpty(proxyAddress)) {
-                            proxyAddress = NetworkUtils.trimV4AddrZeros(proxyAddress);
-                            final String portString =
-                                    trimWithNullCheck(cursor.getString(COLUMN_MMSPORT));
-                            if (!TextUtils.isEmpty(portString)) {
-                                try {
-                                    proxyPort = Integer.parseInt(portString);
-                                } catch (NumberFormatException e) {
-                                    LogUtil.e(requestId, "Invalid port " + portString + ", use 80");
-                                }
-                            }
-                        }
-                        return new ApnSettings(
-                                mmscUrl, proxyAddress, proxyPort, getDebugText(cursor));
-                    }
-                }
+                    null/*sortOrder*/)) {
 
-            }
-        } finally {
-            if (cursor != null) {
-                cursor.close();
+            ApnSettings settings = getApnSettingsFromCursor(cursor, requestId);
+            if (settings != null) {
+                return settings;
             }
         }
         throw new ApnException("Can not find valid APN");
     }
 
+    private static ApnSettings getApnSettingsFromCursor(Cursor cursor, String requestId)
+            throws ApnException {
+        if (cursor == null) {
+            return null;
+        }
+
+        // Default proxy port to 80
+        int proxyPort = 80;
+        while (cursor.moveToNext()) {
+            // Read values from APN settings
+            if (isValidApnType(
+                    cursor.getString(COLUMN_TYPE), ApnSetting.TYPE_MMS_STRING)) {
+                String mmscUrl = trimWithNullCheck(cursor.getString(COLUMN_MMSC));
+                if (TextUtils.isEmpty(mmscUrl)) {
+                    continue;
+                }
+                mmscUrl = Inet4AddressUtils.trimAddressZeros(mmscUrl);
+                try {
+                    new URI(mmscUrl);
+                } catch (URISyntaxException e) {
+                    throw new ApnException("Invalid MMSC url " + mmscUrl);
+                }
+                String proxyAddress = trimWithNullCheck(cursor.getString(COLUMN_MMSPROXY));
+                if (!TextUtils.isEmpty(proxyAddress)) {
+                    proxyAddress = Inet4AddressUtils.trimAddressZeros(proxyAddress);
+                    final String portString =
+                            trimWithNullCheck(cursor.getString(COLUMN_MMSPORT));
+                    if (!TextUtils.isEmpty(portString)) {
+                        try {
+                            proxyPort = Integer.parseInt(portString);
+                        } catch (NumberFormatException e) {
+                            LogUtil.e(requestId, "Invalid port " + portString + ", use 80");
+                        }
+                    }
+                }
+                return new ApnSettings(
+                        mmscUrl, proxyAddress, proxyPort, getDebugText(cursor));
+            }
+        }
+        return null;
+    }
+
     private static String getDebugText(Cursor cursor) {
         final StringBuilder sb = new StringBuilder();
         sb.append("APN [");
@@ -183,7 +185,7 @@
         mProxyAddress = proxyAddr;
         mProxyPort = proxyPort;
         mDebugText = debugText;
-   }
+    }
 
     public String getMmscUrl() {
         return mServiceCenter;
@@ -202,13 +204,13 @@
     }
 
     private static boolean isValidApnType(String types, String requestType) {
-        // If APN type is unspecified, assume APN_TYPE_ALL.
+        // If APN type is unspecified, assume TYPE_ALL_STRING.
         if (TextUtils.isEmpty(types)) {
             return true;
         }
         for (String type : types.split(",")) {
             type = type.trim();
-            if (type.equals(requestType) || type.equals(PhoneConstants.APN_TYPE_ALL)) {
+            if (type.equals(requestType) || type.equals(ApnSetting.TYPE_ALL_STRING)) {
                 return true;
             }
         }
diff --git a/src/com/android/mms/service/DownloadRequest.java b/src/com/android/mms/service/DownloadRequest.java
index 8079fa4..f5fe493 100644
--- a/src/com/android/mms/service/DownloadRequest.java
+++ b/src/com/android/mms/service/DownloadRequest.java
@@ -59,8 +59,8 @@
 
     public DownloadRequest(RequestManager manager, int subId, String locationUrl,
             Uri contentUri, PendingIntent downloadedIntent, String creator,
-            Bundle configOverrides, Context context) {
-        super(manager, subId, creator, configOverrides, context);
+            Bundle configOverrides, Context context, long messageId) {
+        super(manager, subId, creator, configOverrides, context, messageId);
         mLocationUrl = locationUrl;
         mDownloadedIntent = downloadedIntent;
         mContentUri = contentUri;
@@ -72,8 +72,9 @@
         final String requestId = getRequestId();
         final MmsHttpClient mmsHttpClient = netMgr.getOrCreateHttpClient();
         if (mmsHttpClient == null) {
-            LogUtil.e(requestId, "MMS network is not ready!");
-            throw new MmsHttpException(0/*statusCode*/, "MMS network is not ready");
+            LogUtil.e(requestId, "MMS network is not ready! messageId: " + mMessageId);
+            throw new MmsHttpException(0/*statusCode*/, "MMS network is not ready. messageId: "
+                    + mMessageId);
         }
         return mmsHttpClient.execute(
                 mLocationUrl,
@@ -106,9 +107,9 @@
         if (!mRequestManager.getAutoPersistingPref()) {
             return null;
         }
-        LogUtil.d(requestId, "persistIfRequired");
+        LogUtil.d(requestId, "persistIfRequired. messageId: " + mMessageId);
         if (response == null || response.length < 1) {
-            LogUtil.e(requestId, "persistIfRequired: empty response");
+            LogUtil.e(requestId, "persistIfRequired: empty response. messageId: " + mMessageId);
             return null;
         }
         final long identity = Binder.clearCallingIdentity();
@@ -117,13 +118,15 @@
                     mMmsConfig.getBoolean(SmsManager.MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION);
             final GenericPdu pdu = (new PduParser(response, supportMmsContentDisposition)).parse();
             if (pdu == null || !(pdu instanceof RetrieveConf)) {
-                LogUtil.e(requestId, "persistIfRequired: invalid parsed PDU");
+                LogUtil.e(requestId, "persistIfRequired: invalid parsed PDU. messageId: "
+                        + mMessageId);
                 return null;
             }
             final RetrieveConf retrieveConf = (RetrieveConf) pdu;
             final int status = retrieveConf.getRetrieveStatus();
             if (status != PduHeaders.RETRIEVE_STATUS_OK) {
-                LogUtil.e(requestId, "persistIfRequired: retrieve failed " + status);
+                LogUtil.e(requestId, "persistIfRequired: retrieve failed " + status
+                        + ", messageId: " + mMessageId);
                 // Update the retrieve status of the NotificationInd
                 final ContentValues values = new ContentValues(1);
                 values.put(Telephony.Mms.RETRIEVE_STATUS, status);
@@ -148,7 +151,8 @@
                     true/*groupMmsEnabled*/,
                     null/*preOpenedFiles*/);
             if (messageUri == null) {
-                LogUtil.e(requestId, "persistIfRequired: can not persist message");
+                LogUtil.e(requestId, "persistIfRequired: can not persist message. messageId: "
+                        + mMessageId);
                 return null;
             }
             // Update some of the properties of the message
@@ -167,7 +171,8 @@
                     values,
                     null/*where*/,
                     null/*selectionArg*/) != 1) {
-                LogUtil.e(requestId, "persistIfRequired: can not update message");
+                LogUtil.e(requestId, "persistIfRequired: can not update message. messageId: "
+                        + mMessageId);
             }
             // Delete the corresponding NotificationInd
             SqliteWrapper.delete(context,
@@ -181,11 +186,14 @@
 
             return messageUri;
         } catch (MmsException e) {
-            LogUtil.e(requestId, "persistIfRequired: can not persist message", e);
+            LogUtil.e(requestId, "persistIfRequired: can not persist message. messageId: "
+                    + mMessageId, e);
         } catch (SQLiteException e) {
-            LogUtil.e(requestId, "persistIfRequired: can not update message", e);
+            LogUtil.e(requestId, "persistIfRequired: can not update message. messageId: "
+                    + mMessageId, e);
         } catch (RuntimeException e) {
-            LogUtil.e(requestId, "persistIfRequired: can not parse response", e);
+            LogUtil.e(requestId, "persistIfRequired: can not parse response. messageId: "
+                    + mMessageId, e);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -269,29 +277,38 @@
     /**
      * Downloads the MMS through through the carrier app.
      */
-    private final class CarrierDownloadManager extends CarrierMessagingServiceWrapper {
+    private final class CarrierDownloadManager {
         // Initialized in downloadMms
         private volatile CarrierDownloadCompleteCallback mCarrierDownloadCallback;
+        private final CarrierMessagingServiceWrapper mCarrierMessagingServiceWrapper =
+                new CarrierMessagingServiceWrapper();
+
+        void disposeConnection(Context context) {
+            mCarrierMessagingServiceWrapper.disposeConnection(context);
+        }
 
         void downloadMms(Context context, String carrierMessagingServicePackage,
                 CarrierDownloadCompleteCallback carrierDownloadCallback) {
             mCarrierDownloadCallback = carrierDownloadCallback;
-            if (bindToCarrierMessagingService(context, carrierMessagingServicePackage)) {
-                LogUtil.v("bindService() for carrier messaging service succeeded");
+            if (mCarrierMessagingServiceWrapper.bindToCarrierMessagingService(
+                    context, carrierMessagingServicePackage, ()->onServiceReady())) {
+                LogUtil.v("bindService() for carrier messaging service succeeded. messageId: "
+                        + mMessageId);
             } else {
-                LogUtil.e("bindService() for carrier messaging service failed");
+                LogUtil.e("bindService() for carrier messaging service failed. messageId: "
+                        + mMessageId);
                 carrierDownloadCallback.onDownloadMmsComplete(
                         CarrierMessagingService.DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK);
             }
         }
 
-        @Override
-        public void onServiceReady() {
+        private void onServiceReady() {
             try {
-                downloadMms(mContentUri, mSubId, Uri.parse(mLocationUrl),
-                        mCarrierDownloadCallback);
+                mCarrierMessagingServiceWrapper.downloadMms(
+                        mContentUri, mSubId, Uri.parse(mLocationUrl), mCarrierDownloadCallback);
             } catch (RuntimeException e) {
-                LogUtil.e("Exception downloading MMS using the carrier messaging service: " + e, e);
+                LogUtil.e("Exception downloading MMS for messageId " + mMessageId
+                        + " using the carrier messaging service: " + e, e);
                 mCarrierDownloadCallback.onDownloadMmsComplete(
                         CarrierMessagingService.DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK);
             }
@@ -315,17 +332,19 @@
 
         @Override
         public void onSendMmsComplete(int result, byte[] sendConfPdu) {
-            LogUtil.e("Unexpected onSendMmsComplete call with result: " + result);
+            LogUtil.e("Unexpected onSendMmsComplete call with result: " + result
+                    + ", messageId: " + mMessageId);
         }
 
         @Override
         public void onDownloadMmsComplete(int result) {
-            LogUtil.d("Carrier app result for download: " + result);
+            LogUtil.d("Carrier app result for download: " + result
+                    + ", messageId: " + mMessageId);
             mCarrierDownloadManager.disposeConnection(mContext);
 
             if (!maybeFallbackToRegularDelivery(result)) {
                 processResult(mContext, toSmsManagerResult(result), null/* response */,
-                        0/* httpStatusCode */);
+                        0/* httpStatusCode */, /* handledByCarrierApp= */ true);
             }
         }
     }
diff --git a/src/com/android/mms/service/MmsConfigManager.java b/src/com/android/mms/service/MmsConfigManager.java
index 20ae9e4..3606ed8 100644
--- a/src/com/android/mms/service/MmsConfigManager.java
+++ b/src/com/android/mms/service/MmsConfigManager.java
@@ -20,9 +20,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.os.BaseBundle;
 import android.os.Bundle;
-import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SmsManager;
 import android.telephony.SubscriptionInfo;
@@ -99,90 +97,10 @@
     }
 
     /**
-     * Filters a bundle to only contain MMS config variables.
-     *
-     * This is for use with bundles returned by {@link CarrierConfigManager} which contain MMS
-     * config and unrelated config. It is assumed that all MMS_CONFIG_* keys are present in the
-     * supplied bundle.
-     *
-     * @param config a Bundle that contains MMS config variables and possibly more.
-     * @return a new Bundle that only contains the MMS_CONFIG_* keys defined above.
-     * @hide
-     */
-    private static Bundle getMmsConfig(BaseBundle config) {
-        Bundle filtered = new Bundle();
-        filtered.putBoolean(SmsManager.MMS_CONFIG_APPEND_TRANSACTION_ID,
-                config.getBoolean(SmsManager.MMS_CONFIG_APPEND_TRANSACTION_ID));
-        filtered.putBoolean(SmsManager.MMS_CONFIG_MMS_ENABLED,
-                config.getBoolean(SmsManager.MMS_CONFIG_MMS_ENABLED));
-        filtered.putBoolean(SmsManager.MMS_CONFIG_GROUP_MMS_ENABLED,
-                config.getBoolean(SmsManager.MMS_CONFIG_GROUP_MMS_ENABLED));
-        filtered.putBoolean(SmsManager.MMS_CONFIG_NOTIFY_WAP_MMSC_ENABLED,
-                config.getBoolean(SmsManager.MMS_CONFIG_NOTIFY_WAP_MMSC_ENABLED));
-        filtered.putBoolean(SmsManager.MMS_CONFIG_ALIAS_ENABLED,
-                config.getBoolean(SmsManager.MMS_CONFIG_ALIAS_ENABLED));
-        filtered.putBoolean(SmsManager.MMS_CONFIG_ALLOW_ATTACH_AUDIO,
-                config.getBoolean(SmsManager.MMS_CONFIG_ALLOW_ATTACH_AUDIO));
-        filtered.putBoolean(SmsManager.MMS_CONFIG_MULTIPART_SMS_ENABLED,
-                config.getBoolean(SmsManager.MMS_CONFIG_MULTIPART_SMS_ENABLED));
-        filtered.putBoolean(SmsManager.MMS_CONFIG_SMS_DELIVERY_REPORT_ENABLED,
-                config.getBoolean(SmsManager.MMS_CONFIG_SMS_DELIVERY_REPORT_ENABLED));
-        filtered.putBoolean(SmsManager.MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION,
-                config.getBoolean(SmsManager.MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION));
-        filtered.putBoolean(SmsManager.MMS_CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES,
-                config.getBoolean(SmsManager.MMS_CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES));
-        filtered.putBoolean(SmsManager.MMS_CONFIG_MMS_READ_REPORT_ENABLED,
-                config.getBoolean(SmsManager.MMS_CONFIG_MMS_READ_REPORT_ENABLED));
-        filtered.putBoolean(SmsManager.MMS_CONFIG_MMS_DELIVERY_REPORT_ENABLED,
-                config.getBoolean(SmsManager.MMS_CONFIG_MMS_DELIVERY_REPORT_ENABLED));
-        filtered.putBoolean(SmsManager.MMS_CONFIG_CLOSE_CONNECTION,
-                config.getBoolean(SmsManager.MMS_CONFIG_CLOSE_CONNECTION));
-        filtered.putInt(SmsManager.MMS_CONFIG_MAX_MESSAGE_SIZE,
-                config.getInt(SmsManager.MMS_CONFIG_MAX_MESSAGE_SIZE));
-        filtered.putInt(SmsManager.MMS_CONFIG_MAX_IMAGE_WIDTH,
-                config.getInt(SmsManager.MMS_CONFIG_MAX_IMAGE_WIDTH));
-        filtered.putInt(SmsManager.MMS_CONFIG_MAX_IMAGE_HEIGHT,
-                config.getInt(SmsManager.MMS_CONFIG_MAX_IMAGE_HEIGHT));
-        filtered.putInt(SmsManager.MMS_CONFIG_RECIPIENT_LIMIT,
-                config.getInt(SmsManager.MMS_CONFIG_RECIPIENT_LIMIT));
-        filtered.putInt(SmsManager.MMS_CONFIG_ALIAS_MIN_CHARS,
-                config.getInt(SmsManager.MMS_CONFIG_ALIAS_MIN_CHARS));
-        filtered.putInt(SmsManager.MMS_CONFIG_ALIAS_MAX_CHARS,
-                config.getInt(SmsManager.MMS_CONFIG_ALIAS_MAX_CHARS));
-        filtered.putInt(SmsManager.MMS_CONFIG_SMS_TO_MMS_TEXT_THRESHOLD,
-                config.getInt(SmsManager.MMS_CONFIG_SMS_TO_MMS_TEXT_THRESHOLD));
-        filtered.putInt(SmsManager.MMS_CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD,
-                config.getInt(SmsManager.MMS_CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD));
-        filtered.putInt(SmsManager.MMS_CONFIG_MESSAGE_TEXT_MAX_SIZE,
-                config.getInt(SmsManager.MMS_CONFIG_MESSAGE_TEXT_MAX_SIZE));
-        filtered.putInt(SmsManager.MMS_CONFIG_SUBJECT_MAX_LENGTH,
-                config.getInt(SmsManager.MMS_CONFIG_SUBJECT_MAX_LENGTH));
-        filtered.putInt(SmsManager.MMS_CONFIG_HTTP_SOCKET_TIMEOUT,
-                config.getInt(SmsManager.MMS_CONFIG_HTTP_SOCKET_TIMEOUT));
-        filtered.putString(SmsManager.MMS_CONFIG_UA_PROF_TAG_NAME,
-                config.getString(SmsManager.MMS_CONFIG_UA_PROF_TAG_NAME));
-        filtered.putString(SmsManager.MMS_CONFIG_USER_AGENT,
-                config.getString(SmsManager.MMS_CONFIG_USER_AGENT));
-        filtered.putString(SmsManager.MMS_CONFIG_UA_PROF_URL,
-                config.getString(SmsManager.MMS_CONFIG_UA_PROF_URL));
-        filtered.putString(SmsManager.MMS_CONFIG_HTTP_PARAMS,
-                config.getString(SmsManager.MMS_CONFIG_HTTP_PARAMS));
-        filtered.putString(SmsManager.MMS_CONFIG_EMAIL_GATEWAY_NUMBER,
-                config.getString(SmsManager.MMS_CONFIG_EMAIL_GATEWAY_NUMBER));
-        filtered.putString(SmsManager.MMS_CONFIG_NAI_SUFFIX,
-                config.getString(SmsManager.MMS_CONFIG_NAI_SUFFIX));
-        filtered.putBoolean(SmsManager.MMS_CONFIG_SHOW_CELL_BROADCAST_APP_LINKS,
-                config.getBoolean(SmsManager.MMS_CONFIG_SHOW_CELL_BROADCAST_APP_LINKS));
-        filtered.putBoolean(SmsManager.MMS_CONFIG_SUPPORT_HTTP_CHARSET_HEADER,
-                config.getBoolean(SmsManager.MMS_CONFIG_SUPPORT_HTTP_CHARSET_HEADER));
-        return filtered;
-    }
-
-    /**
      * This loads the MMS config for each active subscription.
      *
-     * MMS config is fetched from CarrierConfigManager and filtered to only include MMS config
-     * variables. The resulting bundles are stored in mSubIdConfigMap.
+     * MMS config is fetched from SmsManager#getCarrierConfigValues(). The resulting bundles are
+     * stored in mSubIdConfigMap.
      */
     private void load(Context context) {
         List<SubscriptionInfo> subs = mSubscriptionManager.getActiveSubscriptionInfoList();
@@ -193,15 +111,14 @@
         // Load all the config bundles into a new map and then swap it with the real map to avoid
         // blocking.
         final Map<Integer, Bundle> newConfigMap = new ArrayMap<Integer, Bundle>();
-        final CarrierConfigManager configManager =
-                (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
         for (SubscriptionInfo sub : subs) {
             final int subId = sub.getSubscriptionId();
             LogUtil.i("MmsConfigManager loads mms config for "
                     + sub.getMccString() + "/" +  sub.getMncString()
                     + ", CarrierId " + sub.getCarrierId());
-            PersistableBundle config = configManager.getConfigForSubId(subId);
-            newConfigMap.put(subId, getMmsConfig(config));
+            newConfigMap.put(
+                    subId,
+                    SmsManager.getSmsManagerForSubscriptionId(subId).getCarrierConfigValues());
         }
         synchronized(mSubIdConfigMap) {
             mSubIdConfigMap.clear();
diff --git a/src/com/android/mms/service/MmsHttpClient.java b/src/com/android/mms/service/MmsHttpClient.java
index a7d47c4..5cec66f 100644
--- a/src/com/android/mms/service/MmsHttpClient.java
+++ b/src/com/android/mms/service/MmsHttpClient.java
@@ -168,7 +168,7 @@
             // Some carriers require that the HTTP connection's socket is closed
             // after an MMS request/response is complete. In these cases keep alive
             // is disabled. See https://tools.ietf.org/html/rfc7230#section-6.6
-            if (mmsConfig.getBoolean(SmsManager.MMS_CONFIG_CLOSE_CONNECTION, false)) {
+            if (mmsConfig.getBoolean(CarrierConfigManager.KEY_MMS_CLOSE_CONNECTION_BOOL, false)) {
                 LogUtil.i(requestId, "HTTP: Connection close after request");
                 connection.setRequestProperty(HEADER_CONNECTION, HEADER_CONNECTION_CLOSE);
             }
diff --git a/src/com/android/mms/service/MmsNetworkManager.java b/src/com/android/mms/service/MmsNetworkManager.java
index b0eeee8..af4d8a4 100644
--- a/src/com/android/mms/service/MmsNetworkManager.java
+++ b/src/com/android/mms/service/MmsNetworkManager.java
@@ -25,6 +25,7 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkRequest;
+import android.net.TelephonyNetworkSpecifier;
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.DeviceConfig;
@@ -183,7 +184,8 @@
         mNetworkRequest = new NetworkRequest.Builder()
                 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
                 .addCapability(NetworkCapabilities.NET_CAPABILITY_MMS)
-                .setNetworkSpecifier(Integer.toString(mSubId))
+                .setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
+                        .setSubscriptionId(mSubId).build())
                 .build();
 
         mNetworkReleaseTask = new Runnable() {
diff --git a/src/com/android/mms/service/MmsRequest.java b/src/com/android/mms/service/MmsRequest.java
index 9f3e620..f8c882b 100644
--- a/src/com/android/mms/service/MmsRequest.java
+++ b/src/com/android/mms/service/MmsRequest.java
@@ -21,9 +21,19 @@
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
 import android.os.Bundle;
 import android.service.carrier.CarrierMessagingService;
-import android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper;
+import android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallback;
+import android.telephony.AnomalyReporter;
+import android.telephony.PhoneStateListener;
+import android.telephony.PreciseDataConnectionState;
+import android.telephony.TelephonyCallback;
+import android.telephony.data.ApnSetting;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.ImsMmTelManager;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.telephony.SmsManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
@@ -32,11 +42,21 @@
 import com.android.mms.service.exception.MmsHttpException;
 import com.android.mms.service.exception.MmsNetworkException;
 
+import java.util.UUID;
+
 /**
  * Base class for MMS requests. This has the common logic of sending/downloading MMS.
  */
 public abstract class MmsRequest {
     private static final int RETRY_TIMES = 3;
+    // Signal level threshold for both wifi and cellular
+    private static final int SIGNAL_LEVEL_THRESHOLD = 2;
+    // MMS anomaly uuid
+    private final UUID mAnomalyUUID = UUID.fromString("e4330975-17be-43b7-87d6-d9f281d33278");
+    public static final String EXTRA_LAST_CONNECTION_FAILURE_CAUSE_CODE
+            = "android.telephony.extra.LAST_CONNECTION_FAILURE_CAUSE_CODE";
+    public static final String EXTRA_HANDLED_BY_CARRIER_APP
+            = "android.telephony.extra.HANDLED_BY_CARRIER_APP";
 
     /**
      * Interface for certain functionalities from MmsService
@@ -83,15 +103,36 @@
     protected Bundle mMmsConfigOverrides;
     // Context used to get TelephonyManager.
     protected Context mContext;
+    protected long mMessageId;
+    protected int mLastConnectionFailure;
+
+    class MonitorTelephonyCallback extends TelephonyCallback implements
+            TelephonyCallback.PreciseDataConnectionStateListener {
+        @Override
+        public void onPreciseDataConnectionStateChanged(
+                PreciseDataConnectionState connectionState) {
+            if (connectionState == null) {
+                return;
+            }
+            ApnSetting apnSetting = connectionState.getApnSetting();
+            int apnTypes = apnSetting.getApnTypeBitmask();
+            if ((apnTypes & ApnSetting.TYPE_MMS) != 0) {
+                mLastConnectionFailure = connectionState.getLastCauseCode();
+                LogUtil.d("onPreciseDataConnectionStateChanged mLastConnectionFailure: "
+                        + mLastConnectionFailure);
+            }
+        }
+    }
 
     public MmsRequest(RequestManager requestManager, int subId, String creator,
-            Bundle configOverrides, Context context) {
+            Bundle configOverrides, Context context, long messageId) {
         mRequestManager = requestManager;
         mSubId = subId;
         mCreator = creator;
         mMmsConfigOverrides = configOverrides;
         mMmsConfig = null;
         mContext = context;
+        mMessageId = messageId;
     }
 
     public int getSubId() {
@@ -133,7 +174,7 @@
      * @param networkManager The network manager to use
      */
     public void execute(Context context, MmsNetworkManager networkManager) {
-        final String requestId = this.toString();
+        final String requestId = this.getRequestId();
         LogUtil.i(requestId, "Executing...");
         int result = SmsManager.MMS_ERROR_UNSPECIFIED;
         int httpStatusCode = 0;
@@ -150,7 +191,10 @@
             long retryDelaySecs = 2;
             // Try multiple times of MMS HTTP request, depending on the error.
             for (int i = 0; i < RETRY_TIMES; i++) {
+                httpStatusCode = 0; // Clear for retry.
+                MonitorTelephonyCallback connectionStateCallback = new MonitorTelephonyCallback();
                 try {
+                    listenToDataConnectionState(connectionStateCallback);
                     networkManager.acquireNetwork(requestId);
                     final String apnName = networkManager.getApnName();
                     LogUtil.d(requestId, "APN name is " + apnName);
@@ -174,7 +218,9 @@
                         // Success
                         break;
                     } finally {
-                        networkManager.releaseNetwork(requestId, this instanceof DownloadRequest);
+                        // Release the MMS network immediately except successful DownloadRequest.
+                        networkManager.releaseNetwork(requestId,
+                                this instanceof DownloadRequest && result == Activity.RESULT_OK);
                     }
                 } catch (ApnException e) {
                     LogUtil.e(requestId, "APN failure", e);
@@ -193,6 +239,8 @@
                     LogUtil.e(requestId, "Unexpected failure", e);
                     result = SmsManager.MMS_ERROR_UNSPECIFIED;
                     break;
+                } finally {
+                    stopListeningToDataConnectionState(connectionStateCallback);
                 }
                 try {
                     Thread.sleep(retryDelaySecs * 1000, 0/*nano*/);
@@ -200,20 +248,46 @@
                 retryDelaySecs <<= 1;
             }
         }
-        processResult(context, result, response, httpStatusCode);
+        processResult(context, result, response, httpStatusCode, /* handledByCarrierApp= */ false);
+    }
+
+    private void listenToDataConnectionState(MonitorTelephonyCallback connectionStateCallback) {
+        final TelephonyManager telephonyManager = mContext.getSystemService(
+                TelephonyManager.class).createForSubscriptionId(mSubId);
+        telephonyManager.registerTelephonyCallback(r -> r.run(), connectionStateCallback);
+    }
+
+    private void stopListeningToDataConnectionState(
+            MonitorTelephonyCallback connectionStateCallback) {
+        final TelephonyManager telephonyManager = mContext.getSystemService(
+                TelephonyManager.class).createForSubscriptionId(mSubId);
+        telephonyManager.unregisterTelephonyCallback(connectionStateCallback);
     }
 
     /**
      * Process the result of the completed request, including updating the message status
      * in database and sending back the result via pending intents.
-     *  @param context The context
+     * @param context The context
      * @param result The result code of execution
      * @param response The response body
      * @param httpStatusCode The optional http status code in case of http failure
+     * @param handledByCarrierApp True if the sending/downloading was handled by a carrier app
+     *                            rather than MmsService.
      */
-    public void processResult(Context context, int result, byte[] response, int httpStatusCode) {
+    public void processResult(Context context, int result, byte[] response, int httpStatusCode,
+            boolean handledByCarrierApp) {
         final Uri messageUri = persistIfRequired(context, result, response);
 
+        final String requestId = this.getRequestId();
+        // As noted in the @param comment above, the httpStatusCode is only set when there's
+        // an http failure. On success, such as an http code of 200, the value here will be 0.
+        // "httpStatusCode: xxx" is now reported for an http failure only.
+        LogUtil.i(requestId, "processResult: "
+                + (result == Activity.RESULT_OK ? "success" : "failure(" + result + ")")
+                + (httpStatusCode != 0 ? ", httpStatusCode: " + httpStatusCode : "")
+                + " handledByCarrierApp: " + handledByCarrierApp
+                + " mLastConnectionFailure: " + mLastConnectionFailure);
+
         // Return MMS HTTP request result via PendingIntent
         final PendingIntent pendingIntent = getPendingIntent();
         if (pendingIntent != null) {
@@ -229,19 +303,97 @@
             if (result == SmsManager.MMS_ERROR_HTTP_FAILURE && httpStatusCode != 0) {
                 fillIn.putExtra(SmsManager.EXTRA_MMS_HTTP_STATUS, httpStatusCode);
             }
+            fillIn.putExtra(EXTRA_LAST_CONNECTION_FAILURE_CAUSE_CODE,
+                    mLastConnectionFailure);
+            fillIn.putExtra(EXTRA_HANDLED_BY_CARRIER_APP, handledByCarrierApp);
             try {
                 if (!succeeded) {
                     result = SmsManager.MMS_ERROR_IO_ERROR;
                 }
+                reportPossibleAnomaly(result, httpStatusCode);
                 pendingIntent.send(context, result, fillIn);
             } catch (PendingIntent.CanceledException e) {
-                LogUtil.e(this.toString(), "Sending pending intent canceled", e);
+                LogUtil.e(requestId, "Sending pending intent canceled", e);
             }
         }
 
         revokeUriPermission(context);
     }
 
+    private void reportPossibleAnomaly(int result, int httpStatusCode) {
+        switch (result) {
+            case SmsManager.MMS_ERROR_HTTP_FAILURE:
+                if (isPoorSignal()) {
+                    LogUtil.i(this.toString(), "Poor Signal");
+                    break;
+                }
+            case SmsManager.MMS_ERROR_INVALID_APN:
+            case SmsManager.MMS_ERROR_UNABLE_CONNECT_MMS:
+            case SmsManager.MMS_ERROR_UNSPECIFIED:
+            case SmsManager.MMS_ERROR_IO_ERROR:
+                String message = "MMS failed";
+                LogUtil.i(this.toString(),
+                        message + " with error: " + result + " httpStatus:" + httpStatusCode);
+                AnomalyReporter.reportAnomaly(generateUUID(result, httpStatusCode), message);
+                break;
+            default:
+                break;
+        }
+    }
+
+    private UUID generateUUID(int result, int httpStatusCode) {
+        long lresult = result;
+        long lhttpStatusCode = httpStatusCode;
+        return new UUID(mAnomalyUUID.getMostSignificantBits(),
+                mAnomalyUUID.getLeastSignificantBits() + ((lhttpStatusCode << 32) + lresult));
+    }
+
+    private boolean isPoorSignal() {
+        // Check Wifi signal strength when IMS registers via Wifi
+        if (isImsOnWifi()) {
+            int rssi = 0;
+            WifiManager wifiManager = mContext.getSystemService(WifiManager.class);
+            final WifiInfo wifiInfo = wifiManager.getConnectionInfo();
+            if (wifiInfo != null) {
+                rssi = wifiInfo.getRssi();
+            } else {
+                return false;
+            }
+            final int wifiLevel = wifiManager.calculateSignalLevel(rssi);
+            LogUtil.d(this.toString(), "Wifi signal rssi: " + rssi + " level:" + wifiLevel);
+            if (wifiLevel <= SIGNAL_LEVEL_THRESHOLD) {
+                return true;
+            }
+            return false;
+        } else {
+            // Check cellular signal strength
+            final TelephonyManager telephonyManager = mContext.getSystemService(
+                    TelephonyManager.class).createForSubscriptionId(mSubId);
+            final int cellLevel = telephonyManager.getSignalStrength().getLevel();
+            LogUtil.d(this.toString(), "Cellular signal level:" + cellLevel);
+            if (cellLevel <= SIGNAL_LEVEL_THRESHOLD) {
+                return true;
+            }
+            return false;
+        }
+    }
+
+    private boolean isImsOnWifi() {
+        ImsMmTelManager imsManager;
+        try {
+            imsManager = ImsMmTelManager.createForSubscriptionId(mSubId);
+        } catch (IllegalArgumentException e) {
+            LogUtil.e(this.toString(), "invalid subid:" + mSubId);
+            return false;
+        }
+        if (imsManager != null) {
+            return imsManager.isAvailable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+                    ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
+        } else {
+            return false;
+        }
+    }
+
     /**
      * Returns true if sending / downloading using the carrier app has failed and completes the
      * action using platform API's, otherwise false.
@@ -251,7 +403,8 @@
                 == CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK
                 || carrierMessagingAppResult
                         == CarrierMessagingService.DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK) {
-            LogUtil.d(this.toString(), "Sending/downloading MMS by IP failed.");
+            LogUtil.d(this.toString(), "Sending/downloading MMS by IP failed. messageId: "
+                    + mMessageId);
             mRequestManager.addSimRequest(MmsRequest.this);
             return true;
         } else {
@@ -275,7 +428,8 @@
 
     @Override
     public String toString() {
-        return getClass().getSimpleName() + '@' + Integer.toHexString(hashCode());
+        return getClass().getSimpleName() + '@' + Integer.toHexString(hashCode())
+                + " messageId: " + mMessageId;
     }
 
 
@@ -341,20 +495,23 @@
     /**
      * Base class for handling carrier app send / download result.
      */
-    protected abstract class CarrierMmsActionCallback extends CarrierMessagingCallbackWrapper {
+    protected abstract class CarrierMmsActionCallback implements CarrierMessagingCallback {
         @Override
         public void onSendSmsComplete(int result, int messageRef) {
-            LogUtil.e("Unexpected onSendSmsComplete call with result: " + result);
+            LogUtil.e("Unexpected onSendSmsComplete call for messageId " + mMessageId
+                    + " with result: " + result);
         }
 
         @Override
         public void onSendMultipartSmsComplete(int result, int[] messageRefs) {
-            LogUtil.e("Unexpected onSendMultipartSmsComplete call with result: " + result);
+            LogUtil.e("Unexpected onSendMultipartSmsComplete call for messageId " + mMessageId
+                    + " with result: " + result);
         }
 
         @Override
         public void onFilterComplete(int result) {
-            LogUtil.e("Unexpected onFilterComplete call with result: " + result);
+            LogUtil.e("Unexpected onFilterComplete call for messageId " + mMessageId
+                    + " with result: " + result);
         }
     }
 }
diff --git a/src/com/android/mms/service/MmsService.java b/src/com/android/mms/service/MmsService.java
index 66b23ac..eb0fd88 100644
--- a/src/com/android/mms/service/MmsService.java
+++ b/src/com/android/mms/service/MmsService.java
@@ -39,8 +39,10 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Telephony;
+import android.security.NetworkSecurityPolicy;
 import android.service.carrier.CarrierMessagingService;
 import android.telephony.SmsManager;
+import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.data.ApnSetting;
@@ -62,7 +64,10 @@
 
 import java.io.IOException;
 import java.util.ArrayDeque;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 import java.util.Queue;
 import java.util.concurrent.Callable;
@@ -70,6 +75,8 @@
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * System service to process MMS API requests
@@ -91,6 +98,15 @@
     // The default number of threads allowed to run MMS requests in each queue
     public static final int THREAD_POOL_SIZE = 4;
 
+    /** Represents the received SMS message for importing. */
+    public static final int SMS_TYPE_INCOMING = 0;
+    /** Represents the sent SMS message for importing. */
+    public static final int SMS_TYPE_OUTGOING = 1;
+    /** Message status property: whether the message has been seen. */
+    public static final String MESSAGE_STATUS_SEEN = "seen";
+    /** Message status property: whether the message has been read. */
+    public static final String MESSAGE_STATUS_READ = "read";
+
     // Pending requests that are waiting for the SIM to be available
     // If a different SIM is currently used by previous requests, the following
     // requests will stay in this queue until that SIM finishes its current requests in
@@ -171,8 +187,9 @@
     private IMms.Stub mStub = new IMms.Stub() {
         @Override
         public void sendMessage(int subId, String callingPkg, Uri contentUri,
-                String locationUrl, Bundle configOverrides, PendingIntent sentIntent) {
-            LogUtil.d("sendMessage");
+                String locationUrl, Bundle configOverrides, PendingIntent sentIntent,
+                long messageId) {
+            LogUtil.d("sendMessage messageId: " + messageId);
             enforceSystemUid();
 
             // Make sure the subId is correct
@@ -192,7 +209,8 @@
             }
 
             final SendRequest request = new SendRequest(MmsService.this, subId, contentUri,
-                    locationUrl, sentIntent, callingPkg, configOverrides, MmsService.this);
+                    locationUrl, sentIntent, callingPkg, configOverrides, MmsService.this,
+                    messageId);
 
             final String carrierMessagingServicePackage =
                     getCarrierMessagingServicePackageIfExists(subId);
@@ -222,11 +240,12 @@
         @Override
         public void downloadMessage(int subId, String callingPkg, String locationUrl,
                 Uri contentUri, Bundle configOverrides,
-                PendingIntent downloadedIntent) {
+                PendingIntent downloadedIntent, long messageId) {
             // If the subId is no longer active it could be caused by an MVNO using multiple
             // subIds, so we should try to download anyway.
             // TODO: Fail fast when downloading will fail (i.e. SIM swapped)
-            LogUtil.d("downloadMessage: " + MmsHttpClient.redactUrlForNonVerbose(locationUrl));
+            LogUtil.d("downloadMessage: " + MmsHttpClient.redactUrlForNonVerbose(locationUrl) +
+                    ", messageId: " + messageId);
 
             enforceSystemUid();
 
@@ -240,8 +259,27 @@
                 subId = SubscriptionManager.getDefaultSmsSubscriptionId();
             }
 
+            if (!isActiveSubId(subId)) {
+                List<SubscriptionInfo> activeSubList = getActiveSubscriptionsInGroup(subId);
+                if (activeSubList.isEmpty()) {
+                    sendErrorInPendingIntent(downloadedIntent);
+                    return;
+                }
+
+                subId = activeSubList.get(0).getSubscriptionId();
+                int defaultSmsSubId = SubscriptionManager.getDefaultSmsSubscriptionId();
+                // If we have default sms subscription, prefer to use that. Otherwise, use first
+                // subscription
+                for (SubscriptionInfo subInfo : activeSubList) {
+                    if (subInfo.getSubscriptionId() == defaultSmsSubId) {
+                        subId = subInfo.getSubscriptionId();
+                    }
+                }
+            }
+
             final DownloadRequest request = new DownloadRequest(MmsService.this, subId, locationUrl,
-                    contentUri, downloadedIntent, callingPkg, configOverrides, MmsService.this);
+                    contentUri, downloadedIntent, callingPkg, configOverrides, MmsService.this,
+                    messageId);
 
             final String carrierMessagingServicePackage =
                     getCarrierMessagingServicePackageIfExists(subId);
@@ -262,22 +300,46 @@
             addSimRequest(request);
         }
 
-        @Override
-        public Bundle getCarrierConfigValues(int subId) {
-            LogUtil.d("getCarrierConfigValues");
-            // Make sure the subId is correct
-            if (!SubscriptionManager.isValidSubscriptionId(subId)) {
-                LogUtil.e("Invalid subId " + subId);
-                return new Bundle();
+        private List<SubscriptionInfo> getActiveSubscriptionsInGroup(int subId) {
+            SubscriptionManager subManager =
+                    (SubscriptionManager) getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+
+            if (subManager == null) {
+                return Collections.emptyList();
             }
-            if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
-                subId = SubscriptionManager.getDefaultSmsSubscriptionId();
+
+            List<SubscriptionInfo> subList = subManager.getAvailableSubscriptionInfoList();
+
+            if (subList == null) {
+                return Collections.emptyList();
             }
-            final Bundle mmsConfig = MmsConfigManager.getInstance().getMmsConfigBySubId(subId);
-            if (mmsConfig == null) {
-                return new Bundle();
+
+            SubscriptionInfo subscriptionInfo = null;
+            for (SubscriptionInfo subInfo : subList) {
+                if (subInfo.getSubscriptionId() == subId) {
+                    subscriptionInfo = subInfo;
+                    break;
+                }
             }
-            return mmsConfig;
+
+            if (subscriptionInfo == null) {
+                return Collections.emptyList();
+            }
+
+            if (subscriptionInfo.getGroupUuid() == null) {
+                return Collections.emptyList();
+            }
+
+            List<SubscriptionInfo> subscriptionInGroupList =
+                    subManager.getSubscriptionsInGroup(subscriptionInfo.getGroupUuid());
+
+            // the list is sorted by isOpportunistic and isOpportunistic == false will have higher
+            // priority
+            return subscriptionInGroupList.stream()
+                    .filter(info ->
+                            info.getSimSlotIndex() != SubscriptionManager.INVALID_SIM_SLOT_INDEX)
+                    .sorted(Comparator.comparing(SubscriptionInfo::isOpportunistic))
+                    .collect(Collectors.toList());
         }
 
         @Override
@@ -555,6 +617,9 @@
         LogUtil.d("onCreate");
         // Load mms_config
         MmsConfigManager.getInstance().init(this);
+
+        NetworkSecurityPolicy.getInstance().setCleartextTrafficPermitted(true);
+
         // Initialize running request state
         for (int i = 0; i < mRunningRequestExecutors.length; i++) {
             mRunningRequestExecutors[i] = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
@@ -578,11 +643,11 @@
             boolean seen, boolean read, String creator) {
         Uri insertUri = null;
         switch (type) {
-            case SmsManager.SMS_TYPE_INCOMING:
+            case SMS_TYPE_INCOMING:
                 insertUri = Telephony.Sms.Inbox.CONTENT_URI;
 
                 break;
-            case SmsManager.SMS_TYPE_OUTGOING:
+            case SMS_TYPE_OUTGOING:
                 insertUri = Telephony.Sms.Sent.CONTENT_URI;
                 break;
         }
@@ -702,14 +767,14 @@
             return false;
         }
         final ContentValues values = new ContentValues();
-        if (statusValues.containsKey(SmsManager.MESSAGE_STATUS_READ)) {
-            final Integer val = statusValues.getAsInteger(SmsManager.MESSAGE_STATUS_READ);
+        if (statusValues.containsKey(MESSAGE_STATUS_READ)) {
+            final Integer val = statusValues.getAsInteger(MESSAGE_STATUS_READ);
             if (val != null) {
                 // MMS uses the same column name
                 values.put(Telephony.Sms.READ, val);
             }
-        } else if (statusValues.containsKey(SmsManager.MESSAGE_STATUS_SEEN)) {
-            final Integer val = statusValues.getAsInteger(SmsManager.MESSAGE_STATUS_SEEN);
+        } else if (statusValues.containsKey(MESSAGE_STATUS_SEEN)) {
+            final Integer val = statusValues.getAsInteger(MESSAGE_STATUS_SEEN);
             if (val != null) {
                 // MMS uses the same column name
                 values.put(Telephony.Sms.SEEN, val);
diff --git a/src/com/android/mms/service/SendRequest.java b/src/com/android/mms/service/SendRequest.java
index fefbbc0..8fd5185 100644
--- a/src/com/android/mms/service/SendRequest.java
+++ b/src/com/android/mms/service/SendRequest.java
@@ -57,8 +57,9 @@
     private final PendingIntent mSentIntent;
 
     public SendRequest(RequestManager manager, int subId, Uri contentUri, String locationUrl,
-            PendingIntent sentIntent, String creator, Bundle configOverrides, Context context) {
-        super(manager, subId, creator, configOverrides, context);
+            PendingIntent sentIntent, String creator, Bundle configOverrides, Context context,
+            long messageId) {
+        super(manager, subId, creator, configOverrides, context, messageId);
         mPduUri = contentUri;
         mPduData = null;
         mLocationUrl = locationUrl;
@@ -71,8 +72,9 @@
         final String requestId = getRequestId();
         final MmsHttpClient mmsHttpClient = netMgr.getOrCreateHttpClient();
         if (mmsHttpClient == null) {
-            LogUtil.e(requestId, "MMS network is not ready!");
-            throw new MmsHttpException(0/*statusCode*/, "MMS network is not ready");
+            String notReady = "MMS network is not ready! messageId: " + mMessageId;
+            LogUtil.e(requestId, notReady);
+            throw new MmsHttpException(0/*statusCode*/, notReady);
         }
         final GenericPdu parsedPdu = parsePdu();
         notifyIfEmergencyContactNoThrow(parsedPdu);
@@ -93,14 +95,14 @@
         final String requestId = getRequestId();
         try {
             if (mPduData == null) {
-                LogUtil.w(requestId, "Empty PDU raw data");
+                LogUtil.w(requestId, "Empty PDU raw data. messageId: " + mMessageId);
                 return null;
             }
             final boolean supportContentDisposition =
                     mMmsConfig.getBoolean(SmsManager.MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION);
             return new PduParser(mPduData, supportContentDisposition).parse();
         } catch (final Exception e) {
-            LogUtil.w(requestId, "Failed to parse PDU raw data");
+            LogUtil.w(requestId, "Failed to parse PDU raw data. messageId: " + mMessageId);
         }
         return null;
     }
@@ -113,7 +115,7 @@
         try {
             notifyIfEmergencyContact(parsedPdu);
         } catch (Exception e) {
-            LogUtil.w(getRequestId(), "Error in notifyIfEmergencyContact", e);
+            LogUtil.w(getRequestId(), "Error in notifyIfEmergencyContact. messageId: " + mMessageId, e);
         }
     }
 
@@ -122,15 +124,18 @@
             SendReq sendReq = (SendReq) parsedPdu;
             for (EncodedStringValue encodedStringValue : sendReq.getTo()) {
                 if (isEmergencyNumber(encodedStringValue.getString())) {
-                    LogUtil.i(getRequestId(), "Notifying emergency contact");
+                    LogUtil.i(getRequestId(), "Notifying emergency contact. messageId: "
+                            + mMessageId);
                     new AsyncTask<Void, Void, Void>() {
                         @Override
                         protected Void doInBackground(Void... voids) {
                             try {
-                                notifyEmergencyContact(mContext);
+                                BlockedNumberContract.SystemContract
+                                        .notifyEmergencyContact(mContext);
                             } catch (Exception e) {
                                 LogUtil.e(getRequestId(),
-                                    "Exception notifying emergency contact: " + e);
+                                    "Exception notifying emergency contact. messageId: "
+                                            + mMessageId + e);
                             }
                             return null;
                         }
@@ -167,9 +172,9 @@
             // Not required to persist
             return null;
         }
-        LogUtil.d(requestId, "persistIfRequired");
+        LogUtil.d(requestId, "persistIfRequired. messageId: " + mMessageId);
         if (mPduData == null) {
-            LogUtil.e(requestId, "persistIfRequired: empty PDU");
+            LogUtil.e(requestId, "persistIfRequired: empty PDU. messageId: " + mMessageId);
             return null;
         }
         final long identity = Binder.clearCallingIdentity();
@@ -179,11 +184,12 @@
             // Persist the request PDU first
             GenericPdu pdu = (new PduParser(mPduData, supportContentDisposition)).parse();
             if (pdu == null) {
-                LogUtil.e(requestId, "persistIfRequired: can't parse input PDU");
+                LogUtil.e(requestId, "persistIfRequired: can't parse input PDU. messageId: "
+                        + mMessageId);
                 return null;
             }
             if (!(pdu instanceof SendReq)) {
-                LogUtil.d(requestId, "persistIfRequired: not SendReq");
+                LogUtil.d(requestId, "persistIfRequired: not SendReq. messageId: " + mMessageId);
                 return null;
             }
             final PduPersister persister = PduPersister.getPduPersister(context);
@@ -194,7 +200,8 @@
                     true/*groupMmsEnabled*/,
                     null/*preOpenedFiles*/);
             if (messageUri == null) {
-                LogUtil.e(requestId, "persistIfRequired: can not persist message");
+                LogUtil.e(requestId, "persistIfRequired: can not persist message. messageId: "
+                        + mMessageId);
                 return null;
             }
             // Update the additional columns based on the send result
@@ -232,13 +239,16 @@
             values.put(Telephony.Mms.SUBSCRIPTION_ID, mSubId);
             if (SqliteWrapper.update(context, context.getContentResolver(), messageUri, values,
                     null/*where*/, null/*selectionArg*/) != 1) {
-                LogUtil.e(requestId, "persistIfRequired: failed to update message");
+                LogUtil.e(requestId, "persistIfRequired: failed to update message. messageId: "
+                        + mMessageId);
             }
             return messageUri;
         } catch (MmsException e) {
-            LogUtil.e(requestId, "persistIfRequired: can not persist message", e);
+            LogUtil.e(requestId, "persistIfRequired: can not persist message. messageId: "
+                    + mMessageId, e);
         } catch (RuntimeException e) {
-            LogUtil.e(requestId, "persistIfRequired: unexpected parsing failure", e);
+            LogUtil.e(requestId, "persistIfRequired: unexpected parsing failure. messageId: "
+                    + mMessageId, e);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -253,11 +263,12 @@
     private void updateDestinationAddress(final GenericPdu pdu) {
         final String requestId = getRequestId();
         if (pdu == null) {
-            LogUtil.e(requestId, "updateDestinationAddress: can't parse input PDU");
+            LogUtil.e(requestId, "updateDestinationAddress: can't parse input PDU. messageId: "
+                    + mMessageId);
             return ;
         }
         if (!(pdu instanceof SendReq)) {
-            LogUtil.i(requestId, "updateDestinationAddress: not SendReq");
+            LogUtil.i(requestId, "updateDestinationAddress: not SendReq. messageId: " + mMessageId);
             return;
         }
 
@@ -382,33 +393,43 @@
     /**
      * Sends the MMS through through the carrier app.
      */
-    private final class CarrierSendManager extends CarrierMessagingServiceWrapper {
+    private final class CarrierSendManager {
         // Initialized in sendMms
         private volatile CarrierSendCompleteCallback mCarrierSendCompleteCallback;
+        private final CarrierMessagingServiceWrapper mCarrierMessagingServiceWrapper =
+                new CarrierMessagingServiceWrapper();
+
+        void disposeConnection(Context context) {
+            mCarrierMessagingServiceWrapper.disposeConnection(context);
+        }
 
         void sendMms(Context context, String carrierMessagingServicePackage,
                 CarrierSendCompleteCallback carrierSendCompleteCallback) {
             mCarrierSendCompleteCallback = carrierSendCompleteCallback;
-            if (bindToCarrierMessagingService(context, carrierMessagingServicePackage)) {
-                LogUtil.v("bindService() for carrier messaging service succeeded");
+            if (mCarrierMessagingServiceWrapper.bindToCarrierMessagingService(
+                    context, carrierMessagingServicePackage, () -> onServiceReady())) {
+                LogUtil.v("bindService() for carrier messaging service succeeded. messageId: "
+                        + mMessageId);
             } else {
-                LogUtil.e("bindService() for carrier messaging service failed");
+                LogUtil.e("bindService() for carrier messaging service failed. messageId: "
+                        + mMessageId);
                 carrierSendCompleteCallback.onSendMmsComplete(
                         CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
                         null /* no sendConfPdu */);
             }
         }
 
-        @Override
-        public void onServiceReady() {
+        private void onServiceReady() {
             try {
                 Uri locationUri = null;
                 if (mLocationUrl != null) {
                     locationUri = Uri.parse(mLocationUrl);
                 }
-                sendMms(mPduUri, mSubId, locationUri, mCarrierSendCompleteCallback);
+                mCarrierMessagingServiceWrapper.sendMms(
+                        mPduUri, mSubId, locationUri, mCarrierSendCompleteCallback);
             } catch (RuntimeException e) {
-                LogUtil.e("Exception sending MMS using the carrier messaging service: " + e, e);
+                LogUtil.e("Exception sending MMS using the carrier messaging service. messageId: "
+                        + mMessageId + e, e);
                 mCarrierSendCompleteCallback.onSendMmsComplete(
                         CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
                         null /* no sendConfPdu */);
@@ -432,35 +453,19 @@
 
         @Override
         public void onSendMmsComplete(int result, byte[] sendConfPdu) {
-            LogUtil.d("Carrier app result for send: " + result);
+            LogUtil.d("Carrier app result for sending messageId " + mMessageId + ": " + result);
             mCarrierSendManager.disposeConnection(mContext);
 
             if (!maybeFallbackToRegularDelivery(result)) {
                 processResult(mContext, toSmsManagerResult(result), sendConfPdu,
-                        0/* httpStatusCode */);
+                        0/* httpStatusCode */, /* handledByCarrierApp= */ true);
             }
         }
 
         @Override
         public void onDownloadMmsComplete(int result) {
-            LogUtil.e("Unexpected onDownloadMmsComplete call with result: " + result);
-        }
-    }
-
-    /**
-     * Notifies the provider that emergency services were contacted by the user.
-     */
-    private void notifyEmergencyContact(Context context) {
-        try {
-            LogUtil.i("notifyEmergencyContact; caller=%s", context.getOpPackageName());
-            context.getContentResolver().call(
-                    BlockedNumberContract.AUTHORITY_URI,
-                    BlockedNumberContract.METHOD_NOTIFY_EMERGENCY_CONTACT,
-                    null, null);
-        } catch (NullPointerException | IllegalArgumentException ex) {
-            // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
-            // either of these happen.
-            LogUtil.w(null, "notifyEmergencyContact: provider not ready.");
+            LogUtil.e("Unexpected onDownloadMmsComplete call for messageId " + mMessageId
+                    + " with result: " + result);
         }
     }
 }
diff --git a/tests/robotests/Android.bp b/tests/robotests/Android.bp
index e983542..1b170c0 100644
--- a/tests/robotests/Android.bp
+++ b/tests/robotests/Android.bp
@@ -2,6 +2,10 @@
 // MmsService Robolectric test target.                       #
 //############################################################
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 android_robolectric_test {
     name: "MmsServiceRoboTests",
 
@@ -9,10 +13,10 @@
 
     java_resource_dirs: ["config"],
 
-    static_libs: ["testng"],
+    static_libs: [
+        "androidx.test.core",
+        "testng",
+    ],
 
     instrumentation_for: "MmsService",
 }
-
-
-
diff --git a/tests/robotests/src/com/android/mms/service/ApnSettingsTest.java b/tests/robotests/src/com/android/mms/service/ApnSettingsTest.java
new file mode 100644
index 0000000..b184e16
--- /dev/null
+++ b/tests/robotests/src/com/android/mms/service/ApnSettingsTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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.mms.service;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.Telephony;
+import android.telephony.data.ApnSetting;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.mms.service.exception.ApnException;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.fakes.RoboCursor;
+import org.robolectric.shadows.ShadowContentResolver;
+
+import java.util.Arrays;
+
+@RunWith(RobolectricTestRunner.class)
+public final class ApnSettingsTest {
+
+    private Context context;
+
+    @Before
+    public void setUp() {
+        context = ApplicationProvider.getApplicationContext();
+    }
+
+    @Test
+    public void load_noApnSettings_throwsApnException() {
+        assertThrows(ApnException.class,
+                () -> ApnSettings.load(context, "apnName", /* subId= */ 0, "requestId"));
+    }
+
+    @Test
+    public void getApnSettingsFromCursor_validSettings_correctApnSettingsLoaded() throws Exception {
+        createApnSettingsCursor("mmscUrl", "mmsProxy", /* proxyPort= */ "123");
+        ApnSettings apnSettings = ApnSettings.load(context, "apnName", /* subId= */ 0, "requestId");
+
+        assertThat(apnSettings.getProxyPort()).isEqualTo(123);
+        assertThat(apnSettings.getMmscUrl()).isEqualTo("mmscUrl");
+        assertThat(apnSettings.getProxyAddress()).isEqualTo("mmsProxy");
+    }
+
+    @Test
+    public void getApnSettingsFromCursor_nullMmsPort_defaultProxyPortUsed() throws Exception {
+        createApnSettingsCursor("mmscUrl", "mmsProxy", /* proxyPort= */ null);
+        ApnSettings apnSettings = ApnSettings.load(context, "apnName", /* subId= */ 0, "requestId");
+        assertThat(apnSettings.getProxyPort()).isEqualTo(80);
+    }
+
+    @Test
+    public void getApnSettingsFromCursor_emptyMmsPort_defaultProxyPortUsed() throws Exception {
+        createApnSettingsCursor("mmscUrl", "mmsProxy",
+                /* proxyPort= */ "");
+        ApnSettings apnSettings = ApnSettings.load(context, "apnName", /* subId= */ 0, "requestId");
+        assertThat(apnSettings.getProxyPort()).isEqualTo(80);
+    }
+
+    private void createApnSettingsCursor(String mmscUrl, String mmsProxy, String proxyPort) {
+        Object[][] apnValues =
+                {new Object[]{ApnSetting.TYPE_MMS_STRING, mmscUrl, mmsProxy, proxyPort}};
+        RoboCursor cursor = new RoboCursor();
+        cursor.setResults(apnValues);
+        cursor.setColumnNames(Arrays.asList(Telephony.Carriers.TYPE, Telephony.Carriers.MMSC,
+                Telephony.Carriers.MMSPROXY, Telephony.Carriers.MMSPORT));
+
+        ShadowContentResolver.registerProviderInternal(
+                Telephony.Carriers.CONTENT_URI.getAuthority(), new FakeApnSettingsProvider(cursor));
+    }
+
+    private final class FakeApnSettingsProvider extends ContentProvider {
+
+        private final Cursor cursor;
+
+        FakeApnSettingsProvider(Cursor cursor) {
+            this.cursor = cursor;
+        }
+
+        @Override
+        public boolean onCreate() {
+            return false;
+        }
+
+        @Override
+        public Cursor query(Uri uri, String[] projection,
+                String selection, String[] selectionArgs, String sortOrder) {
+            return cursor;
+        }
+
+        @Override
+        public String getType(Uri uri) {
+            return null;
+        }
+
+        @Override
+        public Uri insert(Uri uri, ContentValues values) {
+            return null;
+        }
+
+        @Override
+        public int delete(Uri uri, String selection, String[] selectionArgs) {
+            return 0;
+        }
+
+        @Override
+        public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+            return 0;
+        }
+    }
+
+}
diff --git a/tests/robotests/src/com/android/mms/service/MmsServiceRoboTest.java b/tests/robotests/src/com/android/mms/service/MmsServiceRoboTest.java
index 5e4c309..c43fa74 100644
--- a/tests/robotests/src/com/android/mms/service/MmsServiceRoboTest.java
+++ b/tests/robotests/src/com/android/mms/service/MmsServiceRoboTest.java
@@ -56,13 +56,15 @@
     public void testSendMessage_DoesNotThrowIfSystemUid() throws RemoteException {
         ShadowBinder.setCallingUid(Process.SYSTEM_UID);
         binder.sendMessage(/* subId= */ 0, "callingPkg", Uri.parse("contentUri"),
-                "locationUrl", /* configOverrides= */ null, /* sentIntent= */ null);
+                "locationUrl", /* configOverrides= */ null, /* sentIntent= */ null,
+                /* messageId= */ 0L);
     }
 
     @Test
     public void testSendMessageThrows_IfNotSystemUid() {
         assertThrows(SecurityException.class,
                 () -> binder.sendMessage(/* subId= */ 0, "callingPkg", Uri.parse("contentUri"),
-                        "locationUrl", /* configOverrides= */ null, /* sentIntent= */ null));
+                        "locationUrl", /* configOverrides= */ null, /* sentIntent= */ null,
+                        /* messageId= */ 0L));
     }
 }