Merge changes I2431a6d2,I9096969a,I748bd9de,Ia5387ca2,I803bdec8, ... into rvc-dev

* changes:
  Update CS helper for clearing NetworkCapabilities UIDs.
  Simplify unregister logic for Connectivity Diagnostics callbacks.
  Clarify comments for Connectivity Diagnostics reports.
  Sort administrator UIDs for NetworkCapabilities.
  Add combine() and equals() for NetworkCapabilities admin UIDs.
  Use IBinder as key for ConnectivityDiagnostics storage in CS.
  Decrement networkRequestPerUid when callbacks are unregistered.
  Invoke onConnectivityReport on registering ConnectivityDiagnostics.
This commit is contained in:
Cody Kesting
2020-04-01 01:13:11 +00:00
committed by Android (Google) Code Review
6 changed files with 228 additions and 38 deletions

View File

@@ -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.
*
* <p>The Network specified in the ConnectivityReport may not be active any more when this
* method is invoked.

View File

@@ -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 {
* <p>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+.
*
* <p>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 {
*
* <p>UIDs included in administratorUids gain administrator privileges over this Network.
* Examples of UIDs that should be included in administratorUids are:
*
* <ul>
* <li>Carrier apps with privileges for the relevant subscription
* <li>Active VPN apps
* <li>Other application groups with a particular Network-related role
* <li>Carrier apps with privileges for the relevant subscription
* <li>Active VPN apps
* <li>Other application groups with a particular Network-related role
* </ul>
*
* <p>In general, user-supplied networks (such as WiFi networks) do not have an administrator.
@@ -938,19 +944,34 @@ public final class NetworkCapabilities implements Parcelable {
* <p>An app is granted owner privileges over Networks that it supplies. The owner UID MUST
* always be included in administratorUids.
*
* <p>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.
*
* <p>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.
*
* <p>The administrator UIDs must be in sorted order.
*
* <p>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.
*
* <p>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.
*
* <p>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);

View File

@@ -653,8 +653,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
final MultipathPolicyTracker mMultipathPolicyTracker;
@VisibleForTesting
final Map<IConnectivityDiagnosticsCallback, ConnectivityDiagnosticsCallbackInfo>
mConnectivityDiagnosticsCallbacks = new HashMap<>();
final Map<IBinder, ConnectivityDiagnosticsCallbackInfo> mConnectivityDiagnosticsCallbacks =
new HashMap<>();
/**
* Implements support for the legacy "one network per network type" model.
@@ -7835,11 +7835,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
ensureRunningOnConnectivityServiceThread();
final IConnectivityDiagnosticsCallback cb = cbInfo.mCb;
final IBinder iCb = cb.asBinder();
final NetworkRequestInfo nri = cbInfo.mRequestInfo;
// This means that the client registered the same callback multiple times. Do
// not override the previous entry, and exit silently.
if (mConnectivityDiagnosticsCallbacks.containsKey(cb)) {
if (mConnectivityDiagnosticsCallbacks.containsKey(iCb)) {
if (VDBG) log("Diagnostics callback is already registered");
// Decrement the reference count for this NetworkRequestInfo. The reference count is
@@ -7849,40 +7850,75 @@ public class ConnectivityService extends IConnectivityManager.Stub
return;
}
mConnectivityDiagnosticsCallbacks.put(cb, cbInfo);
mConnectivityDiagnosticsCallbacks.put(iCb, cbInfo);
try {
cb.asBinder().linkToDeath(cbInfo, 0);
iCb.linkToDeath(cbInfo, 0);
} catch (RemoteException e) {
cbInfo.binderDied();
return;
}
// Once registered, provide ConnectivityReports for matching Networks
final List<NetworkAgentInfo> matchingNetworks = new ArrayList<>();
synchronized (mNetworkForNetId) {
for (int i = 0; i < mNetworkForNetId.size(); i++) {
final NetworkAgentInfo nai = mNetworkForNetId.valueAt(i);
if (nai.satisfies(nri.request)) {
matchingNetworks.add(nai);
}
}
}
for (final NetworkAgentInfo nai : matchingNetworks) {
final ConnectivityReport report = nai.getConnectivityReport();
if (report == null) {
continue;
}
if (!checkConnectivityDiagnosticsPermissions(
nri.mPid, nri.mUid, nai, cbInfo.mCallingPackageName)) {
continue;
}
try {
cb.onConnectivityReportAvailable(report);
} catch (RemoteException e) {
// Exception while sending the ConnectivityReport. Move on to the next network.
}
}
}
private void handleUnregisterConnectivityDiagnosticsCallback(
@NonNull IConnectivityDiagnosticsCallback cb, int uid) {
ensureRunningOnConnectivityServiceThread();
final IBinder iCb = cb.asBinder();
if (!mConnectivityDiagnosticsCallbacks.containsKey(cb)) {
final ConnectivityDiagnosticsCallbackInfo cbInfo =
mConnectivityDiagnosticsCallbacks.remove(iCb);
if (cbInfo == null) {
if (VDBG) log("Removing diagnostics callback that is not currently registered");
return;
}
final NetworkRequestInfo nri = mConnectivityDiagnosticsCallbacks.get(cb).mRequestInfo;
final NetworkRequestInfo nri = cbInfo.mRequestInfo;
if (uid != nri.mUid) {
if (VDBG) loge("Different uid than registrant attempting to unregister cb");
return;
}
cb.asBinder().unlinkToDeath(mConnectivityDiagnosticsCallbacks.remove(cb), 0);
// Decrement the reference count for this NetworkRequestInfo. The reference count is
// incremented when the NetworkRequestInfo is created as part of
// enforceRequestCountLimit().
decrementNetworkRequestPerUidCount(nri);
iCb.unlinkToDeath(cbInfo, 0);
}
private void handleNetworkTestedWithExtras(
@NonNull ConnectivityReportEvent reportEvent, @NonNull PersistableBundle extras) {
final NetworkAgentInfo nai = reportEvent.mNai;
final NetworkCapabilities networkCapabilities =
new NetworkCapabilities(nai.networkCapabilities);
clearNetworkCapabilitiesUids(networkCapabilities);
getNetworkCapabilitiesWithoutUids(nai.networkCapabilities);
final ConnectivityReport report =
new ConnectivityReport(
reportEvent.mNai.network,
@@ -7890,6 +7926,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
nai.linkProperties,
networkCapabilities,
extras);
nai.setConnectivityReport(report);
final List<IConnectivityDiagnosticsCallback> results =
getMatchingPermissionedCallbacks(nai);
for (final IConnectivityDiagnosticsCallback cb : results) {
@@ -7905,8 +7942,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
@NonNull NetworkAgentInfo nai, long timestampMillis, int detectionMethod,
@NonNull PersistableBundle extras) {
final NetworkCapabilities networkCapabilities =
new NetworkCapabilities(nai.networkCapabilities);
clearNetworkCapabilitiesUids(networkCapabilities);
getNetworkCapabilitiesWithoutUids(nai.networkCapabilities);
final DataStallReport report =
new DataStallReport(
nai.network,
@@ -7939,23 +7975,25 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
private void clearNetworkCapabilitiesUids(@NonNull NetworkCapabilities nc) {
nc.setUids(null);
nc.setAdministratorUids(new int[0]);
nc.setOwnerUid(Process.INVALID_UID);
private NetworkCapabilities getNetworkCapabilitiesWithoutUids(@NonNull NetworkCapabilities nc) {
final NetworkCapabilities sanitized = new NetworkCapabilities(nc);
sanitized.setUids(null);
sanitized.setAdministratorUids(new int[0]);
sanitized.setOwnerUid(Process.INVALID_UID);
return sanitized;
}
private List<IConnectivityDiagnosticsCallback> getMatchingPermissionedCallbacks(
@NonNull NetworkAgentInfo nai) {
final List<IConnectivityDiagnosticsCallback> results = new ArrayList<>();
for (Entry<IConnectivityDiagnosticsCallback, ConnectivityDiagnosticsCallbackInfo> entry :
for (Entry<IBinder, ConnectivityDiagnosticsCallbackInfo> entry :
mConnectivityDiagnosticsCallbacks.entrySet()) {
final ConnectivityDiagnosticsCallbackInfo cbInfo = entry.getValue();
final NetworkRequestInfo nri = cbInfo.mRequestInfo;
if (nai.satisfies(nri.request)) {
if (checkConnectivityDiagnosticsPermissions(
nri.mPid, nri.mUid, nai, cbInfo.mCallingPackageName)) {
results.add(entry.getKey());
results.add(entry.getValue().mCb);
}
}
}

View File

@@ -16,6 +16,7 @@
package com.android.server.connectivity;
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
import static android.net.NetworkCapabilities.transportNamesOf;
import android.annotation.NonNull;
@@ -243,6 +244,10 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
// How many of the satisfied requests are of type BACKGROUND_REQUEST.
private int mNumBackgroundNetworkRequests = 0;
// The last ConnectivityReport made available for this network. This value is only null before a
// report is generated. Once non-null, it will never be null again.
@Nullable private ConnectivityReport mConnectivityReport;
public final Messenger messenger;
public final AsyncChannel asyncChannel;
@@ -621,6 +626,30 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
}
/**
* Sets the most recent ConnectivityReport for this network.
*
* <p>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.
*
* <p>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() {

View File

@@ -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

View File

@@ -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);