Merge "Fix a socket leak" into main
diff --git a/bpf_progs/netd.c b/bpf_progs/netd.c
index c3258e9..0054d4a 100644
--- a/bpf_progs/netd.c
+++ b/bpf_progs/netd.c
@@ -401,11 +401,6 @@
     return true;  // disallowed interface
 }
 
-// DROP_IF_SET is set of rules that DROP if rule is globally enabled, and per-uid bit is set
-#define DROP_IF_SET (STANDBY_MATCH | OEM_DENY_1_MATCH | OEM_DENY_2_MATCH | OEM_DENY_3_MATCH)
-// DROP_IF_UNSET is set of rules that should DROP if globally enabled, and per-uid bit is NOT set
-#define DROP_IF_UNSET (DOZABLE_MATCH | POWERSAVE_MATCH | RESTRICTED_MATCH | LOW_POWER_STANDBY_MATCH)
-
 static __always_inline inline int bpf_owner_match(struct __sk_buff* skb, uint32_t uid,
                                                   bool egress, const unsigned kver) {
     if (is_system_uid(uid)) return PASS;
@@ -418,12 +413,7 @@
     uint32_t uidRules = uidEntry ? uidEntry->rule : 0;
     uint32_t allowed_iif = uidEntry ? uidEntry->iif : 0;
 
-    // Warning: funky bit-wise arithmetic: in parallel, for all DROP_IF_SET/UNSET rules
-    // check whether the rules are globally enabled, and if so whether the rules are
-    // set/unset for the specific uid.  DROP if that is the case for ANY of the rules.
-    // We achieve this by masking out only the bits/rules we're interested in checking,
-    // and negating (via bit-wise xor) the bits/rules that should drop if unset.
-    if (enabledRules & (DROP_IF_SET | DROP_IF_UNSET) & (uidRules ^ DROP_IF_UNSET)) return DROP;
+    if (isBlockedByUidRules(enabledRules, uidRules)) return DROP;
 
     if (!egress && skb->ifindex != 1) {
         if (ingress_should_discard(skb, kver)) return DROP;
diff --git a/bpf_progs/netd.h b/bpf_progs/netd.h
index 6e9acaa..dd27bf9 100644
--- a/bpf_progs/netd.h
+++ b/bpf_progs/netd.h
@@ -235,3 +235,17 @@
 #define CURRENT_STATS_MAP_CONFIGURATION_KEY 1
 
 #undef STRUCT_SIZE
+
+// DROP_IF_SET is set of rules that DROP if rule is globally enabled, and per-uid bit is set
+#define DROP_IF_SET (STANDBY_MATCH | OEM_DENY_1_MATCH | OEM_DENY_2_MATCH | OEM_DENY_3_MATCH)
+// DROP_IF_UNSET is set of rules that should DROP if globally enabled, and per-uid bit is NOT set
+#define DROP_IF_UNSET (DOZABLE_MATCH | POWERSAVE_MATCH | RESTRICTED_MATCH | LOW_POWER_STANDBY_MATCH)
+
+// Warning: funky bit-wise arithmetic: in parallel, for all DROP_IF_SET/UNSET rules
+// check whether the rules are globally enabled, and if so whether the rules are
+// set/unset for the specific uid.  DROP if that is the case for ANY of the rules.
+// We achieve this by masking out only the bits/rules we're interested in checking,
+// and negating (via bit-wise xor) the bits/rules that should drop if unset.
+static inline bool isBlockedByUidRules(BpfConfig enabledRules, uint32_t uidRules) {
+    return enabledRules & (DROP_IF_SET | DROP_IF_UNSET) & (uidRules ^ DROP_IF_UNSET);
+}
diff --git a/remoteauth/service/Android.bp b/remoteauth/service/Android.bp
index 6e7b8d2..ae5fe5c 100644
--- a/remoteauth/service/Android.bp
+++ b/remoteauth/service/Android.bp
@@ -40,14 +40,10 @@
         "framework-statsd",
     ],
     static_libs: [
-        "guava",
-        "libprotobuf-java-lite",
-        "fast-pair-lite-protos",
         "modules-utils-build",
         "modules-utils-handlerexecutor",
         "modules-utils-preconditions",
         "modules-utils-backgroundthread",
-        "presence-lite-protos",
         "uwb_androidx_backend",
     ],
     sdk_version: "system_server_current",
diff --git a/service-t/native/libs/libnetworkstats/NetworkTraceHandler.cpp b/service-t/native/libs/libnetworkstats/NetworkTraceHandler.cpp
index ec63e41..9b1b72d 100644
--- a/service-t/native/libs/libnetworkstats/NetworkTraceHandler.cpp
+++ b/service-t/native/libs/libnetworkstats/NetworkTraceHandler.cpp
@@ -18,6 +18,7 @@
 
 #include "netdbpf/NetworkTraceHandler.h"
 
+#include <android-base/macros.h>
 #include <arpa/inet.h>
 #include <bpf/BpfUtils.h>
 #include <log/log.h>
@@ -75,9 +76,35 @@
   uint32_t bytes = 0;
 };
 
-#define AGG_FIELDS(x)                                              \
-  (x).ifindex, (x).uid, (x).tag, (x).sport, (x).dport, (x).egress, \
-      (x).ipProto, (x).tcpFlags
+BundleKey::BundleKey(const PacketTrace& pkt)
+    : ifindex(pkt.ifindex),
+      uid(pkt.uid),
+      tag(pkt.tag),
+      egress(pkt.egress),
+      ipProto(pkt.ipProto),
+      ipVersion(pkt.ipVersion) {
+  switch (ipProto) {
+    case IPPROTO_TCP:
+      tcpFlags = pkt.tcpFlags;
+      FALLTHROUGH_INTENDED;
+    case IPPROTO_DCCP:
+    case IPPROTO_UDP:
+    case IPPROTO_UDPLITE:
+    case IPPROTO_SCTP:
+      localPort = ntohs(pkt.egress ? pkt.sport : pkt.dport);
+      remotePort = ntohs(pkt.egress ? pkt.dport : pkt.sport);
+      break;
+    case IPPROTO_ICMP:
+    case IPPROTO_ICMPV6:
+      icmpType = ntohs(pkt.sport);
+      icmpCode = ntohs(pkt.dport);
+      break;
+  }
+}
+
+#define AGG_FIELDS(x)                                                    \
+  (x).ifindex, (x).uid, (x).tag, (x).egress, (x).ipProto, (x).ipVersion, \
+      (x).tcpFlags, (x).localPort, (x).remotePort, (x).icmpType, (x).icmpCode
 
 std::size_t BundleHash::operator()(const BundleKey& a) const {
   std::size_t seed = 0;
@@ -179,7 +206,7 @@
       dst->set_timestamp(pkt.timestampNs);
       auto* event = dst->set_network_packet();
       event->set_length(pkt.length);
-      Fill(pkt, event);
+      Fill(BundleKey(pkt), event);
     }
     return;
   }
@@ -187,14 +214,13 @@
   uint64_t minTs = std::numeric_limits<uint64_t>::max();
   std::unordered_map<BundleKey, BundleDetails, BundleHash, BundleEq> bundles;
   for (const PacketTrace& pkt : packets) {
-    BundleKey key = pkt;
+    BundleKey key(pkt);
 
     // Dropping fields should remove them from the output and remove them from
-    // the aggregation key. In order to do the latter without changing the hash
-    // function, set the dropped fields to zero.
-    if (mDropTcpFlags) key.tcpFlags = 0;
-    if (mDropLocalPort) (key.egress ? key.sport : key.dport) = 0;
-    if (mDropRemotePort) (key.egress ? key.dport : key.sport) = 0;
+    // the aggregation key. Reset the optionals to indicate omission.
+    if (mDropTcpFlags) key.tcpFlags.reset();
+    if (mDropLocalPort) key.localPort.reset();
+    if (mDropRemotePort) key.remotePort.reset();
 
     minTs = std::min(minTs, pkt.timestampNs);
 
@@ -245,22 +271,18 @@
   }
 }
 
-void NetworkTraceHandler::Fill(const PacketTrace& src,
+void NetworkTraceHandler::Fill(const BundleKey& src,
                                NetworkPacketEvent* event) {
   event->set_direction(src.egress ? TrafficDirection::DIR_EGRESS
                                   : TrafficDirection::DIR_INGRESS);
   event->set_uid(src.uid);
   event->set_tag(src.tag);
 
-  if (!mDropLocalPort) {
-    event->set_local_port(ntohs(src.egress ? src.sport : src.dport));
-  }
-  if (!mDropRemotePort) {
-    event->set_remote_port(ntohs(src.egress ? src.dport : src.sport));
-  }
-  if (!mDropTcpFlags) {
-    event->set_tcp_flags(src.tcpFlags);
-  }
+  if (src.tcpFlags.has_value()) event->set_tcp_flags(*src.tcpFlags);
+  if (src.localPort.has_value()) event->set_local_port(*src.localPort);
+  if (src.remotePort.has_value()) event->set_remote_port(*src.remotePort);
+  if (src.icmpType.has_value()) event->set_icmp_type(*src.icmpType);
+  if (src.icmpCode.has_value()) event->set_icmp_code(*src.icmpCode);
 
   event->set_ip_proto(src.ipProto);
 
diff --git a/service-t/native/libs/libnetworkstats/NetworkTraceHandlerTest.cpp b/service-t/native/libs/libnetworkstats/NetworkTraceHandlerTest.cpp
index f2c1a86..0c4f049 100644
--- a/service-t/native/libs/libnetworkstats/NetworkTraceHandlerTest.cpp
+++ b/service-t/native/libs/libnetworkstats/NetworkTraceHandlerTest.cpp
@@ -113,7 +113,7 @@
           .length = 100,
           .uid = 10,
           .tag = 123,
-          .ipProto = 6,
+          .ipProto = IPPROTO_TCP,
           .tcpFlags = 1,
       },
   };
@@ -138,12 +138,14 @@
           .sport = htons(8080),
           .dport = htons(443),
           .egress = true,
+          .ipProto = IPPROTO_TCP,
       },
       PacketTrace{
           .timestampNs = 2,
           .sport = htons(443),
           .dport = htons(8080),
           .egress = false,
+          .ipProto = IPPROTO_TCP,
       },
   };
 
@@ -161,6 +163,42 @@
               TrafficDirection::DIR_INGRESS);
 }
 
+TEST_F(NetworkTraceHandlerTest, WriteIcmpTypeAndCode) {
+  std::vector<PacketTrace> input = {
+      PacketTrace{
+          .timestampNs = 1,
+          .sport = htons(11),  // type
+          .dport = htons(22),  // code
+          .egress = true,
+          .ipProto = IPPROTO_ICMP,
+      },
+      PacketTrace{
+          .timestampNs = 2,
+          .sport = htons(33),  // type
+          .dport = htons(44),  // code
+          .egress = false,
+          .ipProto = IPPROTO_ICMPV6,
+      },
+  };
+
+  std::vector<TracePacket> events;
+  ASSERT_TRUE(TraceAndSortPackets(input, &events));
+
+  ASSERT_EQ(events.size(), 2);
+  EXPECT_FALSE(events[0].network_packet().has_local_port());
+  EXPECT_FALSE(events[0].network_packet().has_remote_port());
+  EXPECT_THAT(events[0].network_packet().icmp_type(), 11);
+  EXPECT_THAT(events[0].network_packet().icmp_code(), 22);
+  EXPECT_THAT(events[0].network_packet().direction(),
+              TrafficDirection::DIR_EGRESS);
+  EXPECT_FALSE(events[1].network_packet().local_port());
+  EXPECT_FALSE(events[1].network_packet().remote_port());
+  EXPECT_THAT(events[1].network_packet().icmp_type(), 33);
+  EXPECT_THAT(events[1].network_packet().icmp_code(), 44);
+  EXPECT_THAT(events[1].network_packet().direction(),
+              TrafficDirection::DIR_INGRESS);
+}
+
 TEST_F(NetworkTraceHandlerTest, BasicBundling) {
   // TODO: remove this once bundling becomes default. Until then, set arbitrary
   // aggregation threshold to enable bundling.
@@ -168,12 +206,12 @@
   config.set_aggregation_threshold(10);
 
   std::vector<PacketTrace> input = {
-      PacketTrace{.uid = 123, .timestampNs = 2, .length = 200},
-      PacketTrace{.uid = 123, .timestampNs = 1, .length = 100},
-      PacketTrace{.uid = 123, .timestampNs = 4, .length = 300},
+      PacketTrace{.timestampNs = 2, .length = 200, .uid = 123},
+      PacketTrace{.timestampNs = 1, .length = 100, .uid = 123},
+      PacketTrace{.timestampNs = 4, .length = 300, .uid = 123},
 
-      PacketTrace{.uid = 456, .timestampNs = 2, .length = 400},
-      PacketTrace{.uid = 456, .timestampNs = 4, .length = 100},
+      PacketTrace{.timestampNs = 2, .length = 400, .uid = 456},
+      PacketTrace{.timestampNs = 4, .length = 100, .uid = 456},
   };
 
   std::vector<TracePacket> events;
@@ -203,12 +241,12 @@
   config.set_aggregation_threshold(3);
 
   std::vector<PacketTrace> input = {
-      PacketTrace{.uid = 123, .timestampNs = 2, .length = 200},
-      PacketTrace{.uid = 123, .timestampNs = 1, .length = 100},
-      PacketTrace{.uid = 123, .timestampNs = 4, .length = 300},
+      PacketTrace{.timestampNs = 2, .length = 200, .uid = 123},
+      PacketTrace{.timestampNs = 1, .length = 100, .uid = 123},
+      PacketTrace{.timestampNs = 4, .length = 300, .uid = 123},
 
-      PacketTrace{.uid = 456, .timestampNs = 2, .length = 400},
-      PacketTrace{.uid = 456, .timestampNs = 4, .length = 100},
+      PacketTrace{.timestampNs = 2, .length = 400, .uid = 456},
+      PacketTrace{.timestampNs = 4, .length = 100, .uid = 456},
   };
 
   std::vector<TracePacket> events;
@@ -239,12 +277,17 @@
   __be16 b = htons(10001);
   std::vector<PacketTrace> input = {
       // Recall that local is `src` for egress and `dst` for ingress.
-      PacketTrace{.timestampNs = 1, .length = 2, .egress = true, .sport = a},
-      PacketTrace{.timestampNs = 2, .length = 4, .egress = false, .dport = a},
-      PacketTrace{.timestampNs = 3, .length = 6, .egress = true, .sport = b},
-      PacketTrace{.timestampNs = 4, .length = 8, .egress = false, .dport = b},
+      PacketTrace{.timestampNs = 1, .length = 2, .sport = a, .egress = true},
+      PacketTrace{.timestampNs = 2, .length = 4, .dport = a, .egress = false},
+      PacketTrace{.timestampNs = 3, .length = 6, .sport = b, .egress = true},
+      PacketTrace{.timestampNs = 4, .length = 8, .dport = b, .egress = false},
   };
 
+  // Set common fields.
+  for (PacketTrace& pkt : input) {
+    pkt.ipProto = IPPROTO_TCP;
+  }
+
   std::vector<TracePacket> events;
   ASSERT_TRUE(TraceAndSortPackets(input, &events, config));
   ASSERT_EQ(events.size(), 2);
@@ -274,12 +317,17 @@
   __be16 b = htons(80);
   std::vector<PacketTrace> input = {
       // Recall that remote is `dst` for egress and `src` for ingress.
-      PacketTrace{.timestampNs = 1, .length = 2, .egress = true, .dport = a},
-      PacketTrace{.timestampNs = 2, .length = 4, .egress = false, .sport = a},
-      PacketTrace{.timestampNs = 3, .length = 6, .egress = true, .dport = b},
-      PacketTrace{.timestampNs = 4, .length = 8, .egress = false, .sport = b},
+      PacketTrace{.timestampNs = 1, .length = 2, .dport = a, .egress = true},
+      PacketTrace{.timestampNs = 2, .length = 4, .sport = a, .egress = false},
+      PacketTrace{.timestampNs = 3, .length = 6, .dport = b, .egress = true},
+      PacketTrace{.timestampNs = 4, .length = 8, .sport = b, .egress = false},
   };
 
+  // Set common fields.
+  for (PacketTrace& pkt : input) {
+    pkt.ipProto = IPPROTO_TCP;
+  }
+
   std::vector<TracePacket> events;
   ASSERT_TRUE(TraceAndSortPackets(input, &events, config));
   ASSERT_EQ(events.size(), 2);
@@ -306,12 +354,17 @@
   config.set_aggregation_threshold(10);
 
   std::vector<PacketTrace> input = {
-      PacketTrace{.timestampNs = 1, .uid = 123, .length = 1, .tcpFlags = 1},
-      PacketTrace{.timestampNs = 2, .uid = 123, .length = 2, .tcpFlags = 2},
-      PacketTrace{.timestampNs = 3, .uid = 456, .length = 3, .tcpFlags = 1},
-      PacketTrace{.timestampNs = 4, .uid = 456, .length = 4, .tcpFlags = 2},
+      PacketTrace{.timestampNs = 1, .length = 1, .uid = 123, .tcpFlags = 1},
+      PacketTrace{.timestampNs = 2, .length = 2, .uid = 123, .tcpFlags = 2},
+      PacketTrace{.timestampNs = 3, .length = 3, .uid = 456, .tcpFlags = 1},
+      PacketTrace{.timestampNs = 4, .length = 4, .uid = 456, .tcpFlags = 2},
   };
 
+  // Set common fields.
+  for (PacketTrace& pkt : input) {
+    pkt.ipProto = IPPROTO_TCP;
+  }
+
   std::vector<TracePacket> events;
   ASSERT_TRUE(TraceAndSortPackets(input, &events, config));
 
diff --git a/service-t/native/libs/libnetworkstats/include/netdbpf/NetworkTraceHandler.h b/service-t/native/libs/libnetworkstats/include/netdbpf/NetworkTraceHandler.h
index bc10e68..6bf186a 100644
--- a/service-t/native/libs/libnetworkstats/include/netdbpf/NetworkTraceHandler.h
+++ b/service-t/native/libs/libnetworkstats/include/netdbpf/NetworkTraceHandler.h
@@ -30,15 +30,33 @@
 namespace android {
 namespace bpf {
 
-// BundleKeys are PacketTraces where timestamp and length are ignored.
-using BundleKey = PacketTrace;
+// BundleKey encodes a PacketTrace minus timestamp and length. The key should
+// match many packets over time for interning. For convenience, sport/dport
+// are parsed here as either local/remote port or icmp type/code.
+struct BundleKey {
+  explicit BundleKey(const PacketTrace& pkt);
 
-// BundleKeys are hashed using all fields except timestamp/length.
+  uint32_t ifindex;
+  uint32_t uid;
+  uint32_t tag;
+
+  bool egress;
+  uint8_t ipProto;
+  uint8_t ipVersion;
+
+  std::optional<uint8_t> tcpFlags;
+  std::optional<uint16_t> localPort;
+  std::optional<uint16_t> remotePort;
+  std::optional<uint8_t> icmpType;
+  std::optional<uint8_t> icmpCode;
+};
+
+// BundleKeys are hashed using a simple hash combine.
 struct BundleHash {
   std::size_t operator()(const BundleKey& a) const;
 };
 
-// BundleKeys are equal if all fields except timestamp/length are equal.
+// BundleKeys are equal if all fields are equal.
 struct BundleEq {
   bool operator()(const BundleKey& a, const BundleKey& b) const;
 };
@@ -84,13 +102,13 @@
              NetworkTraceHandler::TraceContext& ctx);
 
  private:
-  // Convert a PacketTrace into a Perfetto trace packet.
-  void Fill(const PacketTrace& src,
+  // Fills in contextual information from a bundle without interning.
+  void Fill(const BundleKey& src,
             ::perfetto::protos::pbzero::NetworkPacketEvent* event);
 
   // Fills in contextual information either inline or via interning.
   ::perfetto::protos::pbzero::NetworkPacketBundle* FillWithInterning(
-      NetworkTraceState* state, const BundleKey& key,
+      NetworkTraceState* state, const BundleKey& src,
       ::perfetto::protos::pbzero::TracePacket* dst);
 
   static internal::NetworkTracePoller sPoller;
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 85507f6..f888da5 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -97,14 +97,12 @@
 import static android.system.OsConstants.ETH_P_ALL;
 import static android.system.OsConstants.IPPROTO_TCP;
 import static android.system.OsConstants.IPPROTO_UDP;
-
 import static com.android.net.module.util.NetworkMonitorUtils.isPrivateDnsValidationRequired;
 import static com.android.net.module.util.PermissionUtils.checkAnyPermissionOf;
 import static com.android.net.module.util.PermissionUtils.enforceAnyPermissionOf;
 import static com.android.net.module.util.PermissionUtils.enforceNetworkStackPermission;
 import static com.android.net.module.util.PermissionUtils.enforceNetworkStackPermissionOr;
 import static com.android.server.ConnectivityStatsLog.CONNECTIVITY_STATE_SAMPLE;
-
 import static java.util.Map.Entry;
 
 import android.Manifest;
@@ -10614,6 +10612,16 @@
                 err.getFileDescriptor(), args);
     }
 
+    private Boolean parseBooleanArgument(final String arg) {
+        if ("true".equals(arg)) {
+            return true;
+        } else if ("false".equals(arg)) {
+            return false;
+        } else {
+            return null;
+        }
+    }
+
     private class ShellCmd extends BasicShellCommandHandler {
         @Override
         public int onCommand(String cmd) {
@@ -10643,6 +10651,54 @@
                             onHelp();
                             return -1;
                         }
+                    case "set-chain3-enabled": {
+                        final Boolean enabled = parseBooleanArgument(getNextArg());
+                        if (null == enabled) {
+                            onHelp();
+                            return -1;
+                        }
+                        Log.i(TAG, (enabled ? "En" : "Dis") + "abled FIREWALL_CHAIN_OEM_DENY_3");
+                        setFirewallChainEnabled(ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_3,
+                                enabled);
+                        return 0;
+                    }
+                    case "get-chain3-enabled": {
+                        final boolean chainEnabled = getFirewallChainEnabled(
+                                ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_3);
+                        pw.println("chain:" + (chainEnabled ? "enabled" : "disabled"));
+                        return 0;
+                    }
+                    case "set-package-networking-enabled": {
+                        final Boolean enabled = parseBooleanArgument(getNextArg());
+                        final String packageName = getNextArg();
+                        if (null == enabled || null == packageName) {
+                            onHelp();
+                            return -1;
+                        }
+                        // Throws NameNotFound if the package doesn't exist.
+                        final int appId = setPackageFirewallRule(
+                                ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_3,
+                                packageName, enabled ? FIREWALL_RULE_DEFAULT : FIREWALL_RULE_DENY);
+                        final String msg = (enabled ? "Enabled" : "Disabled")
+                                + " networking for " + packageName + ", appId " + appId;
+                        Log.i(TAG, msg);
+                        pw.println(msg);
+                        return 0;
+                    }
+                    case "get-package-networking-enabled": {
+                        final String packageName = getNextArg();
+                        final int rule = getPackageFirewallRule(
+                                ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_3, packageName);
+                        if (FIREWALL_RULE_ALLOW == rule || FIREWALL_RULE_DEFAULT == rule) {
+                            pw.println(packageName + ":" + "allow");
+                        } else if (FIREWALL_RULE_DENY == rule) {
+                            pw.println(packageName + ":" + "deny");
+                        } else {
+                            throw new IllegalStateException("Unknown rule " + rule + " for package "
+                                    + packageName);
+                        }
+                        return 0;
+                    }
                     case "reevaluate":
                         // Usage : adb shell cmd connectivity reevaluate <netId>
                         // If netId is omitted, then reevaluate the default network
@@ -10683,6 +10739,15 @@
             pw.println("    Turn airplane mode on or off.");
             pw.println("  airplane-mode");
             pw.println("    Get airplane mode.");
+            pw.println("  set-chain3-enabled [true|false]");
+            pw.println("    Enable or disable FIREWALL_CHAIN_OEM_DENY_3 for debugging.");
+            pw.println("  get-chain3-enabled");
+            pw.println("    Returns whether FIREWALL_CHAIN_OEM_DENY_3 is enabled.");
+            pw.println("  set-package-networking-enabled [true|false] [package name]");
+            pw.println("    Set the deny bit in FIREWALL_CHAIN_OEM_DENY_3 to package. This has\n"
+                    + "    no effect if the chain is disabled.");
+            pw.println("  get-package-networking-enabled [package name]");
+            pw.println("    Get the deny bit in FIREWALL_CHAIN_OEM_DENY_3 for package.");
         }
     }
 
@@ -12418,6 +12483,21 @@
         }
     }
 
+    private int setPackageFirewallRule(final int chain, final String packageName, final int rule)
+            throws PackageManager.NameNotFoundException {
+        final PackageManager pm = mContext.getPackageManager();
+        final int appId = UserHandle.getAppId(pm.getPackageUid(packageName, 0 /* flags */));
+        if (appId < Process.FIRST_APPLICATION_UID) {
+            throw new RuntimeException("Can't set package firewall rule for system app "
+                    + packageName + " with appId " + appId);
+        }
+        for (final UserHandle uh : mUserManager.getUserHandles(false /* excludeDying */)) {
+            final int uid = uh.getUid(appId);
+            setUidFirewallRule(chain, uid, rule);
+        }
+        return appId;
+    }
+
     @Override
     public void setUidFirewallRule(final int chain, final int uid, final int rule) {
         enforceNetworkStackOrSettingsPermission();
@@ -12436,6 +12516,13 @@
         }
     }
 
+    private int getPackageFirewallRule(final int chain, final String packageName)
+            throws PackageManager.NameNotFoundException {
+        final PackageManager pm = mContext.getPackageManager();
+        final int appId = UserHandle.getAppId(pm.getPackageUid(packageName, 0 /* flags */));
+        return getUidFirewallRule(chain, appId);
+    }
+
     @Override
     public int getUidFirewallRule(final int chain, final int uid) {
         enforceNetworkStackOrSettingsPermission();
diff --git a/service/src/com/android/server/connectivity/CarrierPrivilegeAuthenticator.java b/service/src/com/android/server/connectivity/CarrierPrivilegeAuthenticator.java
index 4325763..88aa329 100644
--- a/service/src/com/android/server/connectivity/CarrierPrivilegeAuthenticator.java
+++ b/service/src/com/android/server/connectivity/CarrierPrivilegeAuthenticator.java
@@ -35,6 +35,7 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.util.Log;
+import android.util.SparseIntArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -63,7 +64,7 @@
     private final TelephonyManagerShim mTelephonyManagerShim;
     private final TelephonyManager mTelephonyManager;
     @GuardedBy("mLock")
-    private int[] mCarrierServiceUid;
+    private final SparseIntArray mCarrierServiceUid = new SparseIntArray(2 /* initialCapacity */);
     @GuardedBy("mLock")
     private int mModemCount = 0;
     private final Object mLock = new Object();
@@ -75,7 +76,7 @@
 
     public CarrierPrivilegeAuthenticator(@NonNull final Context c,
             @NonNull final TelephonyManager t,
-            @NonNull final TelephonyManagerShimImpl telephonyManagerShim) {
+            @NonNull final TelephonyManagerShim telephonyManagerShim) {
         mContext = c;
         mTelephonyManager = t;
         mTelephonyManagerShim = telephonyManagerShim;
@@ -91,17 +92,7 @@
 
     public CarrierPrivilegeAuthenticator(@NonNull final Context c,
             @NonNull final TelephonyManager t) {
-        mContext = c;
-        mTelephonyManager = t;
-        mTelephonyManagerShim = TelephonyManagerShimImpl.newInstance(mTelephonyManager);
-        mThread = new HandlerThread(TAG);
-        mThread.start();
-        mHandler = new Handler(mThread.getLooper()) {};
-        synchronized (mLock) {
-            mModemCount = mTelephonyManager.getActiveModemCount();
-            registerForCarrierChanges();
-            updateCarrierServiceUid();
-        }
+        this(c, t, TelephonyManagerShimImpl.newInstance(t));
     }
 
     /**
@@ -233,9 +224,9 @@
     @VisibleForTesting
     void updateCarrierServiceUid() {
         synchronized (mLock) {
-            mCarrierServiceUid = new int[mModemCount];
+            mCarrierServiceUid.clear();
             for (int i = 0; i < mModemCount; i++) {
-                mCarrierServiceUid[i] = getCarrierServicePackageUidForSlot(i);
+                mCarrierServiceUid.put(i, getCarrierServicePackageUidForSlot(i));
             }
         }
     }
@@ -244,11 +235,8 @@
     int getCarrierServiceUidForSubId(int subId) {
         final int slotId = getSlotIndex(subId);
         synchronized (mLock) {
-            if (slotId != SubscriptionManager.INVALID_SIM_SLOT_INDEX && slotId < mModemCount) {
-                return mCarrierServiceUid[slotId];
-            }
+            return mCarrierServiceUid.get(slotId, Process.INVALID_UID);
         }
-        return Process.INVALID_UID;
     }
 
     @VisibleForTesting
diff --git a/staticlibs/native/bpf_headers/include/bpf/BpfMap.h b/staticlibs/native/bpf_headers/include/bpf/BpfMap.h
index 847083e..3be7067 100644
--- a/staticlibs/native/bpf_headers/include/bpf/BpfMap.h
+++ b/staticlibs/native/bpf_headers/include/bpf/BpfMap.h
@@ -18,10 +18,10 @@
 
 #include <linux/bpf.h>
 
+#include <android/log.h>
 #include <android-base/result.h>
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
-#include <utils/Log.h>
 
 #include "BpfSyscallWrappers.h"
 #include "bpf/BpfUtils.h"
diff --git a/staticlibs/testutils/host/com/android/testutils/ConnectivityTestTargetPreparer.kt b/staticlibs/testutils/host/com/android/testutils/ConnectivityTestTargetPreparer.kt
index 3fc74aa..eb94781 100644
--- a/staticlibs/testutils/host/com/android/testutils/ConnectivityTestTargetPreparer.kt
+++ b/staticlibs/testutils/host/com/android/testutils/ConnectivityTestTargetPreparer.kt
@@ -32,6 +32,10 @@
 private const val CONNECTIVITY_CHECK_RUNNER_NAME = "androidx.test.runner.AndroidJUnitRunner"
 private const val IGNORE_CONN_CHECK_OPTION = "ignore-connectivity-check"
 
+// The default updater package names, which might be updating packages while the CTS
+// are running
+private val UPDATER_PKGS = arrayOf("com.google.android.gms", "com.android.vending")
+
 /**
  * A target preparer that sets up and verifies a device for connectivity tests.
  *
@@ -45,35 +49,42 @@
     @Option(name = IGNORE_CONN_CHECK_OPTION,
             description = "Disables the check for mobile data and wifi")
     private var ignoreConnectivityCheck = false
+    // The default value is never used, but false is a reasonable default
+    private var originalTestChainEnabled = false
+    private val originalUpdaterPkgsStatus = HashMap<String, Boolean>()
 
-    override fun setUp(testInformation: TestInformation) {
+    override fun setUp(testInfo: TestInformation) {
         if (isDisabled) return
-        disableGmsUpdate(testInformation)
-        runPreparerApk(testInformation)
+        disableGmsUpdate(testInfo)
+        originalTestChainEnabled = getTestChainEnabled(testInfo)
+        originalUpdaterPkgsStatus.putAll(getUpdaterPkgsStatus(testInfo))
+        setUpdaterNetworkingEnabled(testInfo, enableChain = true,
+                enablePkgs = UPDATER_PKGS.associateWith { false })
+        runPreparerApk(testInfo)
     }
 
-    private fun runPreparerApk(testInformation: TestInformation) {
+    private fun runPreparerApk(testInfo: TestInformation) {
         installer.setCleanApk(true)
         installer.addTestFileName(CONNECTIVITY_CHECKER_APK)
         installer.setShouldGrantPermission(true)
-        installer.setUp(testInformation)
+        installer.setUp(testInfo)
 
         val runner = DefaultRemoteAndroidTestRunner(
                 CONNECTIVITY_PKG_NAME,
                 CONNECTIVITY_CHECK_RUNNER_NAME,
-                testInformation.device.iDevice)
+                testInfo.device.iDevice)
         runner.runOptions = "--no-hidden-api-checks"
 
         val receiver = CollectingTestListener()
-        if (!testInformation.device.runInstrumentationTests(runner, receiver)) {
+        if (!testInfo.device.runInstrumentationTests(runner, receiver)) {
             throw TargetSetupError("Device state check failed to complete",
-                    testInformation.device.deviceDescriptor)
+                    testInfo.device.deviceDescriptor)
         }
 
         val runResult = receiver.currentRunResults
         if (runResult.isRunFailure) {
             throw TargetSetupError("Failed to check device state before the test: " +
-                    runResult.runFailureMessage, testInformation.device.deviceDescriptor)
+                    runResult.runFailureMessage, testInfo.device.deviceDescriptor)
         }
 
         val ignoredTestClasses = mutableSetOf<String>()
@@ -92,25 +103,50 @@
         if (errorMsg.isBlank()) return
 
         throw TargetSetupError("Device setup checks failed. Check the test bench: \n$errorMsg",
-                testInformation.device.deviceDescriptor)
+                testInfo.device.deviceDescriptor)
     }
 
-    private fun disableGmsUpdate(testInformation: TestInformation) {
+    private fun disableGmsUpdate(testInfo: TestInformation) {
         // This will be a no-op on devices without root (su) or not using gservices, but that's OK.
-        testInformation.device.executeShellCommand("su 0 am broadcast " +
+        testInfo.exec("su 0 am broadcast " +
                 "-a com.google.gservices.intent.action.GSERVICES_OVERRIDE " +
                 "-e finsky.play_services_auto_update_enabled false")
     }
 
-    private fun clearGmsUpdateOverride(testInformation: TestInformation) {
-        testInformation.device.executeShellCommand("su 0 am broadcast " +
+    private fun clearGmsUpdateOverride(testInfo: TestInformation) {
+        testInfo.exec("su 0 am broadcast " +
                 "-a com.google.gservices.intent.action.GSERVICES_OVERRIDE " +
                 "--esn finsky.play_services_auto_update_enabled")
     }
 
-    override fun tearDown(testInformation: TestInformation, e: Throwable?) {
+    private fun setUpdaterNetworkingEnabled(
+            testInfo: TestInformation,
+            enableChain: Boolean,
+            enablePkgs: Map<String, Boolean>
+    ) {
+        // Build.VERSION_CODES.S = 31 where this is not available, then do nothing.
+        if (testInfo.device.getApiLevel() < 31) return
+        testInfo.exec("cmd connectivity set-chain3-enabled $enableChain")
+        enablePkgs.forEach { (pkg, allow) ->
+            testInfo.exec("cmd connectivity set-package-networking-enabled $pkg $allow")
+        }
+    }
+
+    private fun getTestChainEnabled(testInfo: TestInformation) =
+            testInfo.exec("cmd connectivity get-chain3-enabled").contains("chain:enabled")
+
+    private fun getUpdaterPkgsStatus(testInfo: TestInformation) =
+            UPDATER_PKGS.associateWith { pkg ->
+                !testInfo.exec("cmd connectivity get-package-networking-enabled $pkg")
+                        .contains(":deny")
+            }
+
+    override fun tearDown(testInfo: TestInformation, e: Throwable?) {
         if (isTearDownDisabled) return
-        installer.tearDown(testInformation, e)
-        clearGmsUpdateOverride(testInformation)
+        installer.tearDown(testInfo, e)
+        setUpdaterNetworkingEnabled(testInfo,
+                enableChain = originalTestChainEnabled,
+                enablePkgs = originalUpdaterPkgsStatus)
+        clearGmsUpdateOverride(testInfo)
     }
 }
diff --git a/staticlibs/testutils/host/com/android/testutils/DisableConfigSyncTargetPreparer.kt b/staticlibs/testutils/host/com/android/testutils/DisableConfigSyncTargetPreparer.kt
index 63f05a6..bc00f3c 100644
--- a/staticlibs/testutils/host/com/android/testutils/DisableConfigSyncTargetPreparer.kt
+++ b/staticlibs/testutils/host/com/android/testutils/DisableConfigSyncTargetPreparer.kt
@@ -58,4 +58,4 @@
     }
 }
 
-private fun TestInformation.exec(cmd: String) = this.device.executeShellCommand(cmd)
\ No newline at end of file
+fun TestInformation.exec(cmd: String) = this.device.executeShellCommand(cmd)