Wait for IPv4 provisioned when connecting to IPv4 MMS URL

T-Mobile's MMS network is IPv6 based. Sometimes MMS URL is provisioned
as static IPv4 address, e.g for "unrecognized" devices. In that case,
IPv4 provisioning takes some time. MMS service needs to wait for it
before making connection.

b/27245530

Change-Id: I62e370a1e6abd668f0b1333664aadf87583045dd
diff --git a/src/com/android/mms/service/MmsHttpClient.java b/src/com/android/mms/service/MmsHttpClient.java
index b4d3ba6..1c88bbf 100644
--- a/src/com/android/mms/service/MmsHttpClient.java
+++ b/src/com/android/mms/service/MmsHttpClient.java
@@ -17,6 +17,8 @@
 package com.android.mms.service;
 
 import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.LinkProperties;
 import android.net.Network;
 import android.os.Bundle;
 import android.telephony.SmsManager;
@@ -25,7 +27,6 @@
 import android.text.TextUtils;
 import android.util.Base64;
 import android.util.Log;
-
 import com.android.mms.service.exception.MmsHttpException;
 
 import java.io.BufferedInputStream;
@@ -36,6 +37,8 @@
 import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
 import java.net.HttpURLConnection;
+import java.net.Inet4Address;
+import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.MalformedURLException;
 import java.net.ProtocolException;
@@ -68,18 +71,24 @@
     private static final String HEADER_VALUE_CONTENT_TYPE_WITHOUT_CHARSET =
             "application/vnd.wap.mms-message";
 
+    private static final int IPV4_WAIT_ATTEMPTS = 15;
+    private static final long IPV4_WAIT_DELAY_MS = 1000; // 1 seconds
+
     private final Context mContext;
     private final Network mNetwork;
+    private final ConnectivityManager mConnectivityManager;
 
     /**
      * Constructor
-     *
-     * @param context The Context object
+     *  @param context The Context object
      * @param network The Network for creating an OKHttp client
+     * @param connectivityManager
      */
-    public MmsHttpClient(Context context, Network network) {
+    public MmsHttpClient(Context context, Network network,
+            ConnectivityManager connectivityManager) {
         mContext = context;
         mNetwork = network;
+        mConnectivityManager = connectivityManager;
     }
 
     /**
@@ -113,6 +122,7 @@
                         new InetSocketAddress(mNetwork.getByName(proxyHost), proxyPort));
             }
             final URL url = new URL(urlString);
+            maybeWaitForIpv4(requestId, url);
             // Now get the connection
             connection = (HttpURLConnection) mNetwork.openConnection(url, proxy);
             connection.setDoInput(true);
@@ -209,6 +219,40 @@
         }
     }
 
+    private void maybeWaitForIpv4(final String requestId, final URL url) {
+        // If it's a literal IPv4 address and we're on an IPv6-only network,
+        // wait until IPv4 is available.
+        Inet4Address ipv4Literal = null;
+        try {
+            ipv4Literal = (Inet4Address) InetAddress.parseNumericAddress(url.getHost());
+        } catch (IllegalArgumentException | ClassCastException e) {
+            // Ignore
+        }
+        if (ipv4Literal == null) {
+            // Not an IPv4 address.
+            return;
+        }
+        for (int i = 0; i < IPV4_WAIT_ATTEMPTS; i++) {
+            final LinkProperties lp = mConnectivityManager.getLinkProperties(mNetwork);
+            if (lp != null) {
+                if (!lp.isReachable(ipv4Literal)) {
+                    LogUtil.w(requestId, "HTTP: IPv4 not yet provisioned");
+                    try {
+                        Thread.sleep(IPV4_WAIT_DELAY_MS);
+                    } catch (InterruptedException e) {
+                        // Ignore
+                    }
+                } else {
+                    LogUtil.i(requestId, "HTTP: IPv4 provisioned");
+                    break;
+                }
+            } else {
+                LogUtil.w(requestId, "HTTP: network disconnected, skip ipv4 check");
+                break;
+            }
+        }
+    }
+
     private static void logHttpHeaders(Map<String, List<String>> headers, String requestId) {
         final StringBuilder sb = new StringBuilder();
         if (headers != null) {
diff --git a/src/com/android/mms/service/MmsNetworkManager.java b/src/com/android/mms/service/MmsNetworkManager.java
index ed78258..67b0af6 100644
--- a/src/com/android/mms/service/MmsNetworkManager.java
+++ b/src/com/android/mms/service/MmsNetworkManager.java
@@ -229,7 +229,7 @@
             if (mMmsHttpClient == null) {
                 if (mNetwork != null) {
                     // Create new MmsHttpClient for the current Network
-                    mMmsHttpClient = new MmsHttpClient(mContext, mNetwork);
+                    mMmsHttpClient = new MmsHttpClient(mContext, mNetwork, mConnectivityManager);
                 }
             }
             return mMmsHttpClient;
diff --git a/src/com/android/mms/service/SendRequest.java b/src/com/android/mms/service/SendRequest.java
index 435e12c..21cd43c 100644
--- a/src/com/android/mms/service/SendRequest.java
+++ b/src/com/android/mms/service/SendRequest.java
@@ -242,7 +242,7 @@
             return ;
         }
         if (!(pdu instanceof SendReq)) {
-            LogUtil.e(requestId, "updateDestinationAddress: not SendReq");
+            LogUtil.i(requestId, "updateDestinationAddress: not SendReq");
             return;
         }