From 8b2dfa7c0275b42e2a67e95a7ca965e47b998231 Mon Sep 17 00:00:00 2001 From: lucaslin Date: Fri, 15 Oct 2021 18:07:12 +0800 Subject: [PATCH] Add new APIs in NetworkCapabilities to set and get underlying networks Previously, the caller can only know about the transport type of the underlying network. The information might not be enough if the device support WiFi STA+STA. Thus, provide an API for the caller to get the correct underlying network. Bug: 191918368 Test: atest FrameworksNetTests:NetworkCapabilitiesTest Change-Id: I7752b2356770f4572f6ca4cbaecaa45c09d6d72f --- framework/api/system-current.txt | 2 + .../src/android/net/NetworkCapabilities.java | 76 ++++++++++++++++++- .../android/net/NetworkCapabilitiesTest.java | 65 +++++++++++++++- 3 files changed, 137 insertions(+), 6 deletions(-) diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt index d1d51da151..cfab8724fd 100644 --- a/framework/api/system-current.txt +++ b/framework/api/system-current.txt @@ -279,6 +279,7 @@ package android.net { method @Nullable public String getSsid(); method @NonNull public java.util.Set getSubscriptionIds(); method @NonNull public int[] getTransportTypes(); + method @Nullable public java.util.List getUnderlyingNetworks(); method public boolean isPrivateDnsBroken(); method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities); field public static final int NET_CAPABILITY_BIP = 31; // 0x1f @@ -309,6 +310,7 @@ package android.net { method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setSsid(@Nullable String); method @NonNull public android.net.NetworkCapabilities.Builder setSubscriptionIds(@NonNull java.util.Set); method @NonNull public android.net.NetworkCapabilities.Builder setTransportInfo(@Nullable android.net.TransportInfo); + method @NonNull public android.net.NetworkCapabilities.Builder setUnderlyingNetworks(@Nullable java.util.List); method @NonNull public static android.net.NetworkCapabilities.Builder withoutDefaultCapabilities(); } diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java index ae5cfda76d..e9bcd95a5e 100644 --- a/framework/src/android/net/NetworkCapabilities.java +++ b/framework/src/android/net/NetworkCapabilities.java @@ -42,7 +42,10 @@ import com.android.net.module.util.NetworkCapabilitiesUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Objects; import java.util.Set; import java.util.StringJoiner; @@ -129,6 +132,11 @@ public final class NetworkCapabilities implements Parcelable { // Set to true when private DNS is broken. private boolean mPrivateDnsBroken; + // Underlying networks, if any. VPNs and VCNs typically have underlying networks. + // This is an unmodifiable list and it will be returned as is in the getter. + @Nullable + private List mUnderlyingNetworks; + /** * Uid of the app making the request. */ @@ -184,6 +192,7 @@ public final class NetworkCapabilities implements Parcelable { mRequestorUid = Process.INVALID_UID; mRequestorPackageName = null; mSubIds = new ArraySet<>(); + mUnderlyingNetworks = null; } /** @@ -213,6 +222,9 @@ public final class NetworkCapabilities implements Parcelable { mRequestorUid = nc.mRequestorUid; mRequestorPackageName = nc.mRequestorPackageName; mSubIds = new ArraySet<>(nc.mSubIds); + // mUnderlyingNetworks is an unmodifiable list if non-null, so a defensive copy is not + // necessary. + mUnderlyingNetworks = nc.mUnderlyingNetworks; } /** @@ -698,6 +710,41 @@ public final class NetworkCapabilities implements Parcelable { setCapabilities(capabilities, new int[] {}); } + /** + * Set the underlying networks of this network. + * + * @param networks The underlying networks of this network. + * + * @hide + */ + public void setUnderlyingNetworks(@Nullable List networks) { + mUnderlyingNetworks = + (networks == null) ? null : Collections.unmodifiableList(new ArrayList<>(networks)); + } + + /** + * Get the underlying networks of this network. If the caller is not system privileged, this is + * always redacted to null and it will be never useful to the caller. + * + * @return
  • If the list is null, this network hasn't declared underlying networks.
  • + *
  • If the list is empty, this network has declared that it has no underlying + * networks or it doesn't run on any of the available networks.
  • + *
  • The list can contain multiple underlying networks, e.g. a VPN running over + * multiple networks at the same time.
  • + * + * @hide + */ + @SuppressLint("NullableCollection") + @Nullable + @SystemApi + public List getUnderlyingNetworks() { + return mUnderlyingNetworks; + } + + private boolean equalsUnderlyingNetworks(@NonNull NetworkCapabilities nc) { + return Objects.equals(getUnderlyingNetworks(), nc.getUnderlyingNetworks()); + } + /** * Tests for the presence of a capability on this instance. * @@ -1901,7 +1948,8 @@ public final class NetworkCapabilities implements Parcelable { && equalsPrivateDnsBroken(that) && equalsRequestor(that) && equalsAdministratorUids(that) - && equalsSubscriptionIds(that); + && equalsSubscriptionIds(that) + && equalsUnderlyingNetworks(that); } @Override @@ -1924,7 +1972,8 @@ public final class NetworkCapabilities implements Parcelable { + Objects.hashCode(mRequestorUid) * 53 + Objects.hashCode(mRequestorPackageName) * 59 + Arrays.hashCode(mAdministratorUids) * 61 - + Objects.hashCode(mSubIds) * 67; + + Objects.hashCode(mSubIds) * 67 + + Objects.hashCode(mUnderlyingNetworks) * 71; } @Override @@ -1959,6 +2008,7 @@ public final class NetworkCapabilities implements Parcelable { dest.writeInt(mRequestorUid); dest.writeString(mRequestorPackageName); dest.writeIntArray(CollectionUtils.toIntArray(mSubIds)); + dest.writeTypedList(mUnderlyingNetworks); } public static final @android.annotation.NonNull Creator CREATOR = @@ -1987,6 +2037,7 @@ public final class NetworkCapabilities implements Parcelable { for (int i = 0; i < subIdInts.length; i++) { netCap.mSubIds.add(subIdInts[i]); } + netCap.setUnderlyingNetworks(in.createTypedArrayList(Network.CREATOR)); return netCap; } @Override @@ -2078,6 +2129,16 @@ public final class NetworkCapabilities implements Parcelable { sb.append(" SubscriptionIds: ").append(mSubIds); } + if (mUnderlyingNetworks != null && mUnderlyingNetworks.size() > 0) { + sb.append(" Underlying networks: ["); + final StringJoiner joiner = new StringJoiner(","); + for (int i = 0; i < mUnderlyingNetworks.size(); i++) { + joiner.add(mUnderlyingNetworks.get(i).toString()); + } + sb.append(joiner.toString()); + sb.append("]"); + } + sb.append("]"); return sb.toString(); } @@ -2795,6 +2856,17 @@ public final class NetworkCapabilities implements Parcelable { return this; } + /** + * Set the underlying networks of this network. + * + * @param networks The underlying networks of this network. + */ + @NonNull + public Builder setUnderlyingNetworks(@Nullable List networks) { + mCaps.setUnderlyingNetworks(networks); + return this; + } + /** * Builds the instance of the capabilities. * diff --git a/tests/common/java/android/net/NetworkCapabilitiesTest.java b/tests/common/java/android/net/NetworkCapabilitiesTest.java index 502b4f6b58..a619d6b2d2 100644 --- a/tests/common/java/android/net/NetworkCapabilitiesTest.java +++ b/tests/common/java/android/net/NetworkCapabilitiesTest.java @@ -50,6 +50,7 @@ import static android.os.Process.INVALID_UID; import static com.android.modules.utils.build.SdkLevel.isAtLeastR; import static com.android.modules.utils.build.SdkLevel.isAtLeastS; +import static com.android.modules.utils.build.SdkLevel.isAtLeastT; import static com.android.testutils.MiscAsserts.assertEmpty; import static com.android.testutils.MiscAsserts.assertThrows; import static com.android.testutils.ParcelUtils.assertParcelSane; @@ -84,7 +85,9 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Set; @RunWith(AndroidJUnit4.class) @@ -342,7 +345,9 @@ public class NetworkCapabilitiesTest { } private void testParcelSane(NetworkCapabilities cap) { - if (isAtLeastS()) { + if (isAtLeastT()) { + assertParcelSane(cap, 17); + } else if (isAtLeastS()) { assertParcelSane(cap, 16); } else if (isAtLeastR()) { assertParcelSane(cap, 15); @@ -711,6 +716,47 @@ public class NetworkCapabilitiesTest { } } + @Test + public void testUnderlyingNetworks() { + assumeTrue(isAtLeastT()); + final NetworkCapabilities nc = new NetworkCapabilities(); + final Network network1 = new Network(100); + final Network network2 = new Network(101); + final ArrayList inputNetworks = new ArrayList<>(); + inputNetworks.add(network1); + inputNetworks.add(network2); + nc.setUnderlyingNetworks(inputNetworks); + final ArrayList outputNetworks = new ArrayList<>(nc.getUnderlyingNetworks()); + assertEquals(network1, outputNetworks.get(0)); + assertEquals(network2, outputNetworks.get(1)); + nc.setUnderlyingNetworks(null); + assertNull(nc.getUnderlyingNetworks()); + } + + @Test + public void testEqualsForUnderlyingNetworks() { + assumeTrue(isAtLeastT()); + final NetworkCapabilities nc1 = new NetworkCapabilities(); + final NetworkCapabilities nc2 = new NetworkCapabilities(); + assertEquals(nc1, nc2); + final Network network = new Network(100); + final ArrayList inputNetworks = new ArrayList<>(); + final ArrayList emptyList = new ArrayList<>(); + inputNetworks.add(network); + nc1.setUnderlyingNetworks(inputNetworks); + assertNotEquals(nc1, nc2); + nc2.setUnderlyingNetworks(inputNetworks); + assertEquals(nc1, nc2); + nc1.setUnderlyingNetworks(emptyList); + assertNotEquals(nc1, nc2); + nc2.setUnderlyingNetworks(emptyList); + assertEquals(nc1, nc2); + nc1.setUnderlyingNetworks(null); + assertNotEquals(nc1, nc2); + nc2.setUnderlyingNetworks(null); + assertEquals(nc1, nc2); + } + @Test public void testSetNetworkSpecifierOnMultiTransportNc() { // Sequence 1: Transport + Transport + NetworkSpecifier @@ -1109,7 +1155,7 @@ public class NetworkCapabilitiesTest { final TransportInfo transportInfo = new TransportInfo() {}; final String ssid = "TEST_SSID"; final String packageName = "com.google.test.networkcapabilities"; - final NetworkCapabilities nc = new NetworkCapabilities.Builder() + final NetworkCapabilities.Builder capBuilder = new NetworkCapabilities.Builder() .addTransportType(TRANSPORT_WIFI) .addTransportType(TRANSPORT_CELLULAR) .removeTransportType(TRANSPORT_CELLULAR) @@ -1125,8 +1171,14 @@ public class NetworkCapabilitiesTest { .setSignalStrength(signalStrength) .setSsid(ssid) .setRequestorUid(requestUid) - .setRequestorPackageName(packageName) - .build(); + .setRequestorPackageName(packageName); + final Network network1 = new Network(100); + final Network network2 = new Network(101); + final List inputNetworks = List.of(network1, network2); + if (isAtLeastT()) { + capBuilder.setUnderlyingNetworks(inputNetworks); + } + final NetworkCapabilities nc = capBuilder.build(); assertEquals(1, nc.getTransportTypes().length); assertEquals(TRANSPORT_WIFI, nc.getTransportTypes()[0]); assertTrue(nc.hasCapability(NET_CAPABILITY_EIMS)); @@ -1144,6 +1196,11 @@ public class NetworkCapabilitiesTest { assertEquals(ssid, nc.getSsid()); assertEquals(requestUid, nc.getRequestorUid()); assertEquals(packageName, nc.getRequestorPackageName()); + if (isAtLeastT()) { + final List outputNetworks = nc.getUnderlyingNetworks(); + assertEquals(network1, outputNetworks.get(0)); + assertEquals(network2, outputNetworks.get(1)); + } // Cannot assign null into NetworkCapabilities.Builder try { final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(null);