Merge "Rename "service-nearby" to "service-nearby-pre-jarjar""
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
index 64365cc..225bd58 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -65,7 +65,7 @@
 import com.android.net.module.util.CollectionUtils;
 import com.android.net.module.util.InterfaceParams;
 import com.android.net.module.util.NetworkStackConstants;
-import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.U32;
 import com.android.net.module.util.bpf.Tether4Key;
 import com.android.net.module.util.bpf.Tether4Value;
 import com.android.net.module.util.netlink.ConntrackMessage;
@@ -1177,22 +1177,13 @@
         }
     }
 
-    /**
-     * Simple struct that only contains a u32. Must be public because Struct needs access to it.
-     * TODO: make this a public inner class of Struct so anyone can use it as, e.g., Struct.U32?
-     */
-    public static class U32Struct extends Struct {
-        @Struct.Field(order = 0, type = Struct.Type.U32)
-        public long val;
-    }
-
     private void dumpCounters(@NonNull IndentingPrintWriter pw) {
         if (!mDeps.isAtLeastS()) {
             pw.println("No counter support");
             return;
         }
-        try (BpfMap<U32Struct, U32Struct> map = new BpfMap<>(TETHER_ERROR_MAP_PATH,
-                BpfMap.BPF_F_RDONLY, U32Struct.class, U32Struct.class)) {
+        try (BpfMap<U32, U32> map = new BpfMap<>(TETHER_ERROR_MAP_PATH, BpfMap.BPF_F_RDONLY,
+                U32.class, U32.class)) {
 
             map.forEach((k, v) -> {
                 String counterName;
@@ -1496,7 +1487,8 @@
     }
 
     @NonNull
-    private byte[] toIpv4MappedAddressBytes(Inet4Address ia4) {
+    @VisibleForTesting
+    static byte[] toIpv4MappedAddressBytes(Inet4Address ia4) {
         final byte[] addr4 = ia4.getAddress();
         final byte[] addr6 = new byte[16];
         addr6[10] = (byte) 0xff;
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 c614046..179fc8a 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
@@ -47,6 +47,7 @@
 import static com.android.networkstack.tethering.BpfCoordinator.StatsType;
 import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_IFACE;
 import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_UID;
+import static com.android.networkstack.tethering.BpfCoordinator.toIpv4MappedAddressBytes;
 import static com.android.networkstack.tethering.BpfUtils.DOWNSTREAM;
 import static com.android.networkstack.tethering.BpfUtils.UPSTREAM;
 import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
@@ -129,6 +130,7 @@
 import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.LinkedHashMap;
 
 @RunWith(AndroidJUnit4.class)
@@ -138,22 +140,225 @@
     public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
 
     private static final int TEST_NET_ID = 24;
+    private static final int TEST_NET_ID2 = 25;
 
+    private static final int INVALID_IFINDEX = 0;
     private static final int UPSTREAM_IFINDEX = 1001;
-    private static final int DOWNSTREAM_IFINDEX = 1002;
+    private static final int UPSTREAM_IFINDEX2 = 1002;
+    private static final int DOWNSTREAM_IFINDEX = 1003;
+    private static final int DOWNSTREAM_IFINDEX2 = 1004;
 
     private static final String UPSTREAM_IFACE = "rmnet0";
+    private static final String UPSTREAM_IFACE2 = "wlan0";
 
     private static final MacAddress DOWNSTREAM_MAC = MacAddress.fromString("12:34:56:78:90:ab");
+    private static final MacAddress DOWNSTREAM_MAC2 = MacAddress.fromString("ab:90:78:56:34:12");
+
     private static final MacAddress MAC_A = MacAddress.fromString("00:00:00:00:00:0a");
     private static final MacAddress MAC_B = MacAddress.fromString("11:22:33:00:00:0b");
 
     private static final InetAddress NEIGH_A = InetAddresses.parseNumericAddress("2001:db8::1");
     private static final InetAddress NEIGH_B = InetAddresses.parseNumericAddress("2001:db8::2");
 
+    private static final Inet4Address REMOTE_ADDR =
+            (Inet4Address) InetAddresses.parseNumericAddress("140.112.8.116");
+    private static final Inet4Address PUBLIC_ADDR =
+            (Inet4Address) InetAddresses.parseNumericAddress("1.0.0.1");
+    private static final Inet4Address PUBLIC_ADDR2 =
+            (Inet4Address) InetAddresses.parseNumericAddress("1.0.0.2");
+    private static final Inet4Address PRIVATE_ADDR =
+            (Inet4Address) InetAddresses.parseNumericAddress("192.168.80.12");
+    private static final Inet4Address PRIVATE_ADDR2 =
+            (Inet4Address) InetAddresses.parseNumericAddress("192.168.90.12");
+
+    // Generally, public port and private port are the same in the NAT conntrack message.
+    // TODO: consider using different private port and public port for testing.
+    private static final short REMOTE_PORT = (short) 443;
+    private static final short PUBLIC_PORT = (short) 62449;
+    private static final short PUBLIC_PORT2 = (short) 62450;
+    private static final short PRIVATE_PORT = (short) 62449;
+    private static final short PRIVATE_PORT2 = (short) 62450;
+
     private static final InterfaceParams UPSTREAM_IFACE_PARAMS = new InterfaceParams(
             UPSTREAM_IFACE, UPSTREAM_IFINDEX, null /* macAddr, rawip */,
             NetworkStackConstants.ETHER_MTU);
+    private static final InterfaceParams UPSTREAM_IFACE_PARAMS2 = new InterfaceParams(
+            UPSTREAM_IFACE2, UPSTREAM_IFINDEX2, MacAddress.fromString("44:55:66:00:00:0c"),
+            NetworkStackConstants.ETHER_MTU);
+
+    private static final HashMap<Integer, UpstreamInformation> UPSTREAM_INFORMATIONS =
+            new HashMap<Integer, UpstreamInformation>() {{
+                    put(UPSTREAM_IFINDEX, new UpstreamInformation(UPSTREAM_IFACE_PARAMS,
+                            PUBLIC_ADDR, NetworkCapabilities.TRANSPORT_CELLULAR, TEST_NET_ID));
+                    put(UPSTREAM_IFINDEX2, new UpstreamInformation(UPSTREAM_IFACE_PARAMS2,
+                            PUBLIC_ADDR2, NetworkCapabilities.TRANSPORT_WIFI, TEST_NET_ID2));
+            }};
+
+    private static final ClientInfo CLIENT_INFO_A = new ClientInfo(DOWNSTREAM_IFINDEX,
+            DOWNSTREAM_MAC, PRIVATE_ADDR, MAC_A);
+    private static final ClientInfo CLIENT_INFO_B = new ClientInfo(DOWNSTREAM_IFINDEX2,
+            DOWNSTREAM_MAC2, PRIVATE_ADDR2, MAC_B);
+
+    private static class UpstreamInformation {
+        public final InterfaceParams interfaceParams;
+        public final Inet4Address address;
+        public final int transportType;
+        public final int netId;
+
+        UpstreamInformation(final InterfaceParams interfaceParams,
+                final Inet4Address address, int transportType, int netId) {
+            this.interfaceParams = interfaceParams;
+            this.address = address;
+            this.transportType = transportType;
+            this.netId = netId;
+        }
+    }
+
+    private static class TestUpstream4Key {
+        public static class Builder {
+            private long mIif = DOWNSTREAM_IFINDEX;
+            private MacAddress mDstMac = DOWNSTREAM_MAC;
+            private short mL4proto = (short) IPPROTO_TCP;
+            private byte[] mSrc4 = PRIVATE_ADDR.getAddress();
+            private byte[] mDst4 = REMOTE_ADDR.getAddress();
+            private int mSrcPort = PRIVATE_PORT;
+            private int mDstPort = REMOTE_PORT;
+
+            Builder() {}
+
+            public Builder setProto(int proto) {
+                if (proto != IPPROTO_TCP && proto != IPPROTO_UDP) {
+                    fail("Not support protocol " + proto);
+                }
+                mL4proto = (short) proto;
+                return this;
+            }
+
+            public Tether4Key build() {
+                return new Tether4Key(mIif, mDstMac, mL4proto, mSrc4, mDst4, mSrcPort, mDstPort);
+            }
+        }
+    }
+
+    private static class TestDownstream4Key {
+        public static class Builder {
+            private long mIif = UPSTREAM_IFINDEX;
+            private MacAddress mDstMac = MacAddress.ALL_ZEROS_ADDRESS /* dstMac (rawip) */;
+            private short mL4proto = (short) IPPROTO_TCP;
+            private byte[] mSrc4 = REMOTE_ADDR.getAddress();
+            private byte[] mDst4 = PUBLIC_ADDR.getAddress();
+            private int mSrcPort = REMOTE_PORT;
+            private int mDstPort = PUBLIC_PORT;
+
+            Builder() {}
+
+            public Builder setProto(int proto) {
+                if (proto != IPPROTO_TCP && proto != IPPROTO_UDP) {
+                    fail("Not support protocol " + proto);
+                }
+                mL4proto = (short) proto;
+                return this;
+            }
+
+            public Tether4Key build() {
+                return new Tether4Key(mIif, mDstMac, mL4proto, mSrc4, mDst4, mSrcPort, mDstPort);
+            }
+        }
+    }
+
+    private static class TestUpstream4Value {
+        public static class Builder {
+            private long mOif = UPSTREAM_IFINDEX;
+            private MacAddress mEthDstMac = MacAddress.ALL_ZEROS_ADDRESS /* dstMac (rawip) */;
+            private MacAddress mEthSrcMac = MacAddress.ALL_ZEROS_ADDRESS /* dstMac (rawip) */;
+            private int mEthProto = ETH_P_IP;
+            private short mPmtu = NetworkStackConstants.ETHER_MTU;
+            private byte[] mSrc46 = toIpv4MappedAddressBytes(PUBLIC_ADDR);
+            private byte[] mDst46 = toIpv4MappedAddressBytes(REMOTE_ADDR);
+            private int mSrcPort = PUBLIC_PORT;
+            private int mDstPort = REMOTE_PORT;
+            private long mLastUsed = 0;
+
+            Builder() {}
+
+            public Tether4Value build() {
+                return new Tether4Value(mOif, mEthDstMac, mEthSrcMac, mEthProto, mPmtu,
+                        mSrc46, mDst46, mSrcPort, mDstPort, mLastUsed);
+            }
+        }
+    }
+
+    private static class TestDownstream4Value {
+        public static class Builder {
+            private long mOif = DOWNSTREAM_IFINDEX;
+            private MacAddress mEthDstMac = MAC_A /* client mac */;
+            private MacAddress mEthSrcMac = DOWNSTREAM_MAC;
+            private int mEthProto = ETH_P_IP;
+            private short mPmtu = NetworkStackConstants.ETHER_MTU;
+            private byte[] mSrc46 = toIpv4MappedAddressBytes(REMOTE_ADDR);
+            private byte[] mDst46 = toIpv4MappedAddressBytes(PRIVATE_ADDR);
+            private int mSrcPort = REMOTE_PORT;
+            private int mDstPort = PRIVATE_PORT;
+            private long mLastUsed = 0;
+
+            Builder() {}
+
+            public Tether4Value build() {
+                return new Tether4Value(mOif, mEthDstMac, mEthSrcMac, mEthProto, mPmtu,
+                        mSrc46, mDst46, mSrcPort, mDstPort, mLastUsed);
+            }
+        }
+    }
+
+    private static class TestConntrackEvent {
+        public static class Builder {
+            private short mMsgType = IPCTNL_MSG_CT_NEW;
+            private short mProto = (short) IPPROTO_TCP;
+            private Inet4Address mPrivateAddr = PRIVATE_ADDR;
+            private Inet4Address mPublicAddr = PUBLIC_ADDR;
+            private Inet4Address mRemoteAddr = REMOTE_ADDR;
+            private short mPrivatePort = PRIVATE_PORT;
+            private short mPublicPort = PUBLIC_PORT;
+            private short mRemotePort = REMOTE_PORT;
+
+            Builder() {}
+
+            public Builder setMsgType(short msgType) {
+                if (msgType != IPCTNL_MSG_CT_NEW && msgType != IPCTNL_MSG_CT_DELETE) {
+                    fail("Not support message type " + msgType);
+                }
+                mMsgType = (short) msgType;
+                return this;
+            }
+
+            public Builder setProto(int proto) {
+                if (proto != IPPROTO_TCP && proto != IPPROTO_UDP) {
+                    fail("Not support protocol " + proto);
+                }
+                mProto = (short) proto;
+                return this;
+            }
+
+            public Builder setRemotePort(int remotePort) {
+                mRemotePort = (short) remotePort;
+                return this;
+            }
+
+            public ConntrackEvent build() {
+                final int status = (mMsgType == IPCTNL_MSG_CT_NEW) ? ESTABLISHED_MASK : DYING_MASK;
+                final int timeoutSec = (mMsgType == IPCTNL_MSG_CT_NEW) ? 100 /* nonzero, new */
+                        : 0 /* unused, delete */;
+                return new ConntrackEvent(
+                        (short) (NetlinkConstants.NFNL_SUBSYS_CTNETLINK << 8 | mMsgType),
+                        new Tuple(new TupleIpv4(mPrivateAddr, mRemoteAddr),
+                                new TupleProto((byte) mProto, mPrivatePort, mRemotePort)),
+                        new Tuple(new TupleIpv4(mRemoteAddr, mPublicAddr),
+                                new TupleProto((byte) mProto, mRemotePort, mPublicPort)),
+                        status,
+                        timeoutSec);
+            }
+        }
+    }
 
     @Mock private NetworkStatsManager mStatsManager;
     @Mock private INetd mNetd;
@@ -161,8 +366,6 @@
     @Mock private IpServer mIpServer2;
     @Mock private TetheringConfiguration mTetherConfig;
     @Mock private ConntrackMonitor mConntrackMonitor;
-    @Mock private BpfMap<Tether4Key, Tether4Value> mBpfDownstream4Map;
-    @Mock private BpfMap<Tether4Key, Tether4Value> mBpfUpstream4Map;
     @Mock private BpfMap<TetherDownstream6Key, Tether6Value> mBpfDownstream6Map;
     @Mock private BpfMap<TetherUpstream6Key, Tether6Value> mBpfUpstream6Map;
     @Mock private BpfMap<TetherDevKey, TetherDevValue> mBpfDevMap;
@@ -179,6 +382,10 @@
     private final ArgumentCaptor<ArrayList> mStringArrayCaptor =
             ArgumentCaptor.forClass(ArrayList.class);
     private final TestLooper mTestLooper = new TestLooper();
+    private final BpfMap<Tether4Key, Tether4Value> mBpfDownstream4Map =
+            spy(new TestBpfMap<>(Tether4Key.class, Tether4Value.class));
+    private final BpfMap<Tether4Key, Tether4Value> mBpfUpstream4Map =
+            spy(new TestBpfMap<>(Tether4Key.class, Tether4Value.class));
     private final TestBpfMap<TetherStatsKey, TetherStatsValue> mBpfStatsMap =
             spy(new TestBpfMap<>(TetherStatsKey.class, TetherStatsValue.class));
     private final TestBpfMap<TetherLimitKey, TetherLimitValue> mBpfLimitMap =
@@ -1244,140 +1451,67 @@
     // |   Sever    +---------+  Upstream  | Downstream +---------+   Client   |
     // +------------+         +------------+------------+         +------------+
     // remote ip              public ip                           private ip
-    // 140.112.8.116:443      100.81.179.1:62449                  192.168.80.12:62449
+    // 140.112.8.116:443      1.0.0.1:62449                       192.168.80.12:62449
     //
-    private static final Inet4Address REMOTE_ADDR =
-            (Inet4Address) InetAddresses.parseNumericAddress("140.112.8.116");
-    private static final Inet4Address PUBLIC_ADDR =
-            (Inet4Address) InetAddresses.parseNumericAddress("100.81.179.1");
-    private static final Inet4Address PRIVATE_ADDR =
-            (Inet4Address) InetAddresses.parseNumericAddress("192.168.80.12");
 
-    // IPv4-mapped IPv6 addresses
-    // Remote addrress ::ffff:140.112.8.116
-    // Public addrress ::ffff:100.81.179.1
-    // Private addrress ::ffff:192.168.80.12
-    private static final byte[] REMOTE_ADDR_V4MAPPED_BYTES = new byte[] {
-            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
-            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xff, (byte) 0xff,
-            (byte) 0x8c, (byte) 0x70, (byte) 0x08, (byte) 0x74 };
-    private static final byte[] PUBLIC_ADDR_V4MAPPED_BYTES = new byte[] {
-            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
-            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xff, (byte) 0xff,
-            (byte) 0x64, (byte) 0x51, (byte) 0xb3, (byte) 0x01 };
-    private static final byte[] PRIVATE_ADDR_V4MAPPED_BYTES = new byte[] {
-            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
-            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xff, (byte) 0xff,
-            (byte) 0xc0, (byte) 0xa8, (byte) 0x50, (byte) 0x0c };
-
-    // Generally, public port and private port are the same in the NAT conntrack message.
-    // TODO: consider using different private port and public port for testing.
-    private static final short REMOTE_PORT = (short) 443;
-    private static final short PUBLIC_PORT = (short) 62449;
-    private static final short PRIVATE_PORT = (short) 62449;
-
-    @NonNull
-    private Tether4Key makeUpstream4Key(int proto) {
-        if (proto != IPPROTO_TCP && proto != IPPROTO_UDP) {
-            fail("Not support protocol " + proto);
-        }
-        return new Tether4Key(DOWNSTREAM_IFINDEX, DOWNSTREAM_MAC, (short) proto,
-            PRIVATE_ADDR.getAddress(), REMOTE_ADDR.getAddress(), PRIVATE_PORT, REMOTE_PORT);
-    }
-
-    @NonNull
-    private Tether4Key makeDownstream4Key(int proto) {
-        if (proto != IPPROTO_TCP && proto != IPPROTO_UDP) {
-            fail("Not support protocol " + proto);
-        }
-        return new Tether4Key(UPSTREAM_IFINDEX,
-                MacAddress.ALL_ZEROS_ADDRESS /* dstMac (rawip) */, (short) proto,
-                REMOTE_ADDR.getAddress(), PUBLIC_ADDR.getAddress(), REMOTE_PORT, PUBLIC_PORT);
-    }
-
-    @NonNull
-    private Tether4Value makeUpstream4Value() {
-        return new Tether4Value(UPSTREAM_IFINDEX,
-                MacAddress.ALL_ZEROS_ADDRESS /* ethDstMac (rawip) */,
-                MacAddress.ALL_ZEROS_ADDRESS /* ethSrcMac (rawip) */, ETH_P_IP,
-                NetworkStackConstants.ETHER_MTU, PUBLIC_ADDR_V4MAPPED_BYTES,
-                REMOTE_ADDR_V4MAPPED_BYTES, PUBLIC_PORT, REMOTE_PORT, 0 /* lastUsed */);
-    }
-
-    @NonNull
-    private Tether4Value makeDownstream4Value() {
-        return new Tether4Value(DOWNSTREAM_IFINDEX, MAC_A /* client mac */, DOWNSTREAM_MAC,
-                ETH_P_IP, NetworkStackConstants.ETHER_MTU, REMOTE_ADDR_V4MAPPED_BYTES,
-                PRIVATE_ADDR_V4MAPPED_BYTES, REMOTE_PORT, PRIVATE_PORT, 0 /* lastUsed */);
-    }
-
-    @NonNull
-    private Tether4Key makeDownstream4Key() {
-        return makeDownstream4Key(IPPROTO_TCP);
-    }
-
-    @NonNull
-    private ConntrackEvent makeTestConntrackEvent(short msgType, int proto, short remotePort) {
-        if (msgType != IPCTNL_MSG_CT_NEW && msgType != IPCTNL_MSG_CT_DELETE) {
-            fail("Not support message type " + msgType);
-        }
-        if (proto != IPPROTO_TCP && proto != IPPROTO_UDP) {
-            fail("Not support protocol " + proto);
+    // Setup upstream interface to BpfCoordinator.
+    //
+    // @param coordinator BpfCoordinator instance.
+    // @param upstreamIfindex upstream interface index. can be the following values.
+    //        INVALID_IFINDEX: no upstream interface
+    //        UPSTREAM_IFINDEX: CELLULAR (raw ip interface)
+    //        UPSTREAM_IFINDEX2: WIFI (ethernet interface)
+    private void setUpstreamInformationTo(final BpfCoordinator coordinator,
+            @Nullable Integer upstreamIfindex) {
+        if (upstreamIfindex == INVALID_IFINDEX) {
+            coordinator.updateUpstreamNetworkState(null);
+            return;
         }
 
-        final int status = (msgType == IPCTNL_MSG_CT_NEW) ? ESTABLISHED_MASK : DYING_MASK;
-        final int timeoutSec = (msgType == IPCTNL_MSG_CT_NEW) ? 100 /* nonzero, new */
-                : 0 /* unused, delete */;
-        return new ConntrackEvent(
-                (short) (NetlinkConstants.NFNL_SUBSYS_CTNETLINK << 8 | msgType),
-                new Tuple(new TupleIpv4(PRIVATE_ADDR, REMOTE_ADDR),
-                        new TupleProto((byte) proto, PRIVATE_PORT, remotePort)),
-                new Tuple(new TupleIpv4(REMOTE_ADDR, PUBLIC_ADDR),
-                        new TupleProto((byte) proto, remotePort, PUBLIC_PORT)),
-                status,
-                timeoutSec);
-    }
-
-    @NonNull
-    private ConntrackEvent makeTestConntrackEvent(short msgType, int proto) {
-        return makeTestConntrackEvent(msgType, proto, REMOTE_PORT);
-    }
-
-    private void setUpstreamInformationTo(final BpfCoordinator coordinator) {
-        final LinkProperties lp = new LinkProperties();
-        lp.setInterfaceName(UPSTREAM_IFACE);
-        lp.addLinkAddress(new LinkAddress(PUBLIC_ADDR, 32 /* prefix length */));
-        final NetworkCapabilities capabilities = new NetworkCapabilities()
-                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
-        coordinator.updateUpstreamNetworkState(new UpstreamNetworkState(lp, capabilities,
-                new Network(TEST_NET_ID)));
-    }
-
-    private void setDownstreamAndClientInformationTo(final BpfCoordinator coordinator) {
-        final ClientInfo clientInfo = new ClientInfo(DOWNSTREAM_IFINDEX, DOWNSTREAM_MAC,
-                PRIVATE_ADDR, MAC_A /* client mac */);
-        coordinator.tetherOffloadClientAdd(mIpServer, clientInfo);
-    }
-
-    private void initBpfCoordinatorForRule4(final BpfCoordinator coordinator) throws Exception {
-        // Needed because addUpstreamIfindexToMap only updates upstream information when polling
-        // was started.
-        coordinator.startPolling();
-
-        // Needed because two reasons: (1) BpfConntrackEventConsumer#accept only performs cleanup
-        // when both upstream and downstream rules are removed. (2) tetherOffloadRuleRemove of
-        // api31.BpfCoordinatorShimImpl only decreases the count while the entry is deleted.
-        // In the other words, deleteEntry returns true.
-        doReturn(true).when(mBpfUpstream4Map).deleteEntry(any());
-        doReturn(true).when(mBpfDownstream4Map).deleteEntry(any());
+        final UpstreamInformation upstreamInfo = UPSTREAM_INFORMATIONS.get(upstreamIfindex);
+        if (upstreamInfo == null) {
+            fail("Not support upstream interface index " + upstreamIfindex);
+        }
 
         // Needed because BpfCoordinator#addUpstreamIfindexToMap queries interface parameter for
         // interface index.
-        doReturn(UPSTREAM_IFACE_PARAMS).when(mDeps).getInterfaceParams(UPSTREAM_IFACE);
+        doReturn(upstreamInfo.interfaceParams).when(mDeps).getInterfaceParams(
+                upstreamInfo.interfaceParams.name);
+        coordinator.addUpstreamNameToLookupTable(upstreamInfo.interfaceParams.index,
+                upstreamInfo.interfaceParams.name);
 
-        coordinator.addUpstreamNameToLookupTable(UPSTREAM_IFINDEX, UPSTREAM_IFACE);
-        setUpstreamInformationTo(coordinator);
-        setDownstreamAndClientInformationTo(coordinator);
+        final LinkProperties lp = new LinkProperties();
+        lp.setInterfaceName(upstreamInfo.interfaceParams.name);
+        lp.addLinkAddress(new LinkAddress(upstreamInfo.address, 32 /* prefix length */));
+        final NetworkCapabilities capabilities = new NetworkCapabilities()
+                .addTransportType(upstreamInfo.transportType);
+        coordinator.updateUpstreamNetworkState(new UpstreamNetworkState(lp, capabilities,
+                new Network(upstreamInfo.netId)));
+    }
+
+    // Setup downstream interface and its client information to BpfCoordinator.
+    //
+    // @param coordinator BpfCoordinator instance.
+    // @param downstreamIfindex downstream interface index. can be the following values.
+    //        DOWNSTREAM_IFINDEX: a client information which uses MAC_A is added.
+    //        DOWNSTREAM_IFINDEX2: a client information which uses MAC_B is added.
+    // TODO: refactor this function once the client switches between each downstream interface.
+    private void addDownstreamAndClientInformationTo(final BpfCoordinator coordinator,
+            int downstreamIfindex) {
+        if (downstreamIfindex != DOWNSTREAM_IFINDEX && downstreamIfindex != DOWNSTREAM_IFINDEX2) {
+            fail("Not support downstream interface index " + downstreamIfindex);
+        }
+
+        if (downstreamIfindex == DOWNSTREAM_IFINDEX) {
+            coordinator.tetherOffloadClientAdd(mIpServer, CLIENT_INFO_A);
+        } else {
+            coordinator.tetherOffloadClientAdd(mIpServer2, CLIENT_INFO_B);
+        }
+    }
+
+    private void initBpfCoordinatorForRule4(final BpfCoordinator coordinator) throws Exception {
+        setUpstreamInformationTo(coordinator, UPSTREAM_IFINDEX);
+        addDownstreamAndClientInformationTo(coordinator, DOWNSTREAM_IFINDEX);
     }
 
     // TODO: Test the IPv4 and IPv6 exist concurrently.
@@ -1401,18 +1535,25 @@
         // because the protocol is not an element of the value. Consider using different address
         // or port to make them different for better testing.
         // TODO: Make the values of {TCP, UDP} rules different.
-        final Tether4Key expectedUpstream4KeyTcp = makeUpstream4Key(IPPROTO_TCP);
-        final Tether4Key expectedDownstream4KeyTcp = makeDownstream4Key(IPPROTO_TCP);
-        final Tether4Value expectedUpstream4ValueTcp = makeUpstream4Value();
-        final Tether4Value expectedDownstream4ValueTcp = makeDownstream4Value();
+        final Tether4Key expectedUpstream4KeyTcp = new TestUpstream4Key.Builder()
+                .setProto(IPPROTO_TCP).build();
+        final Tether4Key expectedDownstream4KeyTcp = new TestDownstream4Key.Builder()
+                .setProto(IPPROTO_TCP).build();
+        final Tether4Value expectedUpstream4ValueTcp = new TestUpstream4Value.Builder().build();
+        final Tether4Value expectedDownstream4ValueTcp = new TestDownstream4Value.Builder().build();
 
-        final Tether4Key expectedUpstream4KeyUdp = makeUpstream4Key(IPPROTO_UDP);
-        final Tether4Key expectedDownstream4KeyUdp = makeDownstream4Key(IPPROTO_UDP);
-        final Tether4Value expectedUpstream4ValueUdp = makeUpstream4Value();
-        final Tether4Value expectedDownstream4ValueUdp = makeDownstream4Value();
+        final Tether4Key expectedUpstream4KeyUdp = new TestUpstream4Key.Builder()
+                .setProto(IPPROTO_UDP).build();
+        final Tether4Key expectedDownstream4KeyUdp = new TestDownstream4Key.Builder()
+                .setProto(IPPROTO_UDP).build();
+        final Tether4Value expectedUpstream4ValueUdp = new TestUpstream4Value.Builder().build();
+        final Tether4Value expectedDownstream4ValueUdp = new TestDownstream4Value.Builder().build();
 
         // [1] Adding the first rule on current upstream immediately sends the quota.
-        mConsumer.accept(makeTestConntrackEvent(IPCTNL_MSG_CT_NEW, IPPROTO_TCP));
+        mConsumer.accept(new TestConntrackEvent.Builder()
+                .setMsgType(IPCTNL_MSG_CT_NEW)
+                .setProto(IPPROTO_TCP)
+                .build());
         verifyTetherOffloadSetInterfaceQuota(inOrder, UPSTREAM_IFINDEX, limit, true /* isInit */);
         inOrder.verify(mBpfUpstream4Map)
                 .insertEntry(eq(expectedUpstream4KeyTcp), eq(expectedUpstream4ValueTcp));
@@ -1421,7 +1562,10 @@
         inOrder.verifyNoMoreInteractions();
 
         // [2] Adding the second rule on current upstream does not send the quota.
-        mConsumer.accept(makeTestConntrackEvent(IPCTNL_MSG_CT_NEW, IPPROTO_UDP));
+        mConsumer.accept(new TestConntrackEvent.Builder()
+                .setMsgType(IPCTNL_MSG_CT_NEW)
+                .setProto(IPPROTO_UDP)
+                .build());
         verifyNeverTetherOffloadSetInterfaceQuota(inOrder);
         inOrder.verify(mBpfUpstream4Map)
                 .insertEntry(eq(expectedUpstream4KeyUdp), eq(expectedUpstream4ValueUdp));
@@ -1430,7 +1574,10 @@
         inOrder.verifyNoMoreInteractions();
 
         // [3] Removing the second rule on current upstream does not send the quota.
-        mConsumer.accept(makeTestConntrackEvent(IPCTNL_MSG_CT_DELETE, IPPROTO_UDP));
+        mConsumer.accept(new TestConntrackEvent.Builder()
+                .setMsgType(IPCTNL_MSG_CT_DELETE)
+                .setProto(IPPROTO_UDP)
+                .build());
         verifyNeverTetherOffloadSetInterfaceQuota(inOrder);
         inOrder.verify(mBpfUpstream4Map).deleteEntry(eq(expectedUpstream4KeyUdp));
         inOrder.verify(mBpfDownstream4Map).deleteEntry(eq(expectedDownstream4KeyUdp));
@@ -1439,7 +1586,10 @@
         // [4] Removing the last rule on current upstream immediately sends the cleanup stuff.
         updateStatsEntryForTetherOffloadGetAndClearStats(
                 buildTestTetherStatsParcel(UPSTREAM_IFINDEX, 0, 0, 0, 0));
-        mConsumer.accept(makeTestConntrackEvent(IPCTNL_MSG_CT_DELETE, IPPROTO_TCP));
+        mConsumer.accept(new TestConntrackEvent.Builder()
+                .setMsgType(IPCTNL_MSG_CT_DELETE)
+                .setProto(IPPROTO_TCP)
+                .build());
         inOrder.verify(mBpfUpstream4Map).deleteEntry(eq(expectedUpstream4KeyTcp));
         inOrder.verify(mBpfDownstream4Map).deleteEntry(eq(expectedDownstream4KeyTcp));
         verifyTetherOffloadGetAndClearStats(inOrder, UPSTREAM_IFINDEX);
@@ -1472,14 +1622,20 @@
         final BpfCoordinator coordinator = makeBpfCoordinator();
         initBpfCoordinatorForRule4(coordinator);
 
-        mConsumer.accept(makeTestConntrackEvent(IPCTNL_MSG_CT_NEW, IPPROTO_TCP));
+        mConsumer.accept(new TestConntrackEvent.Builder()
+                .setMsgType(IPCTNL_MSG_CT_NEW)
+                .setProto(IPPROTO_TCP)
+                .build());
         verify(mBpfDevMap).updateEntry(eq(new TetherDevKey(UPSTREAM_IFINDEX)),
                 eq(new TetherDevValue(UPSTREAM_IFINDEX)));
         verify(mBpfDevMap).updateEntry(eq(new TetherDevKey(DOWNSTREAM_IFINDEX)),
                 eq(new TetherDevValue(DOWNSTREAM_IFINDEX)));
         clearInvocations(mBpfDevMap);
 
-        mConsumer.accept(makeTestConntrackEvent(IPCTNL_MSG_CT_NEW, IPPROTO_UDP));
+        mConsumer.accept(new TestConntrackEvent.Builder()
+                .setMsgType(IPCTNL_MSG_CT_NEW)
+                .setProto(IPPROTO_UDP)
+                .build());
         verify(mBpfDevMap, never()).updateEntry(any(), any());
     }
 
@@ -1559,10 +1715,10 @@
                 new TestBpfMap<>(Tether4Key.class, Tether4Value.class);
         doReturn(bpfUpstream4Map).when(mDeps).getBpfUpstream4Map();
 
-        final Tether4Key tcpKey = makeUpstream4Key(IPPROTO_TCP);
-        final Tether4Key udpKey = makeUpstream4Key(IPPROTO_UDP);
-        final Tether4Value tcpValue = makeUpstream4Value();
-        final Tether4Value udpValue = makeUpstream4Value();
+        final Tether4Key tcpKey = new TestUpstream4Key.Builder().setProto(IPPROTO_TCP).build();
+        final Tether4Key udpKey = new TestUpstream4Key.Builder().setProto(IPPROTO_UDP).build();
+        final Tether4Value tcpValue = new TestUpstream4Value.Builder().build();
+        final Tether4Value udpValue = new TestUpstream4Value.Builder().build();
 
         checkRefreshConntrackTimeout(bpfUpstream4Map, tcpKey, tcpValue, udpKey, udpValue);
     }
@@ -1575,10 +1731,10 @@
                 new TestBpfMap<>(Tether4Key.class, Tether4Value.class);
         doReturn(bpfDownstream4Map).when(mDeps).getBpfDownstream4Map();
 
-        final Tether4Key tcpKey = makeDownstream4Key(IPPROTO_TCP);
-        final Tether4Key udpKey = makeDownstream4Key(IPPROTO_UDP);
-        final Tether4Value tcpValue = makeDownstream4Value();
-        final Tether4Value udpValue = makeDownstream4Value();
+        final Tether4Key tcpKey = new TestDownstream4Key.Builder().setProto(IPPROTO_TCP).build();
+        final Tether4Key udpKey = new TestDownstream4Key.Builder().setProto(IPPROTO_UDP).build();
+        final Tether4Value tcpValue = new TestDownstream4Value.Builder().build();
+        final Tether4Value udpValue = new TestDownstream4Value.Builder().build();
 
         checkRefreshConntrackTimeout(bpfDownstream4Map, tcpKey, tcpValue, udpKey, udpValue);
     }
@@ -1592,26 +1748,46 @@
         final short offloadedPort = 42;
         assertFalse(CollectionUtils.contains(NON_OFFLOADED_UPSTREAM_IPV4_TCP_PORTS,
                 offloadedPort));
-        mConsumer.accept(makeTestConntrackEvent(IPCTNL_MSG_CT_NEW, IPPROTO_TCP, offloadedPort));
+        mConsumer.accept(new TestConntrackEvent.Builder()
+                .setMsgType(IPCTNL_MSG_CT_NEW)
+                .setProto(IPPROTO_TCP)
+                .setRemotePort(offloadedPort)
+                .build());
         verify(mBpfUpstream4Map).insertEntry(any(), any());
         verify(mBpfDownstream4Map).insertEntry(any(), any());
         clearInvocations(mBpfUpstream4Map, mBpfDownstream4Map);
 
         for (final short port : NON_OFFLOADED_UPSTREAM_IPV4_TCP_PORTS) {
-            mConsumer.accept(makeTestConntrackEvent(IPCTNL_MSG_CT_NEW, IPPROTO_TCP, port));
+            mConsumer.accept(new TestConntrackEvent.Builder()
+                    .setMsgType(IPCTNL_MSG_CT_NEW)
+                    .setProto(IPPROTO_TCP)
+                    .setRemotePort(port)
+                    .build());
             verify(mBpfUpstream4Map, never()).insertEntry(any(), any());
             verify(mBpfDownstream4Map, never()).insertEntry(any(), any());
 
-            mConsumer.accept(makeTestConntrackEvent(IPCTNL_MSG_CT_DELETE, IPPROTO_TCP, port));
+            mConsumer.accept(new TestConntrackEvent.Builder()
+                    .setMsgType(IPCTNL_MSG_CT_DELETE)
+                    .setProto(IPPROTO_TCP)
+                    .setRemotePort(port)
+                    .build());
             verify(mBpfUpstream4Map, never()).deleteEntry(any());
             verify(mBpfDownstream4Map, never()).deleteEntry(any());
 
-            mConsumer.accept(makeTestConntrackEvent(IPCTNL_MSG_CT_NEW, IPPROTO_UDP, port));
+            mConsumer.accept(new TestConntrackEvent.Builder()
+                    .setMsgType(IPCTNL_MSG_CT_NEW)
+                    .setProto(IPPROTO_UDP)
+                    .setRemotePort(port)
+                    .build());
             verify(mBpfUpstream4Map).insertEntry(any(), any());
             verify(mBpfDownstream4Map).insertEntry(any(), any());
             clearInvocations(mBpfUpstream4Map, mBpfDownstream4Map);
 
-            mConsumer.accept(makeTestConntrackEvent(IPCTNL_MSG_CT_DELETE, IPPROTO_UDP, port));
+            mConsumer.accept(new TestConntrackEvent.Builder()
+                    .setMsgType(IPCTNL_MSG_CT_DELETE)
+                    .setProto(IPPROTO_UDP)
+                    .setRemotePort(port)
+                    .build());
             verify(mBpfUpstream4Map).deleteEntry(any());
             verify(mBpfDownstream4Map).deleteEntry(any());
             clearInvocations(mBpfUpstream4Map, mBpfDownstream4Map);
diff --git a/framework-t/api/module-lib-current.txt b/framework-t/api/module-lib-current.txt
index 59ca730..aaaa628 100644
--- a/framework-t/api/module-lib-current.txt
+++ b/framework-t/api/module-lib-current.txt
@@ -90,7 +90,7 @@
     method @NonNull public android.net.NetworkStatsCollection build();
   }
 
-  public static class NetworkStatsCollection.Key {
+  public static final class NetworkStatsCollection.Key {
     ctor public NetworkStatsCollection.Key(@NonNull java.util.Set<android.net.NetworkIdentity>, int, int, int);
   }
 
diff --git a/framework/api/module-lib-current.txt b/framework/api/module-lib-current.txt
index 9fe6505..7f50237 100644
--- a/framework/api/module-lib-current.txt
+++ b/framework/api/module-lib-current.txt
@@ -12,8 +12,8 @@
     method @NonNull @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public java.util.List<android.net.NetworkStateSnapshot> getAllNetworkStateSnapshots();
     method @Nullable public android.net.ProxyInfo getGlobalProxy();
     method @NonNull public static android.util.Range<java.lang.Integer> getIpSecNetIdRange();
-    method @Nullable @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public android.net.LinkProperties redactLinkPropertiesForPackage(@NonNull android.net.LinkProperties, int, @NonNull String);
-    method @Nullable @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public android.net.NetworkCapabilities redactNetworkCapabilitiesForPackage(@NonNull android.net.NetworkCapabilities, int, @NonNull String);
+    method @Nullable @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public android.net.LinkProperties getRedactedLinkPropertiesForPackage(@NonNull android.net.LinkProperties, int, @NonNull String);
+    method @Nullable @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public android.net.NetworkCapabilities getRedactedNetworkCapabilitiesForPackage(@NonNull android.net.NetworkCapabilities, int, @NonNull String);
     method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerDefaultNetworkCallbackForUid(int, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
     method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void removeUidFromMeteredNetworkAllowList(int);
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index 68ca46d..a798f6e 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -1643,10 +1643,10 @@
             android.Manifest.permission.NETWORK_SETTINGS})
     @SystemApi(client = MODULE_LIBRARIES)
     @Nullable
-    public LinkProperties redactLinkPropertiesForPackage(@NonNull LinkProperties lp, int uid,
+    public LinkProperties getRedactedLinkPropertiesForPackage(@NonNull LinkProperties lp, int uid,
             @NonNull String packageName) {
         try {
-            return mService.redactLinkPropertiesForPackage(
+            return mService.getRedactedLinkPropertiesForPackage(
                     lp, uid, packageName, getAttributionTag());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1683,9 +1683,11 @@
      * Redact {@link NetworkCapabilities} for a given package.
      *
      * Returns an instance of {@link NetworkCapabilities} that is appropriately redacted to send
-     * to the given package, considering its permissions. Calling this method will blame the UID for
-     * retrieving the device location if the passed capabilities contain location-sensitive
-     * information.
+     * to the given package, considering its permissions. If the passed capabilities contain
+     * location-sensitive information, they will be redacted to the correct degree for the location
+     * permissions of the app (COARSE or FINE), and will blame the UID accordingly for retrieving
+     * that level of location. If the UID holds no location permission, the returned object will
+     * contain no location-sensitive information and the UID is not blamed.
      *
      * @param nc A {@link NetworkCapabilities} instance which will be redacted.
      * @param uid The target uid.
@@ -1700,11 +1702,11 @@
             android.Manifest.permission.NETWORK_SETTINGS})
     @SystemApi(client = MODULE_LIBRARIES)
     @Nullable
-    public NetworkCapabilities redactNetworkCapabilitiesForPackage(
+    public NetworkCapabilities getRedactedNetworkCapabilitiesForPackage(
             @NonNull NetworkCapabilities nc,
             int uid, @NonNull String packageName) {
         try {
-            return mService.redactNetworkCapabilitiesForPackage(nc, uid, packageName,
+            return mService.getRedactedNetworkCapabilitiesForPackage(nc, uid, packageName,
                     getAttributionTag());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
diff --git a/framework/src/android/net/IConnectivityManager.aidl b/framework/src/android/net/IConnectivityManager.aidl
index 1e1f653..0988bf3 100644
--- a/framework/src/android/net/IConnectivityManager.aidl
+++ b/framework/src/android/net/IConnectivityManager.aidl
@@ -76,13 +76,13 @@
     LinkProperties getActiveLinkProperties();
     LinkProperties getLinkPropertiesForType(int networkType);
     LinkProperties getLinkProperties(in Network network);
-    LinkProperties redactLinkPropertiesForPackage(in LinkProperties lp, int uid, String packageName,
-            String callingAttributionTag);
+    LinkProperties getRedactedLinkPropertiesForPackage(in LinkProperties lp, int uid,
+            String packageName, String callingAttributionTag);
 
     NetworkCapabilities getNetworkCapabilities(in Network network, String callingPackageName,
             String callingAttributionTag);
 
-    NetworkCapabilities redactNetworkCapabilitiesForPackage(in NetworkCapabilities nc, int uid,
+    NetworkCapabilities getRedactedNetworkCapabilitiesForPackage(in NetworkCapabilities nc, int uid,
             String callingPackageName, String callingAttributionTag);
 
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index dd92a18..fa8f339 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -2172,7 +2172,7 @@
 
     @Override
     @Nullable
-    public LinkProperties redactLinkPropertiesForPackage(@NonNull LinkProperties lp, int uid,
+    public LinkProperties getRedactedLinkPropertiesForPackage(@NonNull LinkProperties lp, int uid,
             @NonNull String packageName, @Nullable String callingAttributionTag) {
         Objects.requireNonNull(packageName);
         Objects.requireNonNull(lp);
@@ -2207,8 +2207,9 @@
     }
 
     @Override
-    public NetworkCapabilities redactNetworkCapabilitiesForPackage(@NonNull NetworkCapabilities nc,
-            int uid, @NonNull String packageName, @Nullable String callingAttributionTag) {
+    public NetworkCapabilities getRedactedNetworkCapabilitiesForPackage(
+            @NonNull NetworkCapabilities nc, int uid, @NonNull String packageName,
+            @Nullable String callingAttributionTag) {
         Objects.requireNonNull(nc);
         Objects.requireNonNull(packageName);
         enforceNetworkStackOrSettingsPermission();
diff --git a/tests/cts/net/AndroidTestTemplate.xml b/tests/cts/net/AndroidTestTemplate.xml
index d605799..48a1c79 100644
--- a/tests/cts/net/AndroidTestTemplate.xml
+++ b/tests/cts/net/AndroidTestTemplate.xml
@@ -30,6 +30,8 @@
     </target_preparer>
     <target_preparer class="com.android.testutils.ConnectivityCheckTargetPreparer">
     </target_preparer>
+    <target_preparer class="com.android.testutils.DisableConfigSyncTargetPreparer">
+    </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.net.cts" />
         <option name="runtime-hint" value="9m4s" />
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 5e8bffa..d40bc9f 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -591,7 +591,7 @@
 
     @DevSdkIgnoreRule.IgnoreUpTo(SC_V2)
     @Test
-    public void testRedactLinkPropertiesForPackage() throws Exception {
+    public void testGetRedactedLinkPropertiesForPackage() throws Exception {
         final String groundedPkg = findPackageByPermissions(
                 List.of(), /* requiredPermissions */
                 List.of(ACCESS_NETWORK_STATE) /* forbiddenPermissions */);
@@ -628,54 +628,55 @@
         // No matter what the given uid is, a SecurityException will be thrown if the caller
         // doesn't hold the NETWORK_SETTINGS permission.
         assertThrows(SecurityException.class,
-                () -> mCm.redactLinkPropertiesForPackage(lp, groundedUid, groundedPkg));
+                () -> mCm.getRedactedLinkPropertiesForPackage(lp, groundedUid, groundedPkg));
         assertThrows(SecurityException.class,
-                () -> mCm.redactLinkPropertiesForPackage(lp, normalUid, normalPkg));
+                () -> mCm.getRedactedLinkPropertiesForPackage(lp, normalUid, normalPkg));
         assertThrows(SecurityException.class,
-                () -> mCm.redactLinkPropertiesForPackage(lp, privilegedUid, privilegedPkg));
+                () -> mCm.getRedactedLinkPropertiesForPackage(lp, privilegedUid, privilegedPkg));
 
         runAsShell(NETWORK_SETTINGS, () -> {
             // No matter what the given uid is, if the given LinkProperties is null, then
             // NullPointerException will be thrown.
             assertThrows(NullPointerException.class,
-                    () -> mCm.redactLinkPropertiesForPackage(null, groundedUid, groundedPkg));
+                    () -> mCm.getRedactedLinkPropertiesForPackage(null, groundedUid, groundedPkg));
             assertThrows(NullPointerException.class,
-                    () -> mCm.redactLinkPropertiesForPackage(null, normalUid, normalPkg));
+                    () -> mCm.getRedactedLinkPropertiesForPackage(null, normalUid, normalPkg));
             assertThrows(NullPointerException.class,
-                    () -> mCm.redactLinkPropertiesForPackage(null, privilegedUid, privilegedPkg));
+                    () -> mCm.getRedactedLinkPropertiesForPackage(
+                            null, privilegedUid, privilegedPkg));
 
             // Make sure null is returned for a UID without ACCESS_NETWORK_STATE.
-            assertNull(mCm.redactLinkPropertiesForPackage(lp, groundedUid, groundedPkg));
+            assertNull(mCm.getRedactedLinkPropertiesForPackage(lp, groundedUid, groundedPkg));
 
             // CaptivePortalApiUrl & CaptivePortalData will be set to null if given uid doesn't hold
             // the NETWORK_SETTINGS permission.
-            assertNull(mCm.redactLinkPropertiesForPackage(lp, normalUid, normalPkg)
+            assertNull(mCm.getRedactedLinkPropertiesForPackage(lp, normalUid, normalPkg)
                     .getCaptivePortalApiUrl());
-            assertNull(mCm.redactLinkPropertiesForPackage(lp, normalUid, normalPkg)
+            assertNull(mCm.getRedactedLinkPropertiesForPackage(lp, normalUid, normalPkg)
                     .getCaptivePortalData());
             // MTU is not sensitive and is not redacted.
-            assertEquals(mtu, mCm.redactLinkPropertiesForPackage(lp, normalUid, normalPkg)
+            assertEquals(mtu, mCm.getRedactedLinkPropertiesForPackage(lp, normalUid, normalPkg)
                     .getMtu());
 
             // CaptivePortalApiUrl & CaptivePortalData will be preserved if the given uid holds the
             // NETWORK_SETTINGS permission.
             assertEquals(capportUrl,
-                    mCm.redactLinkPropertiesForPackage(lp, privilegedUid, privilegedPkg)
+                    mCm.getRedactedLinkPropertiesForPackage(lp, privilegedUid, privilegedPkg)
                             .getCaptivePortalApiUrl());
             assertEquals(capportData,
-                    mCm.redactLinkPropertiesForPackage(lp, privilegedUid, privilegedPkg)
+                    mCm.getRedactedLinkPropertiesForPackage(lp, privilegedUid, privilegedPkg)
                             .getCaptivePortalData());
         });
     }
 
     private NetworkCapabilities redactNc(@NonNull final NetworkCapabilities nc, int uid,
             @NonNull String packageName) {
-        return mCm.redactNetworkCapabilitiesForPackage(nc, uid, packageName);
+        return mCm.getRedactedNetworkCapabilitiesForPackage(nc, uid, packageName);
     }
 
     @DevSdkIgnoreRule.IgnoreUpTo(SC_V2)
     @Test
-    public void testRedactNetworkCapabilitiesForPackage() throws Exception {
+    public void testGetRedactedNetworkCapabilitiesForPackage() throws Exception {
         final String groundedPkg = findPackageByPermissions(
                 List.of(), /* requiredPermissions */
                 List.of(ACCESS_NETWORK_STATE) /* forbiddenPermissions */);
diff --git a/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java b/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java
index 147fca9..fb720a7 100644
--- a/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java
@@ -21,6 +21,9 @@
 import static android.app.usage.NetworkStats.Bucket.METERED_ALL;
 import static android.app.usage.NetworkStats.Bucket.METERED_NO;
 import static android.app.usage.NetworkStats.Bucket.METERED_YES;
+import static android.app.usage.NetworkStats.Bucket.ROAMING_ALL;
+import static android.app.usage.NetworkStats.Bucket.ROAMING_NO;
+import static android.app.usage.NetworkStats.Bucket.ROAMING_YES;
 import static android.app.usage.NetworkStats.Bucket.STATE_ALL;
 import static android.app.usage.NetworkStats.Bucket.STATE_DEFAULT;
 import static android.app.usage.NetworkStats.Bucket.STATE_FOREGROUND;
@@ -77,6 +80,7 @@
 
     private abstract class NetworkInterfaceToTest {
         private boolean mMetered;
+        private boolean mRoaming;
         private boolean mIsDefault;
 
         abstract int getNetworkType();
@@ -90,6 +94,14 @@
             this.mMetered = metered;
         }
 
+        public boolean getRoaming() {
+            return mRoaming;
+        }
+
+        public void setRoaming(boolean roaming) {
+            this.mRoaming = roaming;
+        }
+
         public boolean getIsDefault() {
             return mIsDefault;
         }
@@ -267,6 +279,7 @@
         private URL mUrl;
         public boolean success;
         public boolean metered;
+        public boolean roaming;
         public boolean isDefault;
 
         NetworkCallback(long tolerance, URL url) {
@@ -274,6 +287,7 @@
             mUrl = url;
             success = false;
             metered = false;
+            roaming = false;
             isDefault = false;
         }
 
@@ -303,6 +317,8 @@
                 success = true;
                 metered = !mCm.getNetworkCapabilities(network)
                         .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+                roaming = !mCm.getNetworkCapabilities(network)
+                        .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
                 synchronized (NetworkStatsManagerTest.this) {
                     NetworkStatsManagerTest.this.notify();
                 }
@@ -333,6 +349,7 @@
         }
         if (callback.success) {
             mNetworkInterfacesToTest[networkTypeIndex].setMetered(callback.metered);
+            mNetworkInterfacesToTest[networkTypeIndex].setRoaming(callback.roaming);
             mNetworkInterfacesToTest[networkTypeIndex].setIsDefault(callback.isDefault);
             return true;
         }
@@ -377,6 +394,7 @@
             assertEquals(bucket.getState(), STATE_ALL);
             assertEquals(bucket.getUid(), UID_ALL);
             assertEquals(bucket.getMetered(), METERED_ALL);
+            assertEquals(bucket.getRoaming(), ROAMING_ALL);
             assertEquals(bucket.getDefaultNetworkStatus(), DEFAULT_NETWORK_ALL);
             setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "deny");
             try {
@@ -412,6 +430,7 @@
             assertEquals(bucket.getState(), STATE_ALL);
             assertEquals(bucket.getUid(), UID_ALL);
             assertEquals(bucket.getMetered(), METERED_ALL);
+            assertEquals(bucket.getRoaming(), ROAMING_ALL);
             assertEquals(bucket.getDefaultNetworkStatus(), DEFAULT_NETWORK_ALL);
             setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "deny");
             try {
@@ -451,15 +470,19 @@
                 long totalTxBytes = 0;
                 long totalRxBytes = 0;
                 boolean hasCorrectMetering = false;
+                boolean hasCorrectRoaming = false;
                 boolean hasCorrectDefaultStatus = false;
                 int expectedMetering = mNetworkInterfacesToTest[i].getMetered()
                         ? METERED_YES : METERED_NO;
+                int expectedRoaming = mNetworkInterfacesToTest[i].getRoaming()
+                        ? ROAMING_YES : ROAMING_NO;
                 int expectedDefaultStatus = mNetworkInterfacesToTest[i].getIsDefault()
                         ? DEFAULT_NETWORK_YES : DEFAULT_NETWORK_NO;
                 while (result.hasNextBucket()) {
                     assertTrue(result.getNextBucket(bucket));
                     assertTimestamps(bucket);
                     hasCorrectMetering |= bucket.getMetered() == expectedMetering;
+                    hasCorrectRoaming |= bucket.getRoaming() == expectedRoaming;
                     if (bucket.getUid() == Process.myUid()) {
                         totalTxPackets += bucket.getTxPackets();
                         totalRxPackets += bucket.getRxPackets();
@@ -472,6 +495,8 @@
                 assertFalse(result.getNextBucket(bucket));
                 assertTrue("Incorrect metering for NetworkType: "
                         + mNetworkInterfacesToTest[i].getNetworkType(), hasCorrectMetering);
+                assertTrue("Incorrect roaming for NetworkType: "
+                        + mNetworkInterfacesToTest[i].getNetworkType(), hasCorrectRoaming);
                 assertTrue("Incorrect isDefault for NetworkType: "
                         + mNetworkInterfacesToTest[i].getNetworkType(), hasCorrectDefaultStatus);
                 assertTrue("No Rx bytes usage for uid " + Process.myUid(), totalRxBytes > 0);
@@ -564,6 +589,7 @@
                     assertTimestamps(bucket);
                     assertEquals(bucket.getState(), STATE_ALL);
                     assertEquals(bucket.getMetered(), METERED_ALL);
+                    assertEquals(bucket.getRoaming(), ROAMING_ALL);
                     assertEquals(bucket.getDefaultNetworkStatus(), DEFAULT_NETWORK_ALL);
                     assertEquals(bucket.getUid(), Process.myUid());
                     totalTxPackets += bucket.getTxPackets();
@@ -617,6 +643,7 @@
                     assertTimestamps(bucket);
                     assertEquals(bucket.getState(), STATE_ALL);
                     assertEquals(bucket.getMetered(), METERED_ALL);
+                    assertEquals(bucket.getRoaming(), ROAMING_ALL);
                     assertEquals(bucket.getDefaultNetworkStatus(), DEFAULT_NETWORK_ALL);
                     assertEquals(bucket.getUid(), Process.myUid());
                     if (bucket.getTag() == NETWORK_TAG) {
@@ -838,6 +865,7 @@
             if (expectedTag != null) assertEquals(bucket.getTag(), (int) expectedTag);
             if (expectedState != null) assertEquals(bucket.getState(), (int) expectedState);
             assertEquals(bucket.getMetered(), METERED_ALL);
+            assertEquals(bucket.getRoaming(), ROAMING_ALL);
             assertEquals(bucket.getDefaultNetworkStatus(), DEFAULT_NETWORK_ALL);
             if (bucket.getUid() == Process.myUid()) {
                 totalTxPackets += bucket.getTxPackets();