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
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index d1d51da..cfab872 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -279,6 +279,7 @@
method @Nullable public String getSsid();
method @NonNull public java.util.Set<java.lang.Integer> getSubscriptionIds();
method @NonNull public int[] getTransportTypes();
+ method @Nullable public java.util.List<android.net.Network> 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 @@
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<java.lang.Integer>);
method @NonNull public android.net.NetworkCapabilities.Builder setTransportInfo(@Nullable android.net.TransportInfo);
+ method @NonNull public android.net.NetworkCapabilities.Builder setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>);
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 ae5cfda..e9bcd95 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -42,7 +42,10 @@
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 @@
// 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<Network> mUnderlyingNetworks;
+
/**
* Uid of the app making the request.
*/
@@ -184,6 +192,7 @@
mRequestorUid = Process.INVALID_UID;
mRequestorPackageName = null;
mSubIds = new ArraySet<>();
+ mUnderlyingNetworks = null;
}
/**
@@ -213,6 +222,9 @@
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;
}
/**
@@ -699,6 +711,41 @@
}
/**
+ * Set the underlying networks of this network.
+ *
+ * @param networks The underlying networks of this network.
+ *
+ * @hide
+ */
+ public void setUnderlyingNetworks(@Nullable List<Network> 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 <li>If the list is null, this network hasn't declared underlying networks.</li>
+ * <li>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.</li>
+ * <li>The list can contain multiple underlying networks, e.g. a VPN running over
+ * multiple networks at the same time.</li>
+ *
+ * @hide
+ */
+ @SuppressLint("NullableCollection")
+ @Nullable
+ @SystemApi
+ public List<Network> 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.
*
* @param capability the capabilities to be tested for.
@@ -1901,7 +1948,8 @@
&& equalsPrivateDnsBroken(that)
&& equalsRequestor(that)
&& equalsAdministratorUids(that)
- && equalsSubscriptionIds(that);
+ && equalsSubscriptionIds(that)
+ && equalsUnderlyingNetworks(that);
}
@Override
@@ -1924,7 +1972,8 @@
+ 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 @@
dest.writeInt(mRequestorUid);
dest.writeString(mRequestorPackageName);
dest.writeIntArray(CollectionUtils.toIntArray(mSubIds));
+ dest.writeTypedList(mUnderlyingNetworks);
}
public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR =
@@ -1987,6 +2037,7 @@
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 @@
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();
}
@@ -2796,6 +2857,17 @@
}
/**
+ * Set the underlying networks of this network.
+ *
+ * @param networks The underlying networks of this network.
+ */
+ @NonNull
+ public Builder setUnderlyingNetworks(@Nullable List<Network> networks) {
+ mCaps.setUnderlyingNetworks(networks);
+ return this;
+ }
+
+ /**
* Builds the instance of the capabilities.
*
* @return the built instance of NetworkCapabilities.
diff --git a/tests/common/java/android/net/NetworkCapabilitiesTest.java b/tests/common/java/android/net/NetworkCapabilitiesTest.java
index 502b4f6..a619d6b 100644
--- a/tests/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/common/java/android/net/NetworkCapabilitiesTest.java
@@ -50,6 +50,7 @@
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.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 @@
}
private void testParcelSane(NetworkCapabilities cap) {
- if (isAtLeastS()) {
+ if (isAtLeastT()) {
+ assertParcelSane(cap, 17);
+ } else if (isAtLeastS()) {
assertParcelSane(cap, 16);
} else if (isAtLeastR()) {
assertParcelSane(cap, 15);
@@ -712,6 +717,47 @@
}
@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<Network> inputNetworks = new ArrayList<>();
+ inputNetworks.add(network1);
+ inputNetworks.add(network2);
+ nc.setUnderlyingNetworks(inputNetworks);
+ final ArrayList<Network> 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<Network> inputNetworks = new ArrayList<>();
+ final ArrayList<Network> 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
NetworkCapabilities nc1 = new NetworkCapabilities();
@@ -1109,7 +1155,7 @@
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 @@
.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<Network> 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 @@
assertEquals(ssid, nc.getSsid());
assertEquals(requestUid, nc.getRequestorUid());
assertEquals(packageName, nc.getRequestorPackageName());
+ if (isAtLeastT()) {
+ final List<Network> 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);