Merge "[TetheringGoogle] Remove explicit targetSdkVersion"
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
index ebc9d26..6a5089d 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -403,6 +403,18 @@
                 return null;
             }
         }
+
+        /** Get error BPF map. */
+        @Nullable public IBpfMap<S32, S32> getBpfErrorMap() {
+            if (!isAtLeastS()) return null;
+            try {
+                return new BpfMap<>(TETHER_ERROR_MAP_PATH,
+                    BpfMap.BPF_F_RDONLY, S32.class, S32.class);
+            } catch (ErrnoException e) {
+                Log.e(TAG, "Cannot create error map: " + e);
+                return null;
+            }
+        }
     }
 
     @VisibleForTesting
@@ -1287,13 +1299,15 @@
     }
 
     private void dumpCounters(@NonNull IndentingPrintWriter pw) {
-        if (!mDeps.isAtLeastS()) {
-            pw.println("No counter support");
-            return;
-        }
-        try (IBpfMap<S32, S32> map = new BpfMap<>(TETHER_ERROR_MAP_PATH, BpfMap.BPF_F_RDONLY,
-                S32.class, S32.class)) {
-
+        try (IBpfMap<S32, S32> map = mDeps.getBpfErrorMap()) {
+            if (map == null) {
+                pw.println("No error counter support");
+                return;
+            }
+            if (map.isEmpty()) {
+                pw.println("<empty>");
+                return;
+            }
             map.forEach((k, v) -> {
                 String counterName;
                 try {
@@ -1307,7 +1321,7 @@
                 if (v.val > 0) pw.println(String.format("%s: %d", counterName, v.val));
             });
         } catch (ErrnoException | IOException e) {
-            pw.println("Error dumping counter map: " + e);
+            pw.println("Error dumping error counter map: " + e);
         }
     }
 
diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index b7eb92c..f0d9057 100644
--- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -100,6 +100,7 @@
 import com.android.net.module.util.InterfaceParams;
 import com.android.net.module.util.NetworkStackConstants;
 import com.android.net.module.util.SharedLog;
+import com.android.net.module.util.Struct.S32;
 import com.android.net.module.util.bpf.Tether4Key;
 import com.android.net.module.util.bpf.Tether4Value;
 import com.android.net.module.util.bpf.TetherStatsKey;
@@ -199,6 +200,7 @@
     @Mock private BpfMap<TetherStatsKey, TetherStatsValue> mBpfStatsMap;
     @Mock private BpfMap<TetherLimitKey, TetherLimitValue> mBpfLimitMap;
     @Mock private BpfMap<TetherDevKey, TetherDevValue> mBpfDevMap;
+    @Mock private BpfMap<S32, S32> mBpfErrorMap;
 
     @Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor;
 
@@ -362,6 +364,11 @@
                     public BpfMap<TetherDevKey, TetherDevValue> getBpfDevMap() {
                         return mBpfDevMap;
                     }
+
+                    @Nullable
+                    public BpfMap<S32, S32> getBpfErrorMap() {
+                        return mBpfErrorMap;
+                    }
                 };
         mBpfCoordinator = spy(new BpfCoordinator(mBpfDeps));
 
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
index bbca565..0bd6380 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
@@ -94,11 +94,13 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.net.module.util.CollectionUtils;
 import com.android.net.module.util.IBpfMap;
 import com.android.net.module.util.InterfaceParams;
 import com.android.net.module.util.NetworkStackConstants;
 import com.android.net.module.util.SharedLog;
+import com.android.net.module.util.Struct.S32;
 import com.android.net.module.util.bpf.Tether4Key;
 import com.android.net.module.util.bpf.Tether4Value;
 import com.android.net.module.util.bpf.TetherStatsKey;
@@ -128,6 +130,7 @@
 import org.mockito.MockitoAnnotations;
 import org.mockito.MockitoSession;
 
+import java.io.StringWriter;
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
@@ -362,9 +365,6 @@
     @Mock private IpServer mIpServer2;
     @Mock private TetheringConfiguration mTetherConfig;
     @Mock private ConntrackMonitor mConntrackMonitor;
-    @Mock private IBpfMap<TetherDownstream6Key, Tether6Value> mBpfDownstream6Map;
-    @Mock private IBpfMap<TetherUpstream6Key, Tether6Value> mBpfUpstream6Map;
-    @Mock private IBpfMap<TetherDevKey, TetherDevValue> mBpfDevMap;
 
     // Late init since methods must be called by the thread that created this object.
     private TestableNetworkStatsProviderCbBinder mTetherStatsProviderCb;
@@ -383,10 +383,18 @@
             spy(new TestBpfMap<>(Tether4Key.class, Tether4Value.class));
     private final IBpfMap<Tether4Key, Tether4Value> mBpfUpstream4Map =
             spy(new TestBpfMap<>(Tether4Key.class, Tether4Value.class));
+    private final IBpfMap<TetherDownstream6Key, Tether6Value> mBpfDownstream6Map =
+            spy(new TestBpfMap<>(TetherDownstream6Key.class, Tether6Value.class));
+    private final IBpfMap<TetherUpstream6Key, Tether6Value> mBpfUpstream6Map =
+            spy(new TestBpfMap<>(TetherUpstream6Key.class, Tether6Value.class));
     private final IBpfMap<TetherStatsKey, TetherStatsValue> mBpfStatsMap =
             spy(new TestBpfMap<>(TetherStatsKey.class, TetherStatsValue.class));
     private final IBpfMap<TetherLimitKey, TetherLimitValue> mBpfLimitMap =
             spy(new TestBpfMap<>(TetherLimitKey.class, TetherLimitValue.class));
+    private final IBpfMap<TetherDevKey, TetherDevValue> mBpfDevMap =
+            spy(new TestBpfMap<>(TetherDevKey.class, TetherDevValue.class));
+    private final IBpfMap<S32, S32> mBpfErrorMap =
+            spy(new TestBpfMap<>(S32.class, S32.class));
     private BpfCoordinator.Dependencies mDeps =
             spy(new BpfCoordinator.Dependencies() {
                     @NonNull
@@ -457,6 +465,11 @@
                     public IBpfMap<TetherDevKey, TetherDevValue> getBpfDevMap() {
                         return mBpfDevMap;
                     }
+
+                    @Nullable
+                    public IBpfMap<S32, S32> getBpfErrorMap() {
+                        return mBpfErrorMap;
+                    }
             });
 
     @Before public void setUp() {
@@ -2099,4 +2112,88 @@
         assertEquals("upstreamIfindex: 1001, downstreamIfindex: 1003, address: 2001:db8::1, "
                 + "srcMac: 12:34:56:78:90:ab, dstMac: 00:00:00:00:00:0a", rule.toString());
     }
+
+    private void verifyDump(@NonNull final BpfCoordinator coordinator) {
+        final StringWriter stringWriter = new StringWriter();
+        final IndentingPrintWriter ipw = new IndentingPrintWriter(stringWriter, " ");
+        coordinator.dump(ipw);
+        assertFalse(stringWriter.toString().isEmpty());
+    }
+
+    @Test
+    public void testDumpDoesNotCrash() throws Exception {
+        // This dump test only used to for improving mainline module test coverage and doesn't
+        // really do any meaningful tests.
+        // TODO: consider verifying the dump content and separate tests into testDumpXXX().
+        final BpfCoordinator coordinator = makeBpfCoordinator();
+
+        // [1] Dump mostly empty content.
+        verifyDump(coordinator);
+
+        // [2] Dump mostly non-empty content.
+        // Test the following dump function and fill the corresponding content to execute
+        // code as more as possible for test coverage.
+        // - dumpBpfForwardingRulesIpv4
+        //   * mBpfDownstream4Map
+        //   * mBpfUpstream4Map
+        // - dumpBpfForwardingRulesIpv6
+        //   * mBpfDownstream6Map
+        //   * mBpfUpstream6Map
+        // - dumpStats
+        //   * mBpfStatsMap
+        // - dumpDevmap
+        //   * mBpfDevMap
+        // - dumpCounters
+        //   * mBpfErrorMap
+        // - dumpIpv6ForwardingRulesByDownstream
+        //   * mIpv6ForwardingRules
+
+        // dumpBpfForwardingRulesIpv4
+        mBpfDownstream4Map.insertEntry(
+                new TestDownstream4Key.Builder().build(),
+                new TestDownstream4Value.Builder().build());
+        mBpfUpstream4Map.insertEntry(
+                new TestUpstream4Key.Builder().build(),
+                new TestUpstream4Value.Builder().build());
+
+        // dumpBpfForwardingRulesIpv6
+        final Ipv6ForwardingRule rule = buildTestForwardingRule(UPSTREAM_IFINDEX, NEIGH_A, MAC_A);
+        mBpfDownstream6Map.insertEntry(rule.makeTetherDownstream6Key(), rule.makeTether6Value());
+
+        final TetherUpstream6Key upstream6Key = new TetherUpstream6Key(DOWNSTREAM_IFINDEX,
+                DOWNSTREAM_MAC);
+        final Tether6Value upstream6Value = new Tether6Value(UPSTREAM_IFINDEX,
+                MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS,
+                ETH_P_IPV6, NetworkStackConstants.ETHER_MTU);
+        mBpfUpstream6Map.insertEntry(upstream6Key, upstream6Value);
+
+        // dumpStats
+        mBpfStatsMap.insertEntry(
+                new TetherStatsKey(UPSTREAM_IFINDEX),
+                new TetherStatsValue(
+                        0L /* rxPackets */, 0L /* rxBytes */, 0L /* rxErrors */,
+                        0L /* txPackets */, 0L /* txBytes */, 0L /* txErrors */));
+
+        // dumpDevmap
+        coordinator.addUpstreamNameToLookupTable(UPSTREAM_IFINDEX, UPSTREAM_IFACE);
+        mBpfDevMap.insertEntry(
+                new TetherDevKey(UPSTREAM_IFINDEX),
+                new TetherDevValue(UPSTREAM_IFINDEX));
+
+        // dumpCounters
+        // The error code is defined in packages/modules/Connectivity/bpf_progs/bpf_tethering.h.
+        mBpfErrorMap.insertEntry(
+                new S32(0 /* INVALID_IPV4_VERSION */),
+                new S32(1000 /* count */));
+
+        // dumpIpv6ForwardingRulesByDownstream
+        final HashMap<IpServer, LinkedHashMap<Inet6Address, Ipv6ForwardingRule>>
+                ipv6ForwardingRules = coordinator.getForwardingRulesForTesting();
+        final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> addressRuleMap =
+                new LinkedHashMap<>();
+        addressRuleMap.put(rule.address, rule);
+        ipv6ForwardingRules.put(mIpServer, addressRuleMap);
+
+        verifyDump(coordinator);
+    }
 }
diff --git a/bpf_progs/bpf_net_helpers.h b/bpf_progs/bpf_net_helpers.h
index e382713..c39269e 100644
--- a/bpf_progs/bpf_net_helpers.h
+++ b/bpf_progs/bpf_net_helpers.h
@@ -28,9 +28,12 @@
 
 static int (*bpf_skb_pull_data)(struct __sk_buff* skb, __u32 len) = (void*)BPF_FUNC_skb_pull_data;
 
-static int (*bpf_skb_load_bytes)(struct __sk_buff* skb, int off, void* to,
+static int (*bpf_skb_load_bytes)(const struct __sk_buff* skb, int off, void* to,
                                  int len) = (void*)BPF_FUNC_skb_load_bytes;
 
+static int (*bpf_skb_load_bytes_relative)(const struct __sk_buff* skb, int off, void* to, int len,
+                                          int start_hdr) = (void*)BPF_FUNC_skb_load_bytes_relative;
+
 static int (*bpf_skb_store_bytes)(struct __sk_buff* skb, __u32 offset, const void* from, __u32 len,
                                   __u64 flags) = (void*)BPF_FUNC_skb_store_bytes;
 
diff --git a/bpf_progs/netd.c b/bpf_progs/netd.c
index 10559dd..f9484fc 100644
--- a/bpf_progs/netd.c
+++ b/bpf_progs/netd.c
@@ -47,9 +47,16 @@
 
 #define IP_PROTO_OFF offsetof(struct iphdr, protocol)
 #define IPV6_PROTO_OFF offsetof(struct ipv6hdr, nexthdr)
+
+// offsetof(struct iphdr, ihl) -- but that's a bitfield
 #define IPPROTO_IHL_OFF 0
-#define TCP_FLAG_OFF 13
-#define RST_OFFSET 2
+
+// This is offsetof(struct tcphdr, "32 bit tcp flag field")
+// The tcp flags are after be16 source, dest & be32 seq, ack_seq, hence 12 bytes in.
+//
+// Note that TCP_FLAG_{ACK,PSH,RST,SYN,FIN} are htonl(0x00{10,08,04,02,01}0000)
+// see include/uapi/linux/tcp.h
+#define TCP_FLAG32_OFF 12
 
 // For maps netd does not need to access
 #define DEFINE_BPF_MAP_NO_NETD(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries) \
@@ -97,9 +104,15 @@
 // programs that need to be usable by netd, but not by netutils_wrappers
 // (this is because these are currently attached by the mainline provided libnetd_updatable .so
 // which is loaded into netd and thus runs as netd uid/gid/selinux context)
-#define DEFINE_NETD_BPF_PROG(SECTION_NAME, prog_uid, prog_gid, the_prog) \
+#define DEFINE_NETD_BPF_PROG_KVER_RANGE(SECTION_NAME, prog_uid, prog_gid, the_prog, minKV, maxKV) \
     DEFINE_BPF_PROG_EXT(SECTION_NAME, prog_uid, prog_gid, the_prog, \
-                        KVER_NONE, KVER_INF, false, "fs_bpf_netd_readonly", "")
+                        minKV, maxKV, false, "fs_bpf_netd_readonly", "")
+
+#define DEFINE_NETD_BPF_PROG_KVER(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv) \
+    DEFINE_NETD_BPF_PROG_KVER_RANGE(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, KVER_INF)
+
+#define DEFINE_NETD_BPF_PROG(SECTION_NAME, prog_uid, prog_gid, the_prog) \
+    DEFINE_NETD_BPF_PROG_KVER(SECTION_NAME, prog_uid, prog_gid, the_prog, KVER_NONE)
 
 // programs that only need to be usable by the system server
 #define DEFINE_SYS_BPF_PROG(SECTION_NAME, prog_uid, prog_gid, the_prog) \
@@ -176,46 +189,49 @@
 DEFINE_UPDATE_STATS(stats_map_A, StatsKey)
 DEFINE_UPDATE_STATS(stats_map_B, StatsKey)
 
-static inline bool skip_owner_match(struct __sk_buff* skb) {
-    int offset = -1;
-    int ret = 0;
+// both of these return 0 on success or -EFAULT on failure (and zero out the buffer)
+static __always_inline inline int bpf_skb_load_bytes_net(const struct __sk_buff* skb, int off,
+                                                         void* to, int len, bool is_4_19) {
+    return is_4_19
+        ? bpf_skb_load_bytes_relative(skb, off, to, len, BPF_HDR_START_NET)
+        : bpf_skb_load_bytes(skb, off, to, len);
+}
+
+static __always_inline inline bool skip_owner_match(struct __sk_buff* skb, bool is_4_19) {
     if (skb->protocol == htons(ETH_P_IP)) {
-        offset = IP_PROTO_OFF;
-        uint8_t proto, ihl;
-        uint8_t flag;
-        ret = bpf_skb_load_bytes(skb, offset, &proto, 1);
-        if (!ret) {
-            if (proto == IPPROTO_ESP) {
-                return true;
-            } else if (proto == IPPROTO_TCP) {
-                ret = bpf_skb_load_bytes(skb, IPPROTO_IHL_OFF, &ihl, 1);
-                ihl = ihl & 0x0F;
-                ret = bpf_skb_load_bytes(skb, ihl * 4 + TCP_FLAG_OFF, &flag, 1);
-                if (ret == 0 && (flag >> RST_OFFSET & 1)) {
-                    return true;
-                }
-            }
-        }
-    } else if (skb->protocol == htons(ETH_P_IPV6)) {
-        offset = IPV6_PROTO_OFF;
         uint8_t proto;
-        ret = bpf_skb_load_bytes(skb, offset, &proto, 1);
-        if (!ret) {
-            if (proto == IPPROTO_ESP) {
-                return true;
-            } else if (proto == IPPROTO_TCP) {
-                uint8_t flag;
-                ret = bpf_skb_load_bytes(skb, sizeof(struct ipv6hdr) + TCP_FLAG_OFF, &flag, 1);
-                if (ret == 0 && (flag >> RST_OFFSET & 1)) {
-                    return true;
-                }
-            }
-        }
+        // no need to check for success, proto will be zeroed if bpf_skb_load_bytes_net() fails
+        (void)bpf_skb_load_bytes_net(skb, IP_PROTO_OFF, &proto, sizeof(proto), is_4_19);
+        if (proto == IPPROTO_ESP) return true;
+        if (proto != IPPROTO_TCP) return false;  // handles read failure above
+        uint8_t ihl;
+        // we don't check for success, as this cannot fail, as it is earlier in the packet than
+        // proto, the reading of which must have succeeded, additionally the next read
+        // (a little bit deeper in the packet in spite of ihl being zeroed) of the tcp flags
+        // field will also fail, and that failure we already handle correctly
+        // (we also don't check that ihl in [0x45,0x4F] nor that ipv4 header checksum is correct)
+        (void)bpf_skb_load_bytes_net(skb, IPPROTO_IHL_OFF, &ihl, sizeof(ihl), is_4_19);
+        uint32_t flag;
+        // if the read below fails, we'll just assume no TCP flags are set, which is fine.
+        (void)bpf_skb_load_bytes_net(skb, (ihl & 0xF) * 4 + TCP_FLAG32_OFF,
+                                     &flag, sizeof(flag), is_4_19);
+        return flag & TCP_FLAG_RST;  // false on read failure
+    } else if (skb->protocol == htons(ETH_P_IPV6)) {
+        uint8_t proto;
+        // no need to check for success, proto will be zeroed if bpf_skb_load_bytes_net() fails
+        (void)bpf_skb_load_bytes_net(skb, IPV6_PROTO_OFF, &proto, sizeof(proto), is_4_19);
+        if (proto == IPPROTO_ESP) return true;
+        if (proto != IPPROTO_TCP) return false;  // handles read failure above
+        uint32_t flag;
+        // if the read below fails, we'll just assume no TCP flags are set, which is fine.
+        (void)bpf_skb_load_bytes_net(skb, sizeof(struct ipv6hdr) + TCP_FLAG32_OFF,
+                                     &flag, sizeof(flag), is_4_19);
+        return flag & TCP_FLAG_RST;  // false on read failure
     }
     return false;
 }
 
-static __always_inline BpfConfig getConfig(uint32_t configKey) {
+static __always_inline inline BpfConfig getConfig(uint32_t configKey) {
     uint32_t mapSettingKey = configKey;
     BpfConfig* config = bpf_configuration_map_lookup_elem(&mapSettingKey);
     if (!config) {
@@ -230,8 +246,9 @@
 // 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 inline int bpf_owner_match(struct __sk_buff* skb, uint32_t uid, int direction) {
-    if (skip_owner_match(skb)) return BPF_PASS;
+static __always_inline inline int bpf_owner_match(struct __sk_buff* skb, uint32_t uid,
+                                                  int direction, bool is_4_19) {
+    if (skip_owner_match(skb, is_4_19)) return BPF_PASS;
 
     if (is_system_uid(uid)) return BPF_PASS;
 
@@ -273,7 +290,8 @@
     }
 }
 
-static __always_inline inline int bpf_traffic_account(struct __sk_buff* skb, int direction) {
+static __always_inline inline int bpf_traffic_account(struct __sk_buff* skb, int direction,
+                                                      bool is_4_19) {
     uint32_t sock_uid = bpf_get_socket_uid(skb);
     uint64_t cookie = bpf_get_socket_cookie(skb);
     UidTagValue* utag = bpf_cookie_tag_map_lookup_elem(&cookie);
@@ -293,7 +311,7 @@
         return BPF_PASS;
     }
 
-    int match = bpf_owner_match(skb, sock_uid, direction);
+    int match = bpf_owner_match(skb, sock_uid, direction, is_4_19);
     if ((direction == BPF_EGRESS) && (match == BPF_DROP)) {
         // If an outbound packet is going to be dropped, we do not count that
         // traffic.
@@ -338,14 +356,28 @@
     return match;
 }
 
-DEFINE_NETD_BPF_PROG("cgroupskb/ingress/stats", AID_ROOT, AID_SYSTEM, bpf_cgroup_ingress)
+DEFINE_NETD_BPF_PROG_KVER_RANGE("cgroupskb/ingress/stats$4_19", AID_ROOT, AID_SYSTEM,
+                                bpf_cgroup_ingress_4_19, KVER(4, 19, 0), KVER_INF)
 (struct __sk_buff* skb) {
-    return bpf_traffic_account(skb, BPF_INGRESS);
+    return bpf_traffic_account(skb, BPF_INGRESS, /* is_4_19 */ true);
 }
 
-DEFINE_NETD_BPF_PROG("cgroupskb/egress/stats", AID_ROOT, AID_SYSTEM, bpf_cgroup_egress)
+DEFINE_NETD_BPF_PROG_KVER_RANGE("cgroupskb/ingress/stats$4_14", AID_ROOT, AID_SYSTEM,
+                                bpf_cgroup_ingress_4_14, KVER_NONE, KVER(4, 19, 0))
 (struct __sk_buff* skb) {
-    return bpf_traffic_account(skb, BPF_EGRESS);
+    return bpf_traffic_account(skb, BPF_INGRESS, /* is_4_19 */ false);
+}
+
+DEFINE_NETD_BPF_PROG_KVER_RANGE("cgroupskb/egress/stats$4_19", AID_ROOT, AID_SYSTEM,
+                                bpf_cgroup_egress_4_19, KVER(4, 19, 0), KVER_INF)
+(struct __sk_buff* skb) {
+    return bpf_traffic_account(skb, BPF_EGRESS, /* is_4_19 */ true);
+}
+
+DEFINE_NETD_BPF_PROG_KVER_RANGE("cgroupskb/egress/stats$4_14", AID_ROOT, AID_SYSTEM,
+                                bpf_cgroup_egress_4_14, KVER_NONE, KVER(4, 19, 0))
+(struct __sk_buff* skb) {
+    return bpf_traffic_account(skb, BPF_EGRESS, /* is_4_19 */ false);
 }
 
 // WARNING: Android T's non-updatable netd depends on the name of this program.
@@ -419,7 +451,8 @@
     return BPF_NOMATCH;
 }
 
-DEFINE_NETD_BPF_PROG("cgroupsock/inet/create", AID_ROOT, AID_ROOT, inet_socket_create)
+DEFINE_NETD_BPF_PROG_KVER("cgroupsock/inet/create", AID_ROOT, AID_ROOT, inet_socket_create,
+                          KVER(4, 14, 0))
 (struct bpf_sock* sk) {
     uint64_t gid_uid = bpf_get_current_uid_gid();
     /*
diff --git a/nearby/halfsheet/Android.bp b/nearby/halfsheet/Android.bp
index 486a3ff..c84caa6 100644
--- a/nearby/halfsheet/Android.bp
+++ b/nearby/halfsheet/Android.bp
@@ -23,7 +23,6 @@
     sdk_version: "module_current",
     // This is included in tethering apex, which uses min SDK 30
     min_sdk_version: "30",
-    target_sdk_version: "current",
     updatable: true,
     certificate: ":com.android.nearby.halfsheetcertificate",
     libs: [
diff --git a/netd/BpfHandler.cpp b/netd/BpfHandler.cpp
index 3f7ed2a..7950ff7 100644
--- a/netd/BpfHandler.cpp
+++ b/netd/BpfHandler.cpp
@@ -87,7 +87,16 @@
     RETURN_IF_NOT_OK(checkProgramAccessible(XT_BPF_INGRESS_PROG_PATH));
     RETURN_IF_NOT_OK(attachProgramToCgroup(BPF_EGRESS_PROG_PATH, cg_fd, BPF_CGROUP_INET_EGRESS));
     RETURN_IF_NOT_OK(attachProgramToCgroup(BPF_INGRESS_PROG_PATH, cg_fd, BPF_CGROUP_INET_INGRESS));
-    RETURN_IF_NOT_OK(attachProgramToCgroup(CGROUP_SOCKET_PROG_PATH, cg_fd, BPF_CGROUP_INET_SOCK_CREATE));
+
+    // For the devices that support cgroup socket filter, the socket filter
+    // should be loaded successfully by bpfloader. So we attach the filter to
+    // cgroup if the program is pinned properly.
+    // TODO: delete the if statement once all devices should support cgroup
+    // socket filter (ie. the minimum kernel version required is 4.14).
+    if (!access(CGROUP_SOCKET_PROG_PATH, F_OK)) {
+        RETURN_IF_NOT_OK(
+                attachProgramToCgroup(CGROUP_SOCKET_PROG_PATH, cg_fd, BPF_CGROUP_INET_SOCK_CREATE));
+    }
     return netdutils::status::ok;
 }
 
diff --git a/tests/mts/bpf_existence_test.cpp b/tests/mts/bpf_existence_test.cpp
index bb07a98..aa5654a 100644
--- a/tests/mts/bpf_existence_test.cpp
+++ b/tests/mts/bpf_existence_test.cpp
@@ -105,7 +105,6 @@
     SHARED "prog_clatd_schedcls_ingress6_clat_rawip",
     NETD "prog_netd_cgroupskb_egress_stats",
     NETD "prog_netd_cgroupskb_ingress_stats",
-    NETD "prog_netd_cgroupsock_inet_create",
     NETD "prog_netd_schedact_ingress_account",
     NETD "prog_netd_skfilter_allowlist_xtbpf",
     NETD "prog_netd_skfilter_denylist_xtbpf",
@@ -113,6 +112,11 @@
     NETD "prog_netd_skfilter_ingress_xtbpf",
 };
 
+// Provided by *current* mainline module for T+ devices with 4.14+ kernels
+static const set<string> MAINLINE_FOR_T_4_14_PLUS = {
+    NETD "prog_netd_cgroupsock_inet_create",
+};
+
 // Provided by *current* mainline module for T+ devices with 5.4+ kernels
 static const set<string> MAINLINE_FOR_T_5_4_PLUS = {
     SHARED "prog_block_bind4_block_port",
@@ -151,6 +155,7 @@
     // Nothing added or removed in SCv2.
 
     DO_EXPECT(IsAtLeastT(), MAINLINE_FOR_T_PLUS);
+    DO_EXPECT(IsAtLeastT() && isAtLeastKernelVersion(4, 14, 0), MAINLINE_FOR_T_4_14_PLUS);
     DO_EXPECT(IsAtLeastT() && isAtLeastKernelVersion(5, 4, 0), MAINLINE_FOR_T_5_4_PLUS);
     DO_EXPECT(IsAtLeastT() && isAtLeastKernelVersion(5, 15, 0), MAINLINE_FOR_T_5_15_PLUS);
 }
diff --git a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
index 49e3514..b651c33 100644
--- a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
@@ -222,7 +222,7 @@
         public String generateIpv6Address(@NonNull String iface, @NonNull String v4,
                 @NonNull String prefix64, int mark) throws IOException {
             if (BASE_IFACE.equals(iface) && XLAT_LOCAL_IPV4ADDR_STRING.equals(v4)
-                    && NAT64_PREFIX_STRING.equals(prefix64)) {
+                    && NAT64_PREFIX_STRING.equals(prefix64) && MARK == mark) {
                 return XLAT_LOCAL_IPV6ADDR_STRING;
             }
             fail("unsupported args: " + iface + ", " + v4 + ", " + prefix64 + ", " + mark);
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketClientTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketClientTests.java
index f84ebfb..b4442a5 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketClientTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketClientTests.java
@@ -24,6 +24,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.never;
@@ -513,11 +514,8 @@
         mdnsClient.setCallback(mockCallback);
         mdnsClient.startDiscovery();
 
-        ArgumentCaptor<MdnsResponse> mdnsResponseCaptor =
-                ArgumentCaptor.forClass(MdnsResponse.class);
-        verify(mockCallback, timeout(TIMEOUT).atLeast(1))
-                .onResponseReceived(mdnsResponseCaptor.capture());
-        assertEquals(21, mdnsResponseCaptor.getValue().getInterfaceIndex());
+        verify(mockCallback, timeout(TIMEOUT).atLeastOnce())
+                .onResponseReceived(argThat(response -> response.getInterfaceIndex() == 21));
     }
 
     @Test
diff --git a/tools/gn2bp/gen_android_bp b/tools/gn2bp/gen_android_bp
index 1422b69..ee092db 100755
--- a/tools/gn2bp/gen_android_bp
+++ b/tools/gn2bp/gen_android_bp
@@ -339,7 +339,7 @@
     self.local_include_dirs = set()
     self.header_libs = set()
     self.required = set()
-    self.tool_files = []
+    self.tool_files = set()
     self.android = Target('android')
     self.host = Target('host')
     self.stl = None
@@ -467,7 +467,7 @@
 
 def is_supported_source_file(name):
   """Returns True if |name| can appear in a 'srcs' list."""
-  return os.path.splitext(name)[1] in ['.c', '.cc', '.proto']
+  return os.path.splitext(name)[1] in ['.c', '.cc', '.java', '.proto']
 
 
 def create_proto_modules(blueprint, gn, target):
@@ -598,9 +598,7 @@
 def create_amalgamated_sql_metrics_module(blueprint, target):
   bp_module_name = label_to_module_name(target.name)
   module = Module('genrule', bp_module_name, target.name)
-  module.tool_files = [
-      'tools/gen_amalgamated_sql_metrics.py',
-  ]
+  module.tool_files.add('tools/gen_amalgamated_sql_metrics.py')
   module.cmd = ' '.join([
       '$(location tools/gen_amalgamated_sql_metrics.py)',
       '--cpp_out=$(out)',
@@ -616,9 +614,7 @@
 def create_cc_proto_descriptor_module(blueprint, target):
   bp_module_name = label_to_module_name(target.name)
   module = Module('genrule', bp_module_name, target.name)
-  module.tool_files = [
-      'tools/gen_cc_proto_descriptor.py',
-  ]
+  module.tool_files.add('tools/gen_cc_proto_descriptor.py')
   module.cmd = ' '.join([
       '$(location tools/gen_cc_proto_descriptor.py)', '--gen_dir=$(genDir)',
       '--cpp_out=$(out)', '$(in)'
@@ -639,7 +635,7 @@
   module = Module('genrule', bp_module_name, gn_utils.GEN_VERSION_TARGET)
   script_path = gn_utils.label_to_path(target.script)
   module.genrule_headers.add(bp_module_name)
-  module.tool_files = [script_path]
+  module.tool_files.add(script_path)
   module.out.update(target.outputs)
   module.srcs.update(gn_utils.label_to_path(src) for src in target.inputs)
   module.cmd = ' '.join([
@@ -668,22 +664,39 @@
 
   blueprint.add_module(module)
 
+
 def create_action_module(blueprint, target):
   bp_module_name = label_to_module_name(target.name)
   module = Module('genrule', bp_module_name, target.name)
   script = gn_utils.label_to_path(target.script)
-  module.tool_files = [script]
+  module.tool_files.add(script)
+
+  # Handle passing parameters via response file by piping them into the script
+  # and reading them from /dev/stdin.
+  response_file = '{{response_file_name}}'
+  use_response_file = response_file in target.args
+  if use_response_file:
+    # Replace {{response_file_contents}} with /dev/stdin
+    target.args = ['/dev/stdin' if it == response_file else it for it in target.args]
 
   arg_string = ' '.join(target.args)
   module.cmd = '$(location %s) %s' % (script, arg_string)
 
+  if use_response_file:
+    # Pipe response file contents into script
+    module.cmd = 'echo \'%s\' | %s' % (target.response_file_contents, module.cmd)
+
   if all(os.path.splitext(it)[1] == '.h' for it in target.outputs):
     module.genrule_headers.add(bp_module_name)
 
-  # For gn actions, sources and inputs are treated equally.
-  # TODO: there should be a label_to_path function that takes a set / list.
-  module.srcs.update(gn_utils.label_to_path(it) for it in target.inputs)
-  module.srcs.update(gn_utils.label_to_path(it) for it in target.sources)
+  # gn treats inputs and sources for actions equally.
+  # soong only supports source files inside srcs, non-source files are added as
+  # tool_files dependency.
+  for it in target.sources or target.inputs:
+    if is_supported_source_file(it):
+      module.srcs.add(gn_utils.label_to_path(it))
+    else:
+      module.tool_files.add(gn_utils.label_to_path(it))
 
   # Actions using template "action_with_pydeps" also put script inside inputs.
   # TODO: it might make sense to filter inputs inside GnParser.
@@ -826,11 +839,6 @@
     if not module_is_compiled:
       continue
 
-
-    # Don't recurse in any other //gn dep if not handled by builtin_deps.
-    if dep_name.startswith('//gn:'):
-      continue
-
     if dep_module is None:
       continue
     if dep_module.type == 'cc_library_shared':
diff --git a/tools/gn2bp/gn_utils.py b/tools/gn2bp/gn_utils.py
index 6cf5b7d..ef3bf55 100644
--- a/tools/gn2bp/gn_utils.py
+++ b/tools/gn2bp/gn_utils.py
@@ -120,6 +120,7 @@
       self.outputs = set()
       self.script = None
       self.args = []
+      self.response_file_contents = None
 
       # These variables are propagated up when encountering a dependency
       # on a source_set target.
@@ -169,6 +170,13 @@
     self.actions = {}
     self.proto_libs = {}
 
+  def _get_response_file_contents(self, action_desc):
+    contents = ' '.join(action_desc.get('response_file_contents', []))
+    # escape both single and double quotes.
+    contents.replace('"', '\"')
+    contents.replace("'", '\'')
+    return contents
+
   def get_target(self, gn_target_name):
     """Returns a Target object from the fully qualified GN target name.
 
@@ -225,6 +233,7 @@
       # Args are typically relative to the root build dir (../../xxx)
       # because root build dir is typically out/xxx/).
       target.args = [re.sub('^../../', '//', x) for x in desc['args']]
+      target.response_file_contents = self._get_response_file_contents(desc)
     elif target.type == 'copy':
       # TODO: copy rules are not currently implemented.
       self.actions[gn_target_name] = target