Merge "Add new APIs in NetworkCapabilities to set and get underlying networks"
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);