Merge "Use "don't actively prefer" timeout when avoiding bad wifi"
diff --git a/bpf_progs/netd.c b/bpf_progs/netd.c
index 839ca40..245ad7a 100644
--- a/bpf_progs/netd.c
+++ b/bpf_progs/netd.c
@@ -412,10 +412,8 @@
 
     // Always allow and never count clat traffic. Only the IPv4 traffic on the stacked
     // interface is accounted for and subject to usage restrictions.
-    // TODO: remove sock_uid check once Nat464Xlat javaland adds the socket tag AID_CLAT for clat.
-    if (sock_uid == AID_CLAT || uid == AID_CLAT) {
-        return PASS;
-    }
+    // CLAT IPv6 TX sockets are *always* tagged with CLAT uid, see tagSocketAsClat()
+    if (uid == AID_CLAT) return PASS;
 
     int match = bpf_owner_match(skb, sock_uid, egress, kver);
 
@@ -441,17 +439,17 @@
     uint32_t mapSettingKey = CURRENT_STATS_MAP_CONFIGURATION_KEY;
     uint32_t* selectedMap = bpf_configuration_map_lookup_elem(&mapSettingKey);
 
-    // Use asm("%0 &= 1" : "+r"(match)) before return match,
-    // to help kernel's bpf verifier, so that it can be 100% certain
-    // that the returned value is always BPF_NOMATCH(0) or BPF_MATCH(1).
-    if (!selectedMap) {
-        asm("%0 &= 1" : "+r"(match));
-        return match;
-    }
+    if (!selectedMap) return PASS;  // cannot happen, needed to keep bpf verifier happy
 
     do_packet_tracing(skb, egress, uid, tag, enable_tracing, kver);
     update_stats_with_config(*selectedMap, skb, &key, egress, kver);
     update_app_uid_stats_map(skb, &uid, egress, kver);
+
+    // We've already handled DROP_UNLESS_DNS up above, thus when we reach here the only
+    // possible values of match are DROP(0) or PASS(1), however we need to use
+    // "match &= 1" before 'return match' to help the kernel's bpf verifier,
+    // so that it can be 100% certain that the returned value is always 0 or 1.
+    // We use assembly so that it cannot be optimized out by a too smart compiler.
     asm("%0 &= 1" : "+r"(match));
     return match;
 }
@@ -502,9 +500,8 @@
     // Clat daemon does not generate new traffic, all its traffic is accounted for already
     // on the v4-* interfaces (except for the 20 (or 28) extra bytes of IPv6 vs IPv4 overhead,
     // but that can be corrected for later when merging v4-foo stats into interface foo's).
-    // TODO: remove sock_uid check once Nat464Xlat javaland adds the socket tag AID_CLAT for clat.
+    // CLAT sockets are created by system server and tagged as uid CLAT, see tagSocketAsClat()
     uint32_t sock_uid = bpf_get_socket_uid(skb);
-    if (sock_uid == AID_CLAT) return BPF_NOMATCH;
     if (sock_uid == AID_SYSTEM) {
         uint64_t cookie = bpf_get_socket_cookie(skb);
         UidTagValue* utag = bpf_cookie_tag_map_lookup_elem(&cookie);
diff --git a/framework/src/android/net/ConnectivitySettingsManager.java b/framework/src/android/net/ConnectivitySettingsManager.java
index 822e67d..67dacb8 100644
--- a/framework/src/android/net/ConnectivitySettingsManager.java
+++ b/framework/src/android/net/ConnectivitySettingsManager.java
@@ -28,6 +28,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.net.ConnectivityManager.MultipathPreference;
 import android.os.Binder;
 import android.os.Build;
@@ -36,6 +37,7 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.ArraySet;
+import android.util.Log;
 import android.util.Range;
 
 import com.android.net.module.util.ConnectivitySettingsUtils;
@@ -55,6 +57,7 @@
  */
 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
 public class ConnectivitySettingsManager {
+    private static final String TAG = ConnectivitySettingsManager.class.getSimpleName();
 
     private ConnectivitySettingsManager() {}
 
@@ -696,10 +699,20 @@
     /**
      * Set global http proxy settings from given {@link ProxyInfo}.
      *
+     * <p class="note">
+     * While a {@link ProxyInfo} for a PAC proxy can be specified, not all devices support
+     * PAC proxies. In particular, smaller devices like watches often do not have the capabilities
+     * necessary to interpret the PAC file. In such cases, calling this API with a PAC proxy
+     * results in undefined behavior, including possibly breaking networking for applications.
+     * You can test for this by checking for the presence of {@link PackageManager.FEATURE_WEBVIEW}.
+     * </p>
+     *
      * @param context The {@link Context} to set the setting.
      * @param proxyInfo The {@link ProxyInfo} for global http proxy settings which build from
      *                    {@link ProxyInfo#buildPacProxy(Uri)} or
      *                    {@link ProxyInfo#buildDirectProxy(String, int, List)}
+     * @throws UnsupportedOperationException if |proxyInfo| codes for a PAC proxy but the system
+     *                                       does not support PAC proxies.
      */
     public static void setGlobalProxy(@NonNull Context context, @NonNull ProxyInfo proxyInfo) {
         final String host = proxyInfo.getHost();
@@ -707,6 +720,14 @@
         final String exclusionList = proxyInfo.getExclusionListAsString();
         final String pacFileUrl = proxyInfo.getPacFileUrl().toString();
 
+
+        if (!TextUtils.isEmpty(pacFileUrl)) {
+            final PackageManager pm = context.getPackageManager();
+            if (null != pm && !pm.hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) {
+                Log.wtf(TAG, "PAC proxy can't be installed on a device without FEATURE_WEBVIEW");
+            }
+        }
+
         if (TextUtils.isEmpty(pacFileUrl)) {
             Settings.Global.putString(context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST, host);
             Settings.Global.putInt(context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, port);
diff --git a/framework/src/android/net/LinkProperties.java b/framework/src/android/net/LinkProperties.java
index 4f7ac30..9fb9bc6 100644
--- a/framework/src/android/net/LinkProperties.java
+++ b/framework/src/android/net/LinkProperties.java
@@ -1456,8 +1456,13 @@
      * @hide
      */
     public boolean isIdenticalPcscfs(@NonNull LinkProperties target) {
-        // list order is important, compare one by one
-        return target.getPcscfServers().equals(mPcscfs);
+        // Per 3GPP TS 24.229, B.2.2.1 PDP context activation and P-CSCF discovery
+        // list order is important, so on U+ compare one by one
+        if (SdkLevel.isAtLeastU()) return target.getPcscfServers().equals(mPcscfs);
+        // but for safety old behaviour on pre-U:
+        Collection<InetAddress> targetPcscfs = target.getPcscfServers();
+        return (mPcscfs.size() == targetPcscfs.size()) ?
+                    mPcscfs.containsAll(targetPcscfs) : false;
     }
 
     /**
diff --git a/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java b/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java
index 40191cf..451909c 100644
--- a/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java
+++ b/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java
@@ -61,13 +61,11 @@
         final StructIfaddrMsg ifaddrMsg = msg.getIfaddrHeader();
         final LinkAddress la = new LinkAddress(msg.getIpAddress(), ifaddrMsg.prefixLen,
                 msg.getFlags(), ifaddrMsg.scope);
-        if (!la.isPreferred()) {
-            // Skip the unusable ip address.
-            return;
-        }
         switch (msg.getHeader().nlmsg_type) {
             case NetlinkConstants.RTM_NEWADDR:
-                mCb.addOrUpdateInterfaceAddress(ifaddrMsg.index, la);
+                if (la.isPreferred()) {
+                    mCb.addOrUpdateInterfaceAddress(ifaddrMsg.index, la);
+                }
                 break;
             case NetlinkConstants.RTM_DELADDR:
                 mCb.deleteInterfaceAddress(ifaddrMsg.index, la);
diff --git a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
index e2ef981..f8285ed 100644
--- a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
+++ b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
@@ -210,8 +210,12 @@
             this.mKi = Objects.requireNonNull(ki);
             mCallback = ki.mCallback;
             mUnderpinnedNetwork = underpinnedNetwork;
-            if (autoOnOff && mDependencies.isFeatureEnabled(AUTOMATIC_ON_OFF_KEEPALIVE_VERSION,
-                    true /* defaultEnabled */)) {
+            // Reading DeviceConfig will check if the calling uid and calling package name are the
+            // same. Clear calling identity to align the calling uid and package
+            final boolean enabled = BinderUtils.withCleanCallingIdentity(
+                    () -> mDependencies.isFeatureEnabled(AUTOMATIC_ON_OFF_KEEPALIVE_VERSION,
+                            true /* defaultEnabled */));
+            if (autoOnOff && enabled) {
                 mAutomaticOnOffState = STATE_ENABLED;
                 if (null == ki.mFd) {
                     throw new IllegalArgumentException("fd can't be null with automatic "
@@ -583,8 +587,12 @@
      */
     public void dump(IndentingPrintWriter pw) {
         mKeepaliveTracker.dump(pw);
-        final boolean featureEnabled = mDependencies.isFeatureEnabled(
-                AUTOMATIC_ON_OFF_KEEPALIVE_VERSION, true /* defaultEnabled */);
+        // Reading DeviceConfig will check if the calling uid and calling package name are the same.
+        // Clear calling identity to align the calling uid and package so that it won't fail if cts
+        // would like to call dump()
+        final boolean featureEnabled = BinderUtils.withCleanCallingIdentity(
+                () -> mDependencies.isFeatureEnabled(AUTOMATIC_ON_OFF_KEEPALIVE_VERSION,
+                        true /* defaultEnabled */));
         pw.println("AutomaticOnOff enabled: " + featureEnabled);
         pw.increaseIndent();
         for (AutomaticOnOffKeepalive autoKi : mAutomaticOnOffKeepalives) {
@@ -837,12 +845,8 @@
          * @return whether the feature is enabled
          */
         public boolean isFeatureEnabled(@NonNull final String name, final boolean defaultEnabled) {
-            // Reading DeviceConfig will check if the calling uid and calling package name are the
-            // same. Clear calling identity to align the calling uid and package so that it won't
-            // fail if cts would like to do the dump()
-            return BinderUtils.withCleanCallingIdentity(() ->
-                    DeviceConfigUtils.isFeatureEnabled(mContext, NAMESPACE_TETHERING, name,
-                    DeviceConfigUtils.TETHERING_MODULE_NAME, defaultEnabled));
+            return DeviceConfigUtils.isFeatureEnabled(mContext, NAMESPACE_TETHERING, name,
+                    DeviceConfigUtils.TETHERING_MODULE_NAME, defaultEnabled);
         }
 
         /**
diff --git a/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java b/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java
index d8a0b07..83b9b81 100644
--- a/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java
@@ -799,7 +799,7 @@
                 // harness, which is untagged, won't cause a failure.
                 long firstTotal = resultsWithTraffic.get(0).total;
                 for (QueryResult queryResult : resultsWithTraffic) {
-                    assertWithinPercentage(queryResult + "", firstTotal, queryResult.total, 10);
+                    assertWithinPercentage(queryResult + "", firstTotal, queryResult.total, 12);
                 }
 
                 // Expect to see no traffic when querying for any tag in tagsWithNoTraffic or any
diff --git a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
index 88b9baf..9808137 100644
--- a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
@@ -73,8 +73,8 @@
 import android.system.OsConstants.IPPROTO_UDP
 import android.system.OsConstants.SOCK_DGRAM
 import android.util.Log
+import androidx.test.filters.SmallTest
 import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.runner.AndroidJUnit4
 import com.android.compatibility.common.util.PollingCheck
 import com.android.compatibility.common.util.PropertyUtil
 import com.android.modules.utils.build.SdkLevel.isAtLeastU
@@ -84,6 +84,8 @@
 import com.android.networkstack.apishim.common.NsdShim
 import com.android.testutils.ConnectivityModuleTest
 import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
 import com.android.testutils.RecorderCallback.CallbackEntry.CapabilitiesChanged
 import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
 import com.android.testutils.TestableNetworkAgent
@@ -129,12 +131,15 @@
 // tried sequentially
 private const val REGISTRATION_TIMEOUT_MS = 10_000L
 private const val DBG = false
+private const val TEST_PORT = 12345
 
 private val nsdShim = NsdShimImpl.newInstance()
 
 @AppModeFull(reason = "Socket cannot bind in instant app mode")
-@RunWith(AndroidJUnit4::class)
+@RunWith(DevSdkIgnoreRunner::class)
+@SmallTest
 @ConnectivityModuleTest
+@IgnoreUpTo(Build.VERSION_CODES.S_V2)
 class NsdManagerTest {
     // Rule used to filter CtsNetTestCasesMaxTargetSdkXX
     @get:Rule
@@ -436,6 +441,13 @@
         return agent
     }
 
+    private fun makeTestServiceInfo(network: Network? = null) = NsdServiceInfo().also {
+        it.serviceType = serviceType
+        it.serviceName = serviceName
+        it.network = network
+        it.port = TEST_PORT
+    }
+
     @After
     fun tearDown() {
         if (TestUtils.shouldTestTApis()) {
@@ -1048,6 +1060,52 @@
         assertEquals(NsdManager.FAILURE_OPERATION_NOT_RUNNING, failedCb.errorCode)
     }
 
+    @Test
+    fun testSubtypeAdvertisingAndDiscovery() {
+        val si = makeTestServiceInfo(network = testNetwork1.network)
+        // Test "_type._tcp.local,_subtype" syntax with the registration
+        si.serviceType = si.serviceType + ",_subtype"
+
+        val registrationRecord = NsdRegistrationRecord()
+
+        val baseTypeDiscoveryRecord = NsdDiscoveryRecord()
+        val subtypeDiscoveryRecord = NsdDiscoveryRecord()
+        val otherSubtypeDiscoveryRecord = NsdDiscoveryRecord()
+        tryTest {
+            registerService(registrationRecord, si)
+
+            // Test "_subtype._type._tcp.local" syntax with discovery. Note this is not
+            // "_subtype._sub._type._tcp.local".
+            nsdManager.discoverServices(serviceType,
+                    NsdManager.PROTOCOL_DNS_SD,
+                    testNetwork1.network, Executor { it.run() }, baseTypeDiscoveryRecord)
+            nsdManager.discoverServices("_othersubtype.$serviceType",
+                    NsdManager.PROTOCOL_DNS_SD,
+                    testNetwork1.network, Executor { it.run() }, otherSubtypeDiscoveryRecord)
+            nsdManager.discoverServices("_subtype.$serviceType",
+                    NsdManager.PROTOCOL_DNS_SD,
+                    testNetwork1.network, Executor { it.run() }, subtypeDiscoveryRecord)
+
+            subtypeDiscoveryRecord.waitForServiceDiscovered(
+                    serviceName, serviceType, testNetwork1.network)
+            baseTypeDiscoveryRecord.waitForServiceDiscovered(
+                    serviceName, serviceType, testNetwork1.network)
+            otherSubtypeDiscoveryRecord.expectCallback<DiscoveryStarted>()
+            // The subtype callback was registered later but called, no need for an extra delay
+            otherSubtypeDiscoveryRecord.assertNoCallback(timeoutMs = 0)
+        } cleanupStep {
+            nsdManager.stopServiceDiscovery(baseTypeDiscoveryRecord)
+            nsdManager.stopServiceDiscovery(subtypeDiscoveryRecord)
+            nsdManager.stopServiceDiscovery(otherSubtypeDiscoveryRecord)
+
+            baseTypeDiscoveryRecord.expectCallback<DiscoveryStopped>()
+            subtypeDiscoveryRecord.expectCallback<DiscoveryStopped>()
+            otherSubtypeDiscoveryRecord.expectCallback<DiscoveryStopped>()
+        } cleanup {
+            nsdManager.unregisterService(registrationRecord)
+        }
+    }
+
     /**
      * Register a service and return its registration record.
      */
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitorTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitorTest.kt
new file mode 100644
index 0000000..c62a081
--- /dev/null
+++ b/tests/unit/java/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitorTest.kt
@@ -0,0 +1,90 @@
+package com.android.server.connectivity.mdns.internal
+
+import android.net.LinkAddress
+import android.os.Build
+import android.os.Handler
+import android.os.HandlerThread
+import android.system.OsConstants
+import com.android.net.module.util.SharedLog
+import com.android.net.module.util.netlink.NetlinkConstants
+import com.android.net.module.util.netlink.RtNetlinkAddressMessage
+import com.android.net.module.util.netlink.StructIfaddrMsg
+import com.android.net.module.util.netlink.StructNlMsgHdr
+import com.android.server.connectivity.mdns.MdnsAdvertiserTest
+import com.android.server.connectivity.mdns.MdnsSocketProvider
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRunner
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mockito
+import org.mockito.Mockito.argThat
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+
+private val LINKADDRV4 = LinkAddress("192.0.2.0/24")
+private val IFACE_IDX = 32
+
+@RunWith(DevSdkIgnoreRunner::class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
+internal class SocketNetlinkMonitorTest {
+    private val thread = HandlerThread(MdnsAdvertiserTest::class.simpleName)
+    private val sharedlog = Mockito.mock(SharedLog::class.java)
+    private val netlinkMonitorCallBack =
+            Mockito.mock(MdnsSocketProvider.NetLinkMonitorCallBack::class.java)
+
+    @Before
+    fun setUp() {
+        thread.start()
+    }
+
+    @After
+    fun tearDown() {
+        thread.quitSafely()
+    }
+
+    @Test
+    fun testHandleDeprecatedNetlinkMessage() {
+        val socketNetlinkMonitor = SocketNetlinkMonitor(Handler(thread.looper), sharedlog,
+                netlinkMonitorCallBack)
+        val nlmsghdr = StructNlMsgHdr().apply {
+            nlmsg_type = NetlinkConstants.RTM_NEWADDR
+            nlmsg_flags = (StructNlMsgHdr.NLM_F_REQUEST.toInt()
+                    or StructNlMsgHdr.NLM_F_ACK.toInt()).toShort()
+            nlmsg_seq = 1
+        }
+        val structIfaddrMsg = StructIfaddrMsg(OsConstants.AF_INET.toShort(),
+                LINKADDRV4.prefixLength.toShort(),
+                LINKADDRV4.flags.toShort(),
+                LINKADDRV4.scope.toShort(), IFACE_IDX)
+        // If the LinkAddress is not preferred, RTM_NEWADDR will not trigger
+        // addOrUpdateInterfaceAddress() callback.
+        val deprecatedAddNetLinkMessage = RtNetlinkAddressMessage(nlmsghdr, structIfaddrMsg,
+            LINKADDRV4.address, null /* structIfacacheInfo */,
+            LINKADDRV4.flags or OsConstants.IFA_F_DEPRECATED)
+        socketNetlinkMonitor.processNetlinkMessage(deprecatedAddNetLinkMessage, 0L /* whenMs */)
+        verify(netlinkMonitorCallBack, never()).addOrUpdateInterfaceAddress(eq(IFACE_IDX),
+                 argThat { it.address == LINKADDRV4.address })
+
+        // If the LinkAddress is preferred, RTM_NEWADDR will trigger addOrUpdateInterfaceAddress()
+        // callback.
+        val preferredAddNetLinkMessage = RtNetlinkAddressMessage(nlmsghdr, structIfaddrMsg,
+            LINKADDRV4.address, null /* structIfacacheInfo */,
+            LINKADDRV4.flags or OsConstants.IFA_F_OPTIMISTIC)
+        socketNetlinkMonitor.processNetlinkMessage(preferredAddNetLinkMessage, 0L /* whenMs */)
+        verify(netlinkMonitorCallBack).addOrUpdateInterfaceAddress(eq(IFACE_IDX),
+            argThat { it.address == LINKADDRV4.address })
+
+        // Even if the LinkAddress is not preferred, RTM_DELADDR will trigger
+        // deleteInterfaceAddress() callback.
+        nlmsghdr.nlmsg_type = NetlinkConstants.RTM_DELADDR
+        val deprecatedDelNetLinkMessage = RtNetlinkAddressMessage(nlmsghdr, structIfaddrMsg,
+            LINKADDRV4.address, null /* structIfacacheInfo */,
+            LINKADDRV4.flags or OsConstants.IFA_F_DEPRECATED)
+        socketNetlinkMonitor.processNetlinkMessage(deprecatedDelNetLinkMessage, 0L /* whenMs */)
+        verify(netlinkMonitorCallBack).deleteInterfaceAddress(eq(IFACE_IDX),
+                argThat { it.address == LINKADDRV4.address })
+    }
+}