Merge "Make LinkPropertiesTest backwards compatible" into rvc-dev
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 732ceb5..02e9b50 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -674,17 +674,29 @@
route.getDestination(),
route.getGateway(),
mIfaceName,
- route.getType());
+ route.getType(),
+ route.getMtu());
+ }
+
+ private int findRouteIndexByDestination(RouteInfo route) {
+ for (int i = 0; i < mRoutes.size(); i++) {
+ if (mRoutes.get(i).isSameDestinationAs(route)) {
+ return i;
+ }
+ }
+ return -1;
}
/**
- * Adds a {@link RouteInfo} to this {@code LinkProperties}, if not present. If the
- * {@link RouteInfo} had an interface name set and that differs from the interface set for this
- * {@code LinkProperties} an {@link IllegalArgumentException} will be thrown. The proper
+ * Adds a {@link RouteInfo} to this {@code LinkProperties}, if a {@link RouteInfo}
+ * with the same destination exists with different properties (e.g., different MTU),
+ * it will be updated. If the {@link RouteInfo} had an interface name set and
+ * that differs from the interface set for this {@code LinkProperties} an
+ * {@link IllegalArgumentException} will be thrown. The proper
* course is to add either un-named or properly named {@link RouteInfo}.
*
* @param route A {@link RouteInfo} to add to this object.
- * @return {@code false} if the route was already present, {@code true} if it was added.
+ * @return {@code true} was added or updated, false otherwise.
*/
public boolean addRoute(@NonNull RouteInfo route) {
String routeIface = route.getInterface();
@@ -694,11 +706,20 @@
+ " vs. " + mIfaceName);
}
route = routeWithInterface(route);
- if (!mRoutes.contains(route)) {
+
+ int i = findRouteIndexByDestination(route);
+ if (i == -1) {
+ // Route was not present. Add it.
mRoutes.add(route);
return true;
+ } else if (mRoutes.get(i).equals(route)) {
+ // Route was present and has same properties. Do nothing.
+ return false;
+ } else {
+ // Route was present and has different properties. Update it.
+ mRoutes.set(i, route);
+ return true;
}
- return false;
}
/**
@@ -706,6 +727,7 @@
* specify an interface and the interface must match the interface of this
* {@code LinkProperties}, or it will not be removed.
*
+ * @param route A {@link RouteInfo} specifying the route to remove.
* @return {@code true} if the route was removed, {@code false} if it was not present.
*
* @hide
diff --git a/core/java/android/net/NattKeepalivePacketData.java b/core/java/android/net/NattKeepalivePacketData.java
index bd39c13..29da495 100644
--- a/core/java/android/net/NattKeepalivePacketData.java
+++ b/core/java/android/net/NattKeepalivePacketData.java
@@ -20,6 +20,7 @@
import static android.net.InvalidPacketException.ERROR_INVALID_PORT;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.net.util.IpUtils;
import android.os.Parcel;
@@ -30,6 +31,7 @@
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.util.Objects;
/** @hide */
@SystemApi
@@ -121,4 +123,19 @@
return new NattKeepalivePacketData[size];
}
};
+
+ @Override
+ public boolean equals(@Nullable final Object o) {
+ if (!(o instanceof NattKeepalivePacketData)) return false;
+ final NattKeepalivePacketData other = (NattKeepalivePacketData) o;
+ return this.srcAddress.equals(other.srcAddress)
+ && this.dstAddress.equals(other.dstAddress)
+ && this.srcPort == other.srcPort
+ && this.dstPort == other.dstPort;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(srcAddress, dstAddress, srcPort, dstPort);
+ }
}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 116e343..7d8df6a 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -37,9 +37,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
+import java.util.Arrays;
import java.util.Objects;
import java.util.Set;
import java.util.StringJoiner;
@@ -96,7 +94,7 @@
mTransportInfo = null;
mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED;
mUids = null;
- mAdministratorUids.clear();
+ mAdministratorUids = new int[0];
mOwnerUid = Process.INVALID_UID;
mSSID = null;
mPrivateDnsBroken = false;
@@ -884,10 +882,10 @@
* empty unless the destination is 1) the System Server, or 2) Telephony. In either case, the
* receiving entity must have the ACCESS_FINE_LOCATION permission and target R+.
*/
- private final List<Integer> mAdministratorUids = new ArrayList<>();
+ private int[] mAdministratorUids = new int[0];
/**
- * Sets the list of UIDs that are administrators of this network.
+ * Sets the int[] of UIDs that are administrators of this network.
*
* <p>UIDs included in administratorUids gain administrator privileges over this Network.
* Examples of UIDs that should be included in administratorUids are:
@@ -907,23 +905,21 @@
*/
@NonNull
@SystemApi
- public NetworkCapabilities setAdministratorUids(
- @NonNull final List<Integer> administratorUids) {
- mAdministratorUids.clear();
- mAdministratorUids.addAll(administratorUids);
+ public NetworkCapabilities setAdministratorUids(@NonNull final int[] administratorUids) {
+ mAdministratorUids = Arrays.copyOf(administratorUids, administratorUids.length);
return this;
}
/**
- * Retrieves the list of UIDs that are administrators of this Network.
+ * Retrieves the UIDs that are administrators of this Network.
*
- * @return the List of UIDs that are administrators of this Network
+ * @return the int[] of UIDs that are administrators of this Network
* @hide
*/
@NonNull
@SystemApi
- public List<Integer> getAdministratorUids() {
- return Collections.unmodifiableList(mAdministratorUids);
+ public int[] getAdministratorUids() {
+ return Arrays.copyOf(mAdministratorUids, mAdministratorUids.length);
}
/**
@@ -1584,7 +1580,7 @@
dest.writeArraySet(mUids);
dest.writeString(mSSID);
dest.writeBoolean(mPrivateDnsBroken);
- dest.writeList(mAdministratorUids);
+ dest.writeIntArray(mAdministratorUids);
dest.writeInt(mOwnerUid);
dest.writeInt(mRequestorUid);
dest.writeString(mRequestorPackageName);
@@ -1608,7 +1604,7 @@
null /* ClassLoader, null for default */);
netCap.mSSID = in.readString();
netCap.mPrivateDnsBroken = in.readBoolean();
- netCap.setAdministratorUids(in.readArrayList(null));
+ netCap.setAdministratorUids(in.createIntArray());
netCap.mOwnerUid = in.readInt();
netCap.mRequestorUid = in.readInt();
netCap.mRequestorPackageName = in.readString();
@@ -1665,8 +1661,8 @@
sb.append(" OwnerUid: ").append(mOwnerUid);
}
- if (!mAdministratorUids.isEmpty()) {
- sb.append(" AdministratorUids: ").append(mAdministratorUids);
+ if (mAdministratorUids.length == 0) {
+ sb.append(" AdministratorUids: ").append(Arrays.toString(mAdministratorUids));
}
if (null != mSSID) {
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index 2b9e9fe..fec2df4 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -527,6 +527,26 @@
}
/**
+ * Compares this RouteInfo object against the specified object and indicates if the
+ * destinations of both routes are equal.
+ * @return {@code true} if the route destinations are equal, {@code false} otherwise.
+ *
+ * @hide
+ */
+ public boolean isSameDestinationAs(@Nullable Object obj) {
+ if (this == obj) return true;
+
+ if (!(obj instanceof RouteInfo)) return false;
+
+ RouteInfo target = (RouteInfo) obj;
+
+ if (Objects.equals(mDestination, target.getDestination())) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Returns a hashcode for this <code>RouteInfo</code> object.
*/
public int hashCode() {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index deae459..748c366 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -110,6 +110,7 @@
import android.net.PrivateDnsConfigParcel;
import android.net.ProxyInfo;
import android.net.RouteInfo;
+import android.net.RouteInfoParcel;
import android.net.SocketKeepalive;
import android.net.TetheringManager;
import android.net.UidRange;
@@ -120,6 +121,7 @@
import android.net.metrics.NetworkEvent;
import android.net.netlink.InetDiagMessage;
import android.net.shared.PrivateDnsConfig;
+import android.net.util.LinkPropertiesUtils.CompareOrUpdateResult;
import android.net.util.LinkPropertiesUtils.CompareResult;
import android.net.util.MultinetworkPolicyTracker;
import android.net.util.NetdService;
@@ -232,6 +234,7 @@
import java.util.StringJoiner;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Function;
/**
* @hide
@@ -1666,7 +1669,7 @@
if (newNc.getNetworkSpecifier() != null) {
newNc.setNetworkSpecifier(newNc.getNetworkSpecifier().redact());
}
- newNc.setAdministratorUids(Collections.EMPTY_LIST);
+ newNc.setAdministratorUids(new int[0]);
return newNc;
}
@@ -1727,7 +1730,7 @@
nc.setSingleUid(callerUid);
}
nc.setRequestorUidAndPackageName(callerUid, callerPackageName);
- nc.setAdministratorUids(Collections.EMPTY_LIST);
+ nc.setAdministratorUids(new int[0]);
// Clear owner UID; this can never come from an app.
nc.setOwnerUid(INVALID_UID);
@@ -5951,15 +5954,49 @@
}
}
+ // TODO: move to frameworks/libs/net.
+ private RouteInfoParcel convertRouteInfo(RouteInfo route) {
+ final String nextHop;
+
+ switch (route.getType()) {
+ case RouteInfo.RTN_UNICAST:
+ if (route.hasGateway()) {
+ nextHop = route.getGateway().getHostAddress();
+ } else {
+ nextHop = INetd.NEXTHOP_NONE;
+ }
+ break;
+ case RouteInfo.RTN_UNREACHABLE:
+ nextHop = INetd.NEXTHOP_UNREACHABLE;
+ break;
+ case RouteInfo.RTN_THROW:
+ nextHop = INetd.NEXTHOP_THROW;
+ break;
+ default:
+ nextHop = INetd.NEXTHOP_NONE;
+ break;
+ }
+
+ final RouteInfoParcel rip = new RouteInfoParcel();
+ rip.ifName = route.getInterface();
+ rip.destination = route.getDestination().toString();
+ rip.nextHop = nextHop;
+ rip.mtu = route.getMtu();
+
+ return rip;
+ }
+
/**
* Have netd update routes from oldLp to newLp.
* @return true if routes changed between oldLp and newLp
*/
private boolean updateRoutes(LinkProperties newLp, LinkProperties oldLp, int netId) {
- // Compare the route diff to determine which routes should be added and removed.
- CompareResult<RouteInfo> routeDiff = new CompareResult<>(
+ Function<RouteInfo, IpPrefix> getDestination = (r) -> r.getDestination();
+ // compare the route diff to determine which routes have been updated
+ CompareOrUpdateResult<IpPrefix, RouteInfo> routeDiff = new CompareOrUpdateResult<>(
oldLp != null ? oldLp.getAllRoutes() : null,
- newLp != null ? newLp.getAllRoutes() : null);
+ newLp != null ? newLp.getAllRoutes() : null,
+ getDestination);
// add routes before removing old in case it helps with continuous connectivity
@@ -5968,10 +6005,10 @@
if (route.hasGateway()) continue;
if (VDBG || DDBG) log("Adding Route [" + route + "] to network " + netId);
try {
- mNMS.addRoute(netId, route);
+ mNetd.networkAddRouteParcel(netId, convertRouteInfo(route));
} catch (Exception e) {
if ((route.getDestination().getAddress() instanceof Inet4Address) || VDBG) {
- loge("Exception in addRoute for non-gateway: " + e);
+ loge("Exception in networkAddRouteParcel for non-gateway: " + e);
}
}
}
@@ -5979,10 +6016,10 @@
if (!route.hasGateway()) continue;
if (VDBG || DDBG) log("Adding Route [" + route + "] to network " + netId);
try {
- mNMS.addRoute(netId, route);
+ mNetd.networkAddRouteParcel(netId, convertRouteInfo(route));
} catch (Exception e) {
if ((route.getGateway() instanceof Inet4Address) || VDBG) {
- loge("Exception in addRoute for gateway: " + e);
+ loge("Exception in networkAddRouteParcel for gateway: " + e);
}
}
}
@@ -5990,12 +6027,22 @@
for (RouteInfo route : routeDiff.removed) {
if (VDBG || DDBG) log("Removing Route [" + route + "] from network " + netId);
try {
- mNMS.removeRoute(netId, route);
+ mNetd.networkRemoveRouteParcel(netId, convertRouteInfo(route));
} catch (Exception e) {
- loge("Exception in removeRoute: " + e);
+ loge("Exception in networkRemoveRouteParcel: " + e);
}
}
- return !routeDiff.added.isEmpty() || !routeDiff.removed.isEmpty();
+
+ for (RouteInfo route : routeDiff.updated) {
+ if (VDBG || DDBG) log("Updating Route [" + route + "] from network " + netId);
+ try {
+ mNetd.networkUpdateRouteParcel(netId, convertRouteInfo(route));
+ } catch (Exception e) {
+ loge("Exception in networkUpdateRouteParcel: " + e);
+ }
+ }
+ return !routeDiff.added.isEmpty() || !routeDiff.removed.isEmpty()
+ || !routeDiff.updated.isEmpty();
}
private void updateDnses(LinkProperties newLp, LinkProperties oldLp, int netId) {
@@ -7864,7 +7911,7 @@
private void clearNetworkCapabilitiesUids(@NonNull NetworkCapabilities nc) {
nc.setUids(null);
- nc.setAdministratorUids(Collections.EMPTY_LIST);
+ nc.setAdministratorUids(new int[0]);
nc.setOwnerUid(Process.INVALID_UID);
}
@@ -7911,8 +7958,9 @@
}
// Administrator UIDs also contains the Owner UID
- if (nai.networkCapabilities.getAdministratorUids().contains(callbackUid)) {
- return true;
+ final int[] administratorUids = nai.networkCapabilities.getAdministratorUids();
+ for (final int uid : administratorUids) {
+ if (uid == callbackUid) return true;
}
return false;
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index f772a4a..81a1372 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -53,7 +53,6 @@
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
-import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
@@ -250,7 +249,7 @@
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
nc.setNetworkSpecifier(new StringNetworkSpecifier(iface));
- nc.setAdministratorUids(intArrayToList(administratorUids));
+ nc.setAdministratorUids(administratorUids);
if (!isMetered) {
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
}
@@ -293,14 +292,6 @@
return new TestNetworkAgent(looper, context, ni, nc, lp, callingUid, binder);
}
- private List<Integer> intArrayToList(@NonNull int[] array) {
- final List<Integer> list = new ArrayList<>(array.length);
- for (final int i : array) {
- list.add(i);
- }
- return list;
- }
-
/**
* Sets up a Network with extremely limited privileges, guarded by the MANAGE_TEST_NETWORKS
* permission.
diff --git a/tests/net/common/java/android/net/KeepalivePacketDataTest.kt b/tests/net/common/java/android/net/KeepalivePacketDataTest.kt
new file mode 100644
index 0000000..f464ec6
--- /dev/null
+++ b/tests/net/common/java/android/net/KeepalivePacketDataTest.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net
+
+import android.net.InvalidPacketException.ERROR_INVALID_IP_ADDRESS
+import android.net.InvalidPacketException.ERROR_INVALID_PORT
+import android.os.Build
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import java.net.InetAddress
+import java.util.Arrays
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Assert.fail
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class KeepalivePacketDataTest {
+ @Rule @JvmField
+ val ignoreRule: DevSdkIgnoreRule = DevSdkIgnoreRule()
+
+ private val INVALID_PORT = 65537
+ private val TEST_DST_PORT = 4244
+ private val TEST_SRC_PORT = 4243
+
+ private val TESTBYTES = byteArrayOf(12, 31, 22, 44)
+ private val TEST_SRC_ADDRV4 = "198.168.0.2".address()
+ private val TEST_DST_ADDRV4 = "198.168.0.1".address()
+ private val TEST_ADDRV6 = "2001:db8::1".address()
+
+ private fun String.address() = InetAddresses.parseNumericAddress(this)
+
+ // Add for test because constructor of KeepalivePacketData is protected.
+ private inner class TestKeepalivePacketData(
+ srcAddress: InetAddress? = TEST_SRC_ADDRV4,
+ srcPort: Int = TEST_SRC_PORT,
+ dstAddress: InetAddress? = TEST_DST_ADDRV4,
+ dstPort: Int = TEST_DST_PORT,
+ data: ByteArray = TESTBYTES
+ ) : KeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, data)
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testConstructor() {
+ var data: TestKeepalivePacketData
+
+ try {
+ data = TestKeepalivePacketData(srcAddress = null)
+ fail("Null src address should cause exception")
+ } catch (e: InvalidPacketException) {
+ assertEquals(e.error, ERROR_INVALID_IP_ADDRESS)
+ }
+
+ try {
+ data = TestKeepalivePacketData(dstAddress = null)
+ fail("Null dst address should cause exception")
+ } catch (e: InvalidPacketException) {
+ assertEquals(e.error, ERROR_INVALID_IP_ADDRESS)
+ }
+
+ try {
+ data = TestKeepalivePacketData(dstAddress = TEST_ADDRV6)
+ fail("Ip family mismatched should cause exception")
+ } catch (e: InvalidPacketException) {
+ assertEquals(e.error, ERROR_INVALID_IP_ADDRESS)
+ }
+
+ try {
+ data = TestKeepalivePacketData(srcPort = INVALID_PORT)
+ fail("Invalid srcPort should cause exception")
+ } catch (e: InvalidPacketException) {
+ assertEquals(e.error, ERROR_INVALID_PORT)
+ }
+
+ try {
+ data = TestKeepalivePacketData(dstPort = INVALID_PORT)
+ fail("Invalid dstPort should cause exception")
+ } catch (e: InvalidPacketException) {
+ assertEquals(e.error, ERROR_INVALID_PORT)
+ }
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testSrcAddress() = assertEquals(TEST_SRC_ADDRV4, TestKeepalivePacketData().srcAddress)
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testDstAddress() = assertEquals(TEST_DST_ADDRV4, TestKeepalivePacketData().dstAddress)
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testSrcPort() = assertEquals(TEST_SRC_PORT, TestKeepalivePacketData().srcPort)
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testDstPort() = assertEquals(TEST_DST_PORT, TestKeepalivePacketData().dstPort)
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testPacket() = assertTrue(Arrays.equals(TESTBYTES, TestKeepalivePacketData().packet))
+}
\ No newline at end of file
diff --git a/tests/net/common/java/android/net/NattKeepalivePacketDataTest.kt b/tests/net/common/java/android/net/NattKeepalivePacketDataTest.kt
new file mode 100644
index 0000000..46f39dd
--- /dev/null
+++ b/tests/net/common/java/android/net/NattKeepalivePacketDataTest.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net
+
+import android.net.InvalidPacketException.ERROR_INVALID_IP_ADDRESS
+import android.net.InvalidPacketException.ERROR_INVALID_PORT
+import android.net.NattSocketKeepalive.NATT_PORT
+import android.os.Build
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.testutils.assertEqualBothWays
+import com.android.testutils.assertFieldCountEquals
+import com.android.testutils.assertParcelSane
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.parcelingRoundTrip
+import java.net.InetAddress
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
+import org.junit.Assert.fail
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class NattKeepalivePacketDataTest {
+ @Rule @JvmField
+ val ignoreRule: DevSdkIgnoreRule = DevSdkIgnoreRule()
+
+ /* Refer to the definition in {@code NattKeepalivePacketData} */
+ private val IPV4_HEADER_LENGTH = 20
+ private val UDP_HEADER_LENGTH = 8
+
+ private val TEST_PORT = 4243
+ private val TEST_PORT2 = 4244
+ private val TEST_SRC_ADDRV4 = "198.168.0.2".address()
+ private val TEST_DST_ADDRV4 = "198.168.0.1".address()
+ private val TEST_ADDRV6 = "2001:db8::1".address()
+
+ private fun String.address() = InetAddresses.parseNumericAddress(this)
+ private fun nattKeepalivePacket(
+ srcAddress: InetAddress? = TEST_SRC_ADDRV4,
+ srcPort: Int = TEST_PORT,
+ dstAddress: InetAddress? = TEST_DST_ADDRV4,
+ dstPort: Int = NATT_PORT
+ ) = NattKeepalivePacketData.nattKeepalivePacket(srcAddress, srcPort, dstAddress, dstPort)
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testConstructor() {
+ try {
+ nattKeepalivePacket(dstPort = TEST_PORT)
+ fail("Dst port is not NATT port should cause exception")
+ } catch (e: InvalidPacketException) {
+ assertEquals(e.error, ERROR_INVALID_PORT)
+ }
+
+ try {
+ nattKeepalivePacket(srcAddress = TEST_ADDRV6)
+ fail("A v6 srcAddress should cause exception")
+ } catch (e: InvalidPacketException) {
+ assertEquals(e.error, ERROR_INVALID_IP_ADDRESS)
+ }
+
+ try {
+ nattKeepalivePacket(dstAddress = TEST_ADDRV6)
+ fail("A v6 dstAddress should cause exception")
+ } catch (e: InvalidPacketException) {
+ assertEquals(e.error, ERROR_INVALID_IP_ADDRESS)
+ }
+
+ try {
+ parcelingRoundTrip(
+ NattKeepalivePacketData(TEST_SRC_ADDRV4, TEST_PORT, TEST_DST_ADDRV4, TEST_PORT,
+ byteArrayOf(12, 31, 22, 44)))
+ fail("Invalid data should cause exception")
+ } catch (e: IllegalArgumentException) { }
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testParcel() {
+ assertParcelSane(nattKeepalivePacket(), 0)
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testEquals() {
+ assertEqualBothWays(nattKeepalivePacket(), nattKeepalivePacket())
+ assertNotEquals(nattKeepalivePacket(dstAddress = TEST_SRC_ADDRV4), nattKeepalivePacket())
+ assertNotEquals(nattKeepalivePacket(srcAddress = TEST_DST_ADDRV4), nattKeepalivePacket())
+ // Test src port only because dst port have to be NATT_PORT
+ assertNotEquals(nattKeepalivePacket(srcPort = TEST_PORT2), nattKeepalivePacket())
+ // Make sure the parceling test is updated if fields are added in the base class.
+ assertFieldCountEquals(5, KeepalivePacketData::class.java)
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testHashCode() {
+ assertEquals(nattKeepalivePacket().hashCode(), nattKeepalivePacket().hashCode())
+ }
+}
\ No newline at end of file
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index c21772a..f7ad09c 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -175,6 +175,7 @@
import android.net.ProxyInfo;
import android.net.ResolverParamsParcel;
import android.net.RouteInfo;
+import android.net.RouteInfoParcel;
import android.net.SocketKeepalive;
import android.net.UidRange;
import android.net.Uri;
@@ -427,7 +428,6 @@
public Object getSystemService(String name) {
if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm;
if (Context.NOTIFICATION_SERVICE.equals(name)) return mNotificationManager;
- if (Context.NETWORK_STACK_SERVICE.equals(name)) return mNetworkStack;
if (Context.USER_SERVICE.equals(name)) return mUserManager;
if (Context.ALARM_SERVICE.equals(name)) return mAlarmManager;
if (Context.LOCATION_SERVICE.equals(name)) return mLocationManager;
@@ -2464,8 +2464,8 @@
final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
mServiceContext, "testFactory", filter);
// Register the factory and don't be surprised when the default request arrives.
- testFactory.register();
testFactory.expectAddRequestsWithScores(0);
+ testFactory.register();
testFactory.waitForNetworkRequests(1);
testFactory.setScoreFilter(42);
@@ -6064,6 +6064,7 @@
verify(mBatteryStatsService).noteNetworkInterfaceType(stackedLp.getInterfaceName(),
TYPE_MOBILE);
}
+ reset(mMockNetd);
// Add ipv4 address, expect that clatd and prefix discovery are stopped and stacked
// linkproperties are cleaned up.
@@ -6115,7 +6116,6 @@
networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kNat64Prefix.toString());
-
// Clat iface comes up. Expect stacked link to be added.
clat.interfaceLinkStateChanged(CLAT_PREFIX + MOBILE_IFNAME, true);
networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
@@ -6701,17 +6701,45 @@
}
}
+ private void assertRouteInfoParcelMatches(RouteInfo route, RouteInfoParcel parcel) {
+ assertEquals(route.getDestination().toString(), parcel.destination);
+ assertEquals(route.getInterface(), parcel.ifName);
+ assertEquals(route.getMtu(), parcel.mtu);
+
+ switch (route.getType()) {
+ case RouteInfo.RTN_UNICAST:
+ if (route.hasGateway()) {
+ assertEquals(route.getGateway().getHostAddress(), parcel.nextHop);
+ } else {
+ assertEquals(INetd.NEXTHOP_NONE, parcel.nextHop);
+ }
+ break;
+ case RouteInfo.RTN_UNREACHABLE:
+ assertEquals(INetd.NEXTHOP_UNREACHABLE, parcel.nextHop);
+ break;
+ case RouteInfo.RTN_THROW:
+ assertEquals(INetd.NEXTHOP_THROW, parcel.nextHop);
+ break;
+ default:
+ assertEquals(INetd.NEXTHOP_NONE, parcel.nextHop);
+ break;
+ }
+ }
+
private void assertRoutesAdded(int netId, RouteInfo... routes) throws Exception {
- InOrder inOrder = inOrder(mNetworkManagementService);
+ ArgumentCaptor<RouteInfoParcel> captor = ArgumentCaptor.forClass(RouteInfoParcel.class);
+ verify(mMockNetd, times(routes.length)).networkAddRouteParcel(eq(netId), captor.capture());
for (int i = 0; i < routes.length; i++) {
- inOrder.verify(mNetworkManagementService).addRoute(eq(netId), eq(routes[i]));
+ assertRouteInfoParcelMatches(routes[i], captor.getAllValues().get(i));
}
}
private void assertRoutesRemoved(int netId, RouteInfo... routes) throws Exception {
- InOrder inOrder = inOrder(mNetworkManagementService);
+ ArgumentCaptor<RouteInfoParcel> captor = ArgumentCaptor.forClass(RouteInfoParcel.class);
+ verify(mMockNetd, times(routes.length)).networkRemoveRouteParcel(eq(netId),
+ captor.capture());
for (int i = 0; i < routes.length; i++) {
- inOrder.verify(mNetworkManagementService).removeRoute(eq(netId), eq(routes[i]));
+ assertRouteInfoParcelMatches(routes[i], captor.getAllValues().get(i));
}
}
@@ -6851,7 +6879,7 @@
@Test
public void testCheckConnectivityDiagnosticsPermissionsNetworkAdministrator() throws Exception {
final NetworkCapabilities nc = new NetworkCapabilities();
- nc.setAdministratorUids(Arrays.asList(Process.myUid()));
+ nc.setAdministratorUids(new int[] {Process.myUid()});
final NetworkAgentInfo naiWithUid =
new NetworkAgentInfo(
null, null, null, null, null, nc, 0, mServiceContext, null, null,
@@ -6873,7 +6901,7 @@
public void testCheckConnectivityDiagnosticsPermissionsFails() throws Exception {
final NetworkCapabilities nc = new NetworkCapabilities();
nc.setOwnerUid(Process.myUid());
- nc.setAdministratorUids(Arrays.asList(Process.myUid()));
+ nc.setAdministratorUids(new int[] {Process.myUid()});
final NetworkAgentInfo naiWithUid =
new NetworkAgentInfo(
null, null, null, null, null, nc, 0, mServiceContext, null, null,
@@ -6926,7 +6954,7 @@
argThat(report -> {
final NetworkCapabilities nc = report.getNetworkCapabilities();
return nc.getUids() == null
- && nc.getAdministratorUids().isEmpty()
+ && nc.getAdministratorUids().length == 0
&& nc.getOwnerUid() == Process.INVALID_UID;
}));
}
@@ -6947,7 +6975,7 @@
argThat(report -> {
final NetworkCapabilities nc = report.getNetworkCapabilities();
return nc.getUids() == null
- && nc.getAdministratorUids().isEmpty()
+ && nc.getAdministratorUids().length == 0
&& nc.getOwnerUid() == Process.INVALID_UID;
}));
}
@@ -6977,4 +7005,60 @@
verify(mConnectivityDiagnosticsCallback)
.onNetworkConnectivityReported(eq(n), eq(noConnectivity));
}
+
+ @Test
+ public void testRouteAddDeleteUpdate() throws Exception {
+ final NetworkRequest request = new NetworkRequest.Builder().build();
+ final TestNetworkCallback networkCallback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(request, networkCallback);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ reset(mMockNetd);
+ mCellNetworkAgent.connect(false);
+ networkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+ final int netId = mCellNetworkAgent.getNetwork().netId;
+
+ final String iface = "rmnet_data0";
+ final InetAddress gateway = InetAddress.getByName("fe80::5678");
+ RouteInfo direct = RouteInfo.makeHostRoute(gateway, iface);
+ RouteInfo rio1 = new RouteInfo(new IpPrefix("2001:db8:1::/48"), gateway, iface);
+ RouteInfo rio2 = new RouteInfo(new IpPrefix("2001:db8:2::/48"), gateway, iface);
+ RouteInfo defaultRoute = new RouteInfo((IpPrefix) null, gateway, iface);
+ RouteInfo defaultWithMtu = new RouteInfo(null, gateway, iface, RouteInfo.RTN_UNICAST,
+ 1280 /* mtu */);
+
+ // Send LinkProperties and check that we ask netd to add routes.
+ LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(iface);
+ lp.addRoute(direct);
+ lp.addRoute(rio1);
+ lp.addRoute(defaultRoute);
+ mCellNetworkAgent.sendLinkProperties(lp);
+ networkCallback.expectLinkPropertiesThat(mCellNetworkAgent, x -> x.getRoutes().size() == 3);
+
+ assertRoutesAdded(netId, direct, rio1, defaultRoute);
+ reset(mMockNetd);
+
+ // Send updated LinkProperties and check that we ask netd to add, remove, update routes.
+ assertTrue(lp.getRoutes().contains(defaultRoute));
+ lp.removeRoute(rio1);
+ lp.addRoute(rio2);
+ lp.addRoute(defaultWithMtu);
+ // Ensure adding the same route with a different MTU replaces the previous route.
+ assertFalse(lp.getRoutes().contains(defaultRoute));
+ assertTrue(lp.getRoutes().contains(defaultWithMtu));
+
+ mCellNetworkAgent.sendLinkProperties(lp);
+ networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
+ x -> x.getRoutes().contains(rio2));
+
+ assertRoutesRemoved(netId, rio1);
+ assertRoutesAdded(netId, rio2);
+
+ ArgumentCaptor<RouteInfoParcel> captor = ArgumentCaptor.forClass(RouteInfoParcel.class);
+ verify(mMockNetd).networkUpdateRouteParcel(eq(netId), captor.capture());
+ assertRouteInfoParcelMatches(defaultWithMtu, captor.getValue());
+
+
+ mCm.unregisterNetworkCallback(networkCallback);
+ }
}