diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java index 6f0a4f9744..9086d49231 100644 --- a/core/java/android/net/ConnectivityDiagnosticsManager.java +++ b/core/java/android/net/ConnectivityDiagnosticsManager.java @@ -659,7 +659,8 @@ public class ConnectivityDiagnosticsManager { public abstract static class ConnectivityDiagnosticsCallback { /** * Called when the platform completes a data connectivity check. This will also be invoked - * upon registration with the latest report. + * immediately upon registration for each network matching the request with the latest + * report, if a report has already been generated for that network. * *
The Network specified in the ConnectivityReport may not be active any more when this * method is invoked. diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index 4e4ff4a63a..9ff7ebee6d 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -16,6 +16,8 @@ package android.net; +import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE; + import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -118,7 +120,7 @@ public final class NetworkCapabilities implements Parcelable { mTransportInfo = nc.mTransportInfo; mSignalStrength = nc.mSignalStrength; setUids(nc.mUids); // Will make the defensive copy - setAdministratorUids(nc.mAdministratorUids); + setAdministratorUids(nc.getAdministratorUids()); mOwnerUid = nc.mOwnerUid; mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities; mSSID = nc.mSSID; @@ -919,6 +921,9 @@ public final class NetworkCapabilities implements Parcelable { *
For NetworkCapability instances being sent from the System Server, this value MUST be * 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+. + * + *
When received from an app in a NetworkRequest this is always cleared out by the system + * server. This field is never used for matching NetworkRequests to NetworkAgents. */ @NonNull private int[] mAdministratorUids = new int[0]; @@ -927,10 +932,11 @@ public final class NetworkCapabilities implements Parcelable { * *
UIDs included in administratorUids gain administrator privileges over this Network. * Examples of UIDs that should be included in administratorUids are: + * *
In general, user-supplied networks (such as WiFi networks) do not have an administrator. @@ -938,19 +944,34 @@ public final class NetworkCapabilities implements Parcelable { *
An app is granted owner privileges over Networks that it supplies. The owner UID MUST * always be included in administratorUids. * + *
The administrator UIDs are set by network agents. + * * @param administratorUids the UIDs to be set as administrators of this Network. + * @throws IllegalArgumentException if duplicate UIDs are contained in administratorUids + * @see #mAdministratorUids * @hide */ @NonNull public NetworkCapabilities setAdministratorUids(@NonNull final int[] administratorUids) { mAdministratorUids = Arrays.copyOf(administratorUids, administratorUids.length); + Arrays.sort(mAdministratorUids); + for (int i = 0; i < mAdministratorUids.length - 1; i++) { + if (mAdministratorUids[i] >= mAdministratorUids[i + 1]) { + throw new IllegalArgumentException("All administrator UIDs must be unique"); + } + } return this; } /** * Retrieves the UIDs that are administrators of this Network. * + *
This is only populated in NetworkCapabilities objects that come from network agents for + * networks that are managed by specific apps on the system, such as carrier privileged apps or + * wifi suggestion apps. This will include the network owner. + * * @return the int[] of UIDs that are administrators of this Network + * @see #mAdministratorUids * @hide */ @NonNull @@ -960,6 +981,40 @@ public final class NetworkCapabilities implements Parcelable { return Arrays.copyOf(mAdministratorUids, mAdministratorUids.length); } + /** + * Tests if the set of administrator UIDs of this network is the same as that of the passed one. + * + *
The administrator UIDs must be in sorted order. + * + *
nc is assumed non-null. Else, NPE. + * + * @hide + */ + @VisibleForTesting(visibility = PRIVATE) + public boolean equalsAdministratorUids(@NonNull final NetworkCapabilities nc) { + return Arrays.equals(mAdministratorUids, nc.mAdministratorUids); + } + + /** + * Combine the administrator UIDs of the capabilities. + * + *
This is only legal if either of the administrators lists are empty, or if they are equal. + * Combining administrator UIDs is only possible for combining non-overlapping sets of UIDs. + * + *
If both administrator lists are non-empty but not equal, they conflict with each other. In
+ * this case, it would not make sense to add them together.
+ */
+ private void combineAdministratorUids(@NonNull final NetworkCapabilities nc) {
+ if (nc.mAdministratorUids.length == 0) return;
+ if (mAdministratorUids.length == 0) {
+ mAdministratorUids = Arrays.copyOf(nc.mAdministratorUids, nc.mAdministratorUids.length);
+ return;
+ }
+ if (!equalsAdministratorUids(nc)) {
+ throw new IllegalStateException("Can't combine two different administrator UID lists");
+ }
+ }
+
/**
* Value indicating that link bandwidth is unspecified.
* @hide
@@ -1455,6 +1510,7 @@ public final class NetworkCapabilities implements Parcelable {
combineUids(nc);
combineSSIDs(nc);
combineRequestor(nc);
+ combineAdministratorUids(nc);
}
/**
@@ -1568,7 +1624,8 @@ public final class NetworkCapabilities implements Parcelable {
&& equalsUids(that)
&& equalsSSID(that)
&& equalsPrivateDnsBroken(that)
- && equalsRequestor(that);
+ && equalsRequestor(that)
+ && equalsAdministratorUids(that);
}
@Override
@@ -1588,7 +1645,8 @@ public final class NetworkCapabilities implements Parcelable {
+ Objects.hashCode(mTransportInfo) * 41
+ Objects.hashCode(mPrivateDnsBroken) * 43
+ Objects.hashCode(mRequestorUid) * 47
- + Objects.hashCode(mRequestorPackageName) * 53;
+ + Objects.hashCode(mRequestorPackageName) * 53
+ + Arrays.hashCode(mAdministratorUids) * 59;
}
@Override
@@ -1609,7 +1667,7 @@ public final class NetworkCapabilities implements Parcelable {
dest.writeArraySet(mUids);
dest.writeString(mSSID);
dest.writeBoolean(mPrivateDnsBroken);
- dest.writeIntArray(mAdministratorUids);
+ dest.writeIntArray(getAdministratorUids());
dest.writeInt(mOwnerUid);
dest.writeInt(mRequestorUid);
dest.writeString(mRequestorPackageName);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index d684f0c199..4ab035e7f4 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -653,8 +653,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
final MultipathPolicyTracker mMultipathPolicyTracker;
@VisibleForTesting
- final Map This should only be called from the ConnectivityService thread.
+ *
+ * @hide
+ */
+ public void setConnectivityReport(@NonNull ConnectivityReport connectivityReport) {
+ mConnectivityReport = connectivityReport;
+ }
+
+ /**
+ * Returns the most recent ConnectivityReport for this network, or null if none have been
+ * reported yet.
+ *
+ * This should only be called from the ConnectivityService thread.
+ *
+ * @hide
+ */
+ @Nullable
+ public ConnectivityReport getConnectivityReport() {
+ return mConnectivityReport;
+ }
+
// TODO: Print shorter members first and only print the boolean variable which value is true
// to improve readability.
public String toString() {
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index 916c339811..316a83adf4 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -58,6 +58,7 @@ import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Arrays;
import java.util.Set;
@RunWith(AndroidJUnit4.class)
@@ -280,6 +281,7 @@ public class NetworkCapabilitiesTest {
.addCapability(NET_CAPABILITY_NOT_METERED);
if (isAtLeastR()) {
netCap.setOwnerUid(123);
+ netCap.setAdministratorUids(new int[] {5, 11});
}
assertParcelingIsLossless(netCap);
netCap.setSSID(TEST_SSID);
@@ -439,6 +441,23 @@ public class NetworkCapabilitiesTest {
return range;
}
+ @Test
+ public void testSetAdministratorUids() {
+ NetworkCapabilities nc =
+ new NetworkCapabilities().setAdministratorUids(new int[] {2, 1, 3});
+
+ assertArrayEquals(new int[] {1, 2, 3}, nc.getAdministratorUids());
+ }
+
+ @Test
+ public void testSetAdministratorUidsWithDuplicates() {
+ try {
+ new NetworkCapabilities().setAdministratorUids(new int[] {1, 1});
+ fail("Expected IllegalArgumentException for duplicate uids");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
@Test
public void testCombineCapabilities() {
NetworkCapabilities nc1 = new NetworkCapabilities();
@@ -491,6 +510,25 @@ public class NetworkCapabilitiesTest {
assertFalse(nc2.appliesToUid(12));
assertTrue(nc1.appliesToUid(22));
assertTrue(nc2.appliesToUid(22));
+
+ final int[] adminUids = {3, 6, 12};
+ nc1.setAdministratorUids(adminUids);
+ nc2.combineCapabilities(nc1);
+ assertTrue(nc2.equalsAdministratorUids(nc1));
+ assertArrayEquals(nc2.getAdministratorUids(), adminUids);
+
+ final int[] adminUidsOtherOrder = {3, 12, 6};
+ nc1.setAdministratorUids(adminUids);
+ assertTrue(nc2.equalsAdministratorUids(nc1));
+
+ final int[] adminUids2 = {11, 1, 12, 3, 6};
+ nc1.setAdministratorUids(adminUids2);
+ assertFalse(nc2.equalsAdministratorUids(nc1));
+ assertFalse(Arrays.equals(nc2.getAdministratorUids(), adminUids2));
+ try {
+ nc2.combineCapabilities(nc1);
+ fail("Shouldn't be able to combine different lists of admin UIDs");
+ } catch (IllegalStateException expected) { }
}
@Test
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 86d8a820e4..b02398d1f1 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -317,6 +317,8 @@ public class ConnectivityServiceTest {
private static final String TEST_PACKAGE_NAME = "com.android.test.package";
private static final String[] EMPTY_STRING_ARRAY = new String[0];
+ private static final String INTERFACE_NAME = "interface";
+
private MockContext mServiceContext;
private HandlerThread mCsHandlerThread;
private ConnectivityService mService;
@@ -6748,16 +6750,12 @@ public class ConnectivityServiceTest {
verify(mIBinder).linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
verify(mConnectivityDiagnosticsCallback).asBinder();
- assertTrue(
- mService.mConnectivityDiagnosticsCallbacks.containsKey(
- mConnectivityDiagnosticsCallback));
+ assertTrue(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder));
mService.unregisterConnectivityDiagnosticsCallback(mConnectivityDiagnosticsCallback);
verify(mIBinder, timeout(TIMEOUT_MS))
.unlinkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
- assertFalse(
- mService.mConnectivityDiagnosticsCallbacks.containsKey(
- mConnectivityDiagnosticsCallback));
+ assertFalse(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder));
verify(mConnectivityDiagnosticsCallback, atLeastOnce()).asBinder();
}
@@ -6775,9 +6773,7 @@ public class ConnectivityServiceTest {
verify(mIBinder).linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
verify(mConnectivityDiagnosticsCallback).asBinder();
- assertTrue(
- mService.mConnectivityDiagnosticsCallbacks.containsKey(
- mConnectivityDiagnosticsCallback));
+ assertTrue(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder));
// Register the same callback again
mService.registerConnectivityDiagnosticsCallback(
@@ -6786,9 +6782,7 @@ public class ConnectivityServiceTest {
// Block until all other events are done processing.
HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
- assertTrue(
- mService.mConnectivityDiagnosticsCallbacks.containsKey(
- mConnectivityDiagnosticsCallback));
+ assertTrue(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder));
}
@Test
@@ -6915,6 +6909,38 @@ public class ConnectivityServiceTest {
mContext.getOpPackageName()));
}
+ @Test
+ public void testRegisterConnectivityDiagnosticsCallbackCallsOnConnectivityReport()
+ throws Exception {
+ // Set up the Network, which leads to a ConnectivityReport being cached for the network.
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(callback);
+ final LinkProperties linkProperties = new LinkProperties();
+ linkProperties.setInterfaceName(INTERFACE_NAME);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, linkProperties);
+ mCellNetworkAgent.connect(true);
+ callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ callback.assertNoCallback();
+
+ final NetworkRequest request = new NetworkRequest.Builder().build();
+ when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
+
+ mServiceContext.setPermission(
+ android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
+
+ mService.registerConnectivityDiagnosticsCallback(
+ mConnectivityDiagnosticsCallback, request, mContext.getPackageName());
+
+ // Block until all other events are done processing.
+ HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+
+ verify(mConnectivityDiagnosticsCallback)
+ .onConnectivityReportAvailable(argThat(report -> {
+ return INTERFACE_NAME.equals(report.getLinkProperties().getInterfaceName())
+ && report.getNetworkCapabilities().hasTransport(TRANSPORT_CELLULAR);
+ }));
+ }
+
private void setUpConnectivityDiagnosticsCallback() throws Exception {
final NetworkRequest request = new NetworkRequest.Builder().build();
when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);