Merge "Listen EXTERNAL_APPLICATIONS_AVAILABLE intent"
diff --git a/framework/src/android/net/ConnectivitySettingsManager.java b/framework/src/android/net/ConnectivitySettingsManager.java
index 085de6b..8fc0065 100644
--- a/framework/src/android/net/ConnectivitySettingsManager.java
+++ b/framework/src/android/net/ConnectivitySettingsManager.java
@@ -29,6 +29,8 @@
import android.annotation.SystemApi;
import android.content.Context;
import android.net.ConnectivityManager.MultipathPreference;
+import android.os.Binder;
+import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
import android.provider.Settings;
@@ -1039,6 +1041,15 @@
return getUidSetFromString(uidList);
}
+ private static boolean isCallingFromSystem() {
+ final int uid = Binder.getCallingUid();
+ final int pid = Binder.getCallingPid();
+ if (uid == Process.SYSTEM_UID && pid == Process.myPid()) {
+ return true;
+ }
+ return false;
+ }
+
/**
* Set the list of uids(from {@link Settings}) that is allowed to use restricted networks.
*
@@ -1047,6 +1058,15 @@
*/
public static void setUidsAllowedOnRestrictedNetworks(@NonNull Context context,
@NonNull Set<Integer> uidList) {
+ final boolean calledFromSystem = isCallingFromSystem();
+ if (!calledFromSystem) {
+ // Enforce NETWORK_SETTINGS check if it's debug build. This is for MTS test only.
+ if (!Build.isDebuggable()) {
+ throw new SecurityException("Only system can set this setting.");
+ }
+ context.enforceCallingOrSelfPermission(android.Manifest.permission.NETWORK_SETTINGS,
+ "Requires NETWORK_SETTINGS permission");
+ }
final String uids = getUidStringFromSet(uidList);
Settings.Global.putString(context.getContentResolver(), UIDS_ALLOWED_ON_RESTRICTED_NETWORKS,
uids);
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 1a9ff25..3c351ba 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -3261,11 +3261,6 @@
}
case NetworkAgent.EVENT_UNDERLYING_NETWORKS_CHANGED: {
// TODO: prevent loops, e.g., if a network declares itself as underlying.
- if (!nai.supportsUnderlyingNetworks()) {
- Log.wtf(TAG, "Non-virtual networks cannot have underlying networks");
- break;
- }
-
final List<Network> underlying = (List<Network>) arg.second;
if (isLegacyLockdownNai(nai)
@@ -5304,12 +5299,12 @@
* information, e.g underlying ifaces.
*/
private UnderlyingNetworkInfo createVpnInfo(NetworkAgentInfo nai) {
- if (!nai.isVPN()) return null;
-
Network[] underlyingNetworks = nai.declaredUnderlyingNetworks;
// see VpnService.setUnderlyingNetworks()'s javadoc about how to interpret
// the underlyingNetworks list.
- if (underlyingNetworks == null) {
+ // TODO: stop using propagateUnderlyingCapabilities here, for example, by always
+ // initializing NetworkAgentInfo#declaredUnderlyingNetworks to an empty array.
+ if (underlyingNetworks == null && nai.propagateUnderlyingCapabilities()) {
final NetworkAgentInfo defaultNai = getDefaultNetworkForUid(
nai.networkCapabilities.getOwnerUid());
if (defaultNai != null) {
@@ -5358,7 +5353,7 @@
private boolean hasUnderlyingNetwork(NetworkAgentInfo nai, Network network) {
// TODO: support more than one level of underlying networks, either via a fixed-depth search
// (e.g., 2 levels of underlying networks), or via loop detection, or....
- if (!nai.supportsUnderlyingNetworks()) return false;
+ if (!nai.propagateUnderlyingCapabilities()) return false;
final Network[] underlying = underlyingNetworksOrDefault(
nai.networkCapabilities.getOwnerUid(), nai.declaredUnderlyingNetworks);
return CollectionUtils.contains(underlying, network);
@@ -7331,7 +7326,7 @@
newNc.addCapability(NET_CAPABILITY_NOT_ROAMING);
}
- if (nai.supportsUnderlyingNetworks()) {
+ if (nai.propagateUnderlyingCapabilities()) {
applyUnderlyingCapabilities(nai.declaredUnderlyingNetworks, nai.declaredCapabilities,
newNc);
}
@@ -8473,7 +8468,7 @@
networkAgent.networkCapabilities.addCapability(NET_CAPABILITY_FOREGROUND);
if (!createNativeNetwork(networkAgent)) return;
- if (networkAgent.supportsUnderlyingNetworks()) {
+ if (networkAgent.propagateUnderlyingCapabilities()) {
// Initialize the network's capabilities to their starting values according to the
// underlying networks. This ensures that the capabilities are correct before
// anything happens to the network.
@@ -9446,7 +9441,7 @@
private boolean ownsVpnRunningOverNetwork(int uid, Network network) {
for (NetworkAgentInfo virtual : mNetworkAgentInfos) {
- if (virtual.supportsUnderlyingNetworks()
+ if (virtual.propagateUnderlyingCapabilities()
&& virtual.networkCapabilities.getOwnerUid() == uid
&& CollectionUtils.contains(virtual.declaredUnderlyingNetworks, network)) {
return true;
diff --git a/service/src/com/android/server/connectivity/NetworkAgentInfo.java b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
index 18becd4..bbf523a 100644
--- a/service/src/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
@@ -157,8 +157,8 @@
@NonNull public NetworkCapabilities networkCapabilities;
@NonNull public final NetworkAgentConfig networkAgentConfig;
- // Underlying networks declared by the agent. Only set if supportsUnderlyingNetworks is true.
- // The networks in this list might be declared by a VPN app using setUnderlyingNetworks and are
+ // Underlying networks declared by the agent.
+ // The networks in this list might be declared by a VPN using setUnderlyingNetworks and are
// not guaranteed to be current or correct, or even to exist.
//
// This array is read and iterated on multiple threads with no locking so its contents must
@@ -168,7 +168,7 @@
// The capabilities originally announced by the NetworkAgent, regardless of any capabilities
// that were added or removed due to this network's underlying networks.
- // Only set if #supportsUnderlyingNetworks is true.
+ // Only set if #propagateUnderlyingCapabilities is true.
public @Nullable NetworkCapabilities declaredCapabilities;
// Indicates if netd has been told to create this Network. From this point on the appropriate
@@ -898,8 +898,11 @@
return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
}
- /** Whether this network might have underlying networks. Currently only true for VPNs. */
- public boolean supportsUnderlyingNetworks() {
+ /**
+ * Whether this network should propagate the capabilities from its underlying networks.
+ * Currently only true for VPNs.
+ */
+ public boolean propagateUnderlyingCapabilities() {
return isVPN();
}
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index cd5281f..527897e 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -2876,6 +2876,10 @@
public void testUidsAllowedOnRestrictedNetworks() throws Exception {
assumeTrue(TestUtils.shouldTestSApis());
+ // TODO (b/175199465): figure out a reasonable permission check for
+ // setUidsAllowedOnRestrictedNetworks that allows tests but not system-external callers.
+ assumeTrue(Build.isDebuggable());
+
final int uid = mPackageManager.getPackageUid(mContext.getPackageName(), 0 /* flag */);
final Set<Integer> originalUidsAllowedOnRestrictedNetworks =
ConnectivitySettingsManager.getUidsAllowedOnRestrictedNetworks(mContext);
@@ -2883,8 +2887,9 @@
// because it has been just installed to device. In case the uid is existed in setting
// mistakenly, try to remove the uid and set correct uids to setting.
originalUidsAllowedOnRestrictedNetworks.remove(uid);
- ConnectivitySettingsManager.setUidsAllowedOnRestrictedNetworks(mContext,
- originalUidsAllowedOnRestrictedNetworks);
+ runWithShellPermissionIdentity(() ->
+ ConnectivitySettingsManager.setUidsAllowedOnRestrictedNetworks(
+ mContext, originalUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
final Handler h = new Handler(Looper.getMainLooper());
final TestableNetworkCallback testNetworkCb = new TestableNetworkCallback();
@@ -2931,8 +2936,9 @@
final Set<Integer> newUidsAllowedOnRestrictedNetworks =
new ArraySet<>(originalUidsAllowedOnRestrictedNetworks);
newUidsAllowedOnRestrictedNetworks.add(uid);
- ConnectivitySettingsManager.setUidsAllowedOnRestrictedNetworks(mContext,
- newUidsAllowedOnRestrictedNetworks);
+ runWithShellPermissionIdentity(() ->
+ ConnectivitySettingsManager.setUidsAllowedOnRestrictedNetworks(
+ mContext, newUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
// Wait a while for sending allowed uids on the restricted network to netd.
// TODD: Have a significant signal to know the uids has been send to netd.
assertBindSocketToNetworkSuccess(network);
@@ -2941,8 +2947,9 @@
agent.unregister();
// Restore setting.
- ConnectivitySettingsManager.setUidsAllowedOnRestrictedNetworks(mContext,
- originalUidsAllowedOnRestrictedNetworks);
+ runWithShellPermissionIdentity(() ->
+ ConnectivitySettingsManager.setUidsAllowedOnRestrictedNetworks(
+ mContext, originalUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
}
}
}
diff --git a/tests/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/integration/util/com/android/server/NetworkAgentWrapper.java
index 17db179..95ea401 100644
--- a/tests/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -55,6 +55,7 @@
import com.android.testutils.HandlerUtils;
import com.android.testutils.TestableNetworkCallback;
+import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -255,6 +256,15 @@
}
}
+ public void setUnderlyingNetworks(List<Network> underlyingNetworks) {
+ mNetworkAgent.setUnderlyingNetworks(underlyingNetworks);
+ }
+
+ public void setOwnerUid(int uid) {
+ mNetworkCapabilities.setOwnerUid(uid);
+ mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+ }
+
public void connect() {
if (!mConnected.compareAndSet(false /* expect */, true /* update */)) {
// compareAndSet returns false when the value couldn't be updated because it did not
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 6d7d3d2..cec3882 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -6029,7 +6029,7 @@
verify(mStatsManager, atLeastOnce()).notifyNetworkStatus(networksCaptor.capture(),
any(List.class), eq(defaultIface), vpnInfosCaptor.capture());
- assertSameElements(networksCaptor.getValue(), networks);
+ assertSameElements(networks, networksCaptor.getValue());
List<UnderlyingNetworkInfo> infos = vpnInfosCaptor.getValue();
if (vpnUid != null) {
@@ -6234,6 +6234,77 @@
}
@Test
+ public void testNonVpnUnderlyingNetworks() throws Exception {
+ // Ensure wifi and cellular are not torn down.
+ for (int transport : new int[]{TRANSPORT_CELLULAR, TRANSPORT_WIFI}) {
+ final NetworkRequest request = new NetworkRequest.Builder()
+ .addTransportType(transport)
+ .removeCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
+ .build();
+ mCm.requestNetwork(request, new NetworkCallback());
+ }
+
+ // Connect a VCN-managed wifi network.
+ final LinkProperties wifiLp = new LinkProperties();
+ wifiLp.setInterfaceName(WIFI_IFNAME);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp);
+ mWiFiNetworkAgent.removeCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
+ mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+ mWiFiNetworkAgent.connect(true /* validated */);
+
+ final List<Network> none = List.of();
+ expectNotifyNetworkStatus(none, null); // Wifi is not the default network
+
+ // Create a virtual network based on the wifi network.
+ final int ownerUid = 10042;
+ NetworkCapabilities nc = new NetworkCapabilities.Builder()
+ .setOwnerUid(ownerUid)
+ .setAdministratorUids(new int[]{ownerUid})
+ .build();
+ final String vcnIface = "ipsec42";
+ final LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(vcnIface);
+ final TestNetworkAgentWrapper vcn = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, lp, nc);
+ vcn.setUnderlyingNetworks(List.of(mWiFiNetworkAgent.getNetwork()));
+ vcn.connect(false /* validated */);
+
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(callback);
+ callback.expectAvailableCallbacksUnvalidated(vcn);
+
+ // The underlying wifi network's capabilities are not propagated to the virtual network,
+ // but NetworkStatsService is informed of the underlying interface.
+ nc = mCm.getNetworkCapabilities(vcn.getNetwork());
+ assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+ assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED));
+ final List<Network> onlyVcn = List.of(vcn.getNetwork());
+ expectNotifyNetworkStatus(onlyVcn, vcnIface, ownerUid, vcnIface, List.of(WIFI_IFNAME));
+
+ // Add NOT_METERED to the underlying network, check that it is not propagated.
+ mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+ callback.assertNoCallback();
+ nc = mCm.getNetworkCapabilities(vcn.getNetwork());
+ assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED));
+
+ // Switch underlying networks.
+ final LinkProperties cellLp = new LinkProperties();
+ cellLp.setInterfaceName(MOBILE_IFNAME);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
+ mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
+ mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_ROAMING);
+ mCellNetworkAgent.connect(false /* validated */);
+ vcn.setUnderlyingNetworks(List.of(mCellNetworkAgent.getNetwork()));
+
+ // The underlying capability changes do not propagate to the virtual network, but
+ // NetworkStatsService is informed of the new underlying interface.
+ callback.assertNoCallback();
+ nc = mCm.getNetworkCapabilities(vcn.getNetwork());
+ assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+ assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+ expectNotifyNetworkStatus(onlyVcn, vcnIface, ownerUid, vcnIface, List.of(MOBILE_IFNAME));
+ }
+
+ @Test
public void testBasicDnsConfigurationPushed() throws Exception {
setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");