Android 12(S) IPV4优先IPV6(优先使用IPv4地址)的实现

根据RFC 6724中 规定 android 会优先选择IPv6 地址而不是 IPv4 地址,当整个网络中,同时支持IPv4和IPv6 地址时,设备中的应用请求服务器DNS时,会优先返回IPv6地址。

假如IPv6服务器支持内容不够完善,则应用显示内容会与IPv4服务器不一致,甚至有问题。

因此有需求是定制设备平台,使得 IPV4优先IPV6,主要修改2个地方:

1. 当设备或平台连接网络时,原来是先请求ipv6地址,再请求ipv4地址,这样会使用应用会先拿到ipv6地址,所以需要调整顺序,让ipv4先请求dhcp地址,再请求ipv6。


2. DNS域名解析时,调整解析DNS的优先顺序,即先解析IPv4地址,再解析IPv6地址,注:android 12在DnsResolver代码中。


接下来对这2个修改点进行逐一修改并说明。

1. 当设备或平台连接网络时,原来是先请求ipv6地址,再请求ipv4地址,这样会使用应用会先拿到ipv6地址,所以需要调整顺序,让ipv4先请求dhcp地址,再请求ipv6。

文件:packages/modules/NetworkStack/src/android/net/ip/IpClient.java

修改差异如下:

--- a/modules/NetworkStack/src/android/net/ip/IpClient.java
+++ b/modules/NetworkStack/src/android/net/ip/IpClient.java
@@ -161,6 +161,8 @@
     private final NetworkInformationShim mShim = NetworkInformationShimImpl.newInstance();
     private final IpProvisioningMetrics mIpProvisioningMetrics = new IpProvisioningMetrics();
     private final NetworkQuirkMetrics mNetworkQuirkMetrics;
+    //CN project let get ipv4 first and then ipv6, tiangui.tang @2023.9.7
//用于区别自定义code与AOSP
+    private final boolean bStartIPv6AfterIPv4 = true;
 
     /**
      * Dump all state machine and connectivity packet logs to the specified writer.
@@ -1734,7 +1736,21 @@
         recordMetric(failureType);
         mCallback.onProvisioningFailure(mLinkProperties);
     }
+    private void enqueueJumpToStoppingState(final DisconnectCode code) {
+        deferMessage(obtainMessage(CMD_JUMP_RUNNING_TO_STOPPING, code.getNumber()));
+    }
+    private void startIPv6AfterIPv4() {
+        if (bStartIPv6AfterIPv4 == false) {
+            return;
+        }
+        Log.d(mTag, "startIPv6AfterIPv4");
 
+        if (mConfiguration.mEnableIPv6 && !startIPv6()) {
+            doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6);
+            enqueueJumpToStoppingState(DisconnectCode.DC_ERROR_STARTING_IPV6);
+            return;
+        }
+    }
     private boolean startIPv4() {
         // If we have a StaticIpConfiguration attempt to apply it and
         // handle the result accordingly.
@@ -1744,6 +1760,7 @@
             } else {
                 return false;
             }
+            startIPv6AfterIPv4();
         } else {
             if (mDhcpClient != null) {
                 Log.wtf(mTag, "DhcpClient should never be non-null in startIPv4()");
@@ -2229,7 +2246,7 @@
             mPacketTracker = createPacketTracker();
             if (mPacketTracker != null) mPacketTracker.start(mConfiguration.mDisplayName);
 
-            if (mConfiguration.mEnableIPv6 && !startIPv6()) {
+            if ((bStartIPv6AfterIPv4 == false) && mConfiguration.mEnableIPv6 && !startIPv6()) {
                 doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6);
                 enqueueJumpToStoppingState(DisconnectCode.DC_ERROR_STARTING_IPV6);
                 return;
@@ -2414,6 +2431,7 @@
                 }
 
                 case EVENT_DHCPACTION_TIMEOUT:
+                    startIPv6AfterIPv4();
                     stopDhcpAction();
                     break;
 
@@ -2431,6 +2449,7 @@
 
                 case DhcpClient.CMD_CONFIGURE_LINKADDRESS: {
                     final LinkAddress ipAddress = (LinkAddress) msg.obj;
+                    startIPv6AfterIPv4();
                     if (mInterfaceCtrl.setIPv4Address(ipAddress)) {
                         mDhcpClient.sendMessage(DhcpClient.EVENT_LINKADDRESS_CONFIGURED);
                     } else {


2. DNS域名解析时,调整解析DNS的优先顺序,即先解析IPv4地址,再解析IPv6地址,注:android 12在DnsResolver代码中。

diff --git a/modules/DnsResolver/getaddrinfo.cpp b/modules/DnsResolver/getaddrinfo.cpp

--- a/modules/DnsResolver/getaddrinfo.cpp
+++ b/modules/DnsResolver/getaddrinfo.cpp
@@ -1147,7 +1147,8 @@
             return 1;
         } else {
             /* All other IPv6 addresses, including global unicast addresses. */
//DNS解析出来的地址进行排序,IPv4(return 35) 比IPv6(改为return 34)更高优先级
-            return 40;
+            LOG(DEBUG) << __func__ << " ttgctt force 34 for ipv6";
+            return 34;
         }
     } else {
         return 1;
@@ -1406,16 +1407,17 @@
                 query_ipv6 = have_ipv6(netcontext->app_mark, netcontext->uid);
                 query_ipv4 = have_ipv4(netcontext->app_mark, netcontext->uid);
             }
//调整顺序,先请求IPv4对应的DNS,再请求IPv6
-            if (query_ipv6) {
-                q.qtype = T_AAAA;
-                if (query_ipv4) {
+            //query_ipv6 = 0;
+            if (query_ipv4) {
+                q.qtype = T_A;
+                if (query_ipv6) {
                     q.next = &q2;
                     q2.name = name;
                     q2.qclass = C_IN;
-                    q2.qtype = T_A;
+                    q2.qtype = T_AAAA;
                 }
-            } else if (query_ipv4) {
-                q.qtype = T_A;
+            } else if (query_ipv6) {
+                q.qtype = T_AAAA;
             } else { 
                 return EAI_NODATA;
             }
@@ -1452,7 +1454,7 @@
         cur->ai_next = ai;
         while (cur && cur->ai_next) cur = cur->ai_next;
     }
     if (q.next) {
         ai = getanswer(q2.answer, q2.n, q2.name, q2.qtype, pai, &he);
         if (ai) cur->ai_next = ai;
     }