Merge "Send special permission information to netd"
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 68ac46c..92b30a4 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -678,11 +678,20 @@
@Deprecated
public static final int TYPE_VPN = 17;
- /** {@hide} */
- public static final int MAX_RADIO_TYPE = TYPE_VPN;
+ /**
+ * A network that is exclusively meant to be used for testing
+ *
+ * @deprecated Use {@link NetworkCapabilities} instead.
+ * @hide
+ */
+ @Deprecated
+ public static final int TYPE_TEST = 18; // TODO: Remove this once NetworkTypes are unused.
/** {@hide} */
- public static final int MAX_NETWORK_TYPE = TYPE_VPN;
+ public static final int MAX_RADIO_TYPE = TYPE_TEST;
+
+ /** {@hide} */
+ public static final int MAX_NETWORK_TYPE = TYPE_TEST;
private static final int MIN_NETWORK_TYPE = TYPE_MOBILE;
@@ -3911,15 +3920,16 @@
*
* <p>This endpoint is exclusively for use by the NetworkStack and is protected by the
* corresponding permission.
+ * @param network Network on which the captive portal was detected.
* @param appExtras Extras to include in the app start intent.
* @hide
*/
@SystemApi
@TestApi
@RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
- public void startCaptivePortalApp(Bundle appExtras) {
+ public void startCaptivePortalApp(Network network, Bundle appExtras) {
try {
- mService.startCaptivePortalAppInternal(appExtras);
+ mService.startCaptivePortalAppInternal(network, appExtras);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 92a5839..83bb3a0 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -168,7 +168,7 @@
void setAcceptUnvalidated(in Network network, boolean accept, boolean always);
void setAvoidUnvalidated(in Network network);
void startCaptivePortalApp(in Network network);
- void startCaptivePortalAppInternal(in Bundle appExtras);
+ void startCaptivePortalAppInternal(in Network network, in Bundle appExtras);
boolean getAvoidBadWifi();
int getMultipathPreference(in Network Network);
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 7e9bda1..1d2d81d 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -597,6 +597,7 @@
TRANSPORT_VPN,
TRANSPORT_WIFI_AWARE,
TRANSPORT_LOWPAN,
+ TRANSPORT_TEST,
})
public @interface Transport { }
@@ -635,10 +636,18 @@
*/
public static final int TRANSPORT_LOWPAN = 6;
+ /**
+ * Indicates this network uses a Test-only virtual interface as a transport.
+ *
+ * @hide
+ */
+ @TestApi
+ public static final int TRANSPORT_TEST = 7;
+
/** @hide */
public static final int MIN_TRANSPORT = TRANSPORT_CELLULAR;
/** @hide */
- public static final int MAX_TRANSPORT = TRANSPORT_LOWPAN;
+ public static final int MAX_TRANSPORT = TRANSPORT_TEST;
/** @hide */
public static boolean isValidTransport(@Transport int transportType) {
@@ -652,7 +661,8 @@
"ETHERNET",
"VPN",
"WIFI_AWARE",
- "LOWPAN"
+ "LOWPAN",
+ "TEST"
};
/**
diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java
index daa2640..c0487b5 100644
--- a/core/java/android/net/NetworkMisc.java
+++ b/core/java/android/net/NetworkMisc.java
@@ -46,7 +46,7 @@
/**
* Set if the user desires to use this network even if it is unvalidated. This field has meaning
- * only if {#link explicitlySelected} is true. If it is, this field must also be set to the
+ * only if {@link explicitlySelected} is true. If it is, this field must also be set to the
* appropriate value based on previous user choice.
*/
public boolean acceptUnvalidated;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 9b4cd23..b5fcde4 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -57,8 +57,10 @@
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.database.ContentObserver;
+import android.net.CaptivePortal;
import android.net.ConnectionInfo;
import android.net.ConnectivityManager;
+import android.net.ICaptivePortal;
import android.net.IConnectivityManager;
import android.net.IIpConnectivityMetrics;
import android.net.INetd;
@@ -85,6 +87,7 @@
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
import android.net.NetworkStack;
+import android.net.NetworkStackClient;
import android.net.NetworkState;
import android.net.NetworkUtils;
import android.net.NetworkWatchlistManager;
@@ -916,7 +919,8 @@
mPermissionMonitor = new PermissionMonitor(mContext, mNMS);
- //set up the listener for user state for creating user VPNs
+ // Set up the listener for user state for creating user VPNs.
+ // Should run on mHandler to avoid any races.
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_USER_STARTED);
intentFilter.addAction(Intent.ACTION_USER_STOPPED);
@@ -924,7 +928,11 @@
intentFilter.addAction(Intent.ACTION_USER_REMOVED);
intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
mContext.registerReceiverAsUser(
- mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+ mIntentReceiver,
+ UserHandle.ALL,
+ intentFilter,
+ null /* broadcastPermission */,
+ mHandler);
mContext.registerReceiverAsUser(mUserPresentReceiver, UserHandle.SYSTEM,
new IntentFilter(Intent.ACTION_USER_PRESENT), null, null);
@@ -935,7 +943,11 @@
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
intentFilter.addDataScheme("package");
mContext.registerReceiverAsUser(
- mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+ mIntentReceiver,
+ UserHandle.ALL,
+ intentFilter,
+ null /* broadcastPermission */,
+ mHandler);
try {
mNMS.registerObserver(mTethering);
@@ -2689,11 +2701,6 @@
EVENT_PROVISIONING_NOTIFICATION, PROVISIONING_NOTIFICATION_HIDE,
mNai.network.netId));
}
-
- @Override
- public void logCaptivePortalLoginEvent(int eventId, String packageName) {
- new MetricsLogger().action(eventId, packageName);
- }
}
private boolean networkRequiresValidation(NetworkAgentInfo nai) {
@@ -3239,22 +3246,63 @@
/**
* NetworkStack endpoint to start the captive portal app. The NetworkStack needs to use this
* endpoint as it does not have INTERACT_ACROSS_USERS_FULL itself.
+ * @param network Network on which the captive portal was detected.
* @param appExtras Bundle to use as intent extras for the captive portal application.
* Must be treated as opaque to avoid preventing the captive portal app to
* update its arguments.
*/
@Override
- public void startCaptivePortalAppInternal(Bundle appExtras) {
+ public void startCaptivePortalAppInternal(Network network, Bundle appExtras) {
mContext.checkCallingOrSelfPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
final Intent appIntent = new Intent(ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN);
appIntent.putExtras(appExtras);
+ appIntent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL,
+ new CaptivePortal(new CaptivePortalImpl(network).asBinder()));
appIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
Binder.withCleanCallingIdentity(() ->
mContext.startActivityAsUser(appIntent, UserHandle.CURRENT));
}
+ private class CaptivePortalImpl extends ICaptivePortal.Stub {
+ private final Network mNetwork;
+
+ private CaptivePortalImpl(Network network) {
+ mNetwork = network;
+ }
+
+ @Override
+ public void appResponse(final int response) throws RemoteException {
+ if (response == CaptivePortal.APP_RETURN_WANTED_AS_IS) {
+ enforceSettingsPermission();
+ }
+
+ // getNetworkAgentInfoForNetwork is thread-safe
+ final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(mNetwork);
+ if (nai == null) return;
+
+ // nai.networkMonitor() is thread-safe
+ final INetworkMonitor nm = nai.networkMonitor();
+ if (nm == null) return;
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ nm.notifyCaptivePortalAppFinished(response);
+ } finally {
+ // Not using Binder.withCleanCallingIdentity() to keep the checked RemoteException
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void logEvent(int eventId, String packageName) {
+ enforceSettingsPermission();
+
+ new MetricsLogger().action(eventId, packageName);
+ }
+ }
+
public boolean avoidBadWifi() {
return mMultinetworkPolicyTracker.getAvoidBadWifi();
}
@@ -4089,17 +4137,27 @@
* handler thread through their agent, this is asynchronous. When the capabilities objects
* are computed they will be up-to-date as they are computed synchronously from here and
* this is running on the ConnectivityService thread.
- * TODO : Fix this and call updateCapabilities inline to remove out-of-order events.
*/
private void updateAllVpnsCapabilities() {
+ Network defaultNetwork = getNetwork(getDefaultNetwork());
synchronized (mVpns) {
for (int i = 0; i < mVpns.size(); i++) {
final Vpn vpn = mVpns.valueAt(i);
- vpn.updateCapabilities();
+ NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork);
+ updateVpnCapabilities(vpn, nc);
}
}
}
+ private void updateVpnCapabilities(Vpn vpn, @Nullable NetworkCapabilities nc) {
+ ensureRunningOnConnectivityServiceThread();
+ NetworkAgentInfo vpnNai = getNetworkAgentInfoForNetId(vpn.getNetId());
+ if (vpnNai == null || nc == null) {
+ return;
+ }
+ updateCapabilities(vpnNai.getCurrentScore(), vpnNai, nc);
+ }
+
@Override
public boolean updateLockdownVpn() {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
@@ -4440,22 +4498,28 @@
private void onUserAdded(int userId) {
mPermissionMonitor.onUserAdded(userId);
+ Network defaultNetwork = getNetwork(getDefaultNetwork());
synchronized (mVpns) {
final int vpnsSize = mVpns.size();
for (int i = 0; i < vpnsSize; i++) {
Vpn vpn = mVpns.valueAt(i);
vpn.onUserAdded(userId);
+ NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork);
+ updateVpnCapabilities(vpn, nc);
}
}
}
private void onUserRemoved(int userId) {
mPermissionMonitor.onUserRemoved(userId);
+ Network defaultNetwork = getNetwork(getDefaultNetwork());
synchronized (mVpns) {
final int vpnsSize = mVpns.size();
for (int i = 0; i < vpnsSize; i++) {
Vpn vpn = mVpns.valueAt(i);
vpn.onUserRemoved(userId);
+ NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork);
+ updateVpnCapabilities(vpn, nc);
}
}
}
@@ -4524,6 +4588,7 @@
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ ensureRunningOnConnectivityServiceThread();
final String action = intent.getAction();
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
@@ -5028,6 +5093,19 @@
return getNetworkForRequest(mDefaultRequest.requestId);
}
+ @Nullable
+ private Network getNetwork(@Nullable NetworkAgentInfo nai) {
+ return nai != null ? nai.network : null;
+ }
+
+ private void ensureRunningOnConnectivityServiceThread() {
+ if (mHandler.getLooper().getThread() != Thread.currentThread()) {
+ throw new IllegalStateException(
+ "Not running on ConnectivityService thread: "
+ + Thread.currentThread().getName());
+ }
+ }
+
private boolean isDefaultNetwork(NetworkAgentInfo nai) {
return nai == getDefaultNetwork();
}
@@ -5057,7 +5135,7 @@
if (DBG) log("registerNetworkAgent " + nai);
final long token = Binder.clearCallingIdentity();
try {
- mContext.getSystemService(NetworkStack.class).makeNetworkMonitor(
+ getNetworkStack().makeNetworkMonitor(
toStableParcelable(nai.network), name, new NetworkMonitorCallbacks(nai));
} finally {
Binder.restoreCallingIdentity(token);
@@ -5069,6 +5147,11 @@
return nai.network.netId;
}
+ @VisibleForTesting
+ protected NetworkStackClient getNetworkStack() {
+ return NetworkStackClient.getInstance();
+ }
+
private void handleRegisterNetworkAgent(NetworkAgentInfo nai, INetworkMonitor networkMonitor) {
nai.onNetworkMonitorCreated(networkMonitor);
if (VDBG) log("Got NetworkAgent Messenger");
@@ -5621,6 +5704,8 @@
updateTcpBufferSizes(newNetwork.linkProperties.getTcpBufferSizes());
mDnsManager.setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
notifyIfacesChangedForNetworkStats();
+ // Fix up the NetworkCapabilities of any VPNs that don't specify underlying networks.
+ updateAllVpnsCapabilities();
}
private void processListenRequests(NetworkAgentInfo nai, boolean capabilitiesChanged) {
@@ -6060,6 +6145,10 @@
// doing.
updateSignalStrengthThresholds(networkAgent, "CONNECT", null);
+ if (networkAgent.isVPN()) {
+ updateAllVpnsCapabilities();
+ }
+
// Consider network even though it is not yet validated.
final long now = SystemClock.elapsedRealtime();
rematchNetworkAndRequests(networkAgent, ReapUnvalidatedNetworks.REAP, now);
@@ -6321,7 +6410,11 @@
success = mVpns.get(user).setUnderlyingNetworks(networks);
}
if (success) {
- mHandler.post(() -> notifyIfacesChangedForNetworkStats());
+ mHandler.post(() -> {
+ // Update VPN's capabilities based on updated underlying network set.
+ updateAllVpnsCapabilities();
+ notifyIfacesChangedForNetworkStats();
+ });
}
return success;
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index c83ab84..d1a0692 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -20,6 +20,7 @@
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+import static android.net.ConnectivityManager.NETID_UNSET;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
@@ -123,7 +124,7 @@
import android.net.NetworkParcelable;
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
-import android.net.NetworkStack;
+import android.net.NetworkStackClient;
import android.net.NetworkUtils;
import android.net.ProxyInfo;
import android.net.RouteInfo;
@@ -245,7 +246,7 @@
@Mock INetworkStatsService mStatsService;
@Mock INetworkPolicyManager mNpm;
@Mock INetd mMockNetd;
- @Mock NetworkStack mNetworkStack;
+ @Mock NetworkStackClient mNetworkStack;
private ArgumentCaptor<String[]> mStringArrayCaptor = ArgumentCaptor.forClass(String[].class);
@@ -885,11 +886,14 @@
public void setUids(Set<UidRange> uids) {
mNetworkCapabilities.setUids(uids);
- updateCapabilities();
+ updateCapabilities(null /* defaultNetwork */);
}
@Override
public int getNetId() {
+ if (mMockNetworkAgent == null) {
+ return NETID_UNSET;
+ }
return mMockNetworkAgent.getNetwork().netId;
}
@@ -911,12 +915,13 @@
}
@Override
- public void updateCapabilities() {
- if (!mConnected) return;
- super.updateCapabilities();
- // Because super.updateCapabilities will update the capabilities of the agent but not
- // the mock agent, the mock agent needs to know about them.
+ public NetworkCapabilities updateCapabilities(Network defaultNetwork) {
+ if (!mConnected) return null;
+ super.updateCapabilities(defaultNetwork);
+ // Because super.updateCapabilities will update the capabilities of the agent but
+ // not the mock agent, the mock agent needs to know about them.
copyCapabilitiesToNetworkAgent();
+ return new NetworkCapabilities(mNetworkCapabilities);
}
private void copyCapabilitiesToNetworkAgent() {
@@ -1061,6 +1066,11 @@
}
@Override
+ protected NetworkStackClient getNetworkStack() {
+ return mNetworkStack;
+ }
+
+ @Override
public WakeupMessage makeWakeupMessage(
Context context, Handler handler, String cmdName, int cmd, Object obj) {
return new FakeWakeupMessage(context, handler, cmdName, cmd, 0, 0, obj);
@@ -4664,6 +4674,7 @@
vpnNetworkAgent.connect(false);
mMockVpn.connect();
+ mMockVpn.setUnderlyingNetworks(new Network[0]);
genericNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
genericNotVpnNetworkCallback.assertNoCallback();
@@ -4696,6 +4707,7 @@
ranges.add(new UidRange(uid, uid));
mMockVpn.setUids(ranges);
+ vpnNetworkAgent.setUids(ranges);
genericNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent);
genericNotVpnNetworkCallback.assertNoCallback();
@@ -4729,12 +4741,11 @@
}
@Test
- public void testVpnWithAndWithoutInternet() {
+ public void testVpnWithoutInternet() {
final int uid = Process.myUid();
final TestNetworkCallback defaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(defaultCallback);
- defaultCallback.assertNoCallback();
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
@@ -4756,11 +4767,30 @@
vpnNetworkAgent.disconnect();
defaultCallback.assertNoCallback();
- vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ mCm.unregisterNetworkCallback(defaultCallback);
+ }
+
+ @Test
+ public void testVpnWithInternet() {
+ final int uid = Process.myUid();
+
+ final TestNetworkCallback defaultCallback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(defaultCallback);
+
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true);
+
+ defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
+ assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
+
+ MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ final ArraySet<UidRange> ranges = new ArraySet<>();
+ ranges.add(new UidRange(uid, uid));
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.setUids(ranges);
vpnNetworkAgent.connect(true /* validated */, true /* hasInternet */);
mMockVpn.connect();
+
defaultCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
@@ -4768,14 +4798,6 @@
defaultCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
- vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
- ranges.clear();
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */);
- mMockVpn.connect();
- defaultCallback.assertNoCallback();
-
mCm.unregisterNetworkCallback(defaultCallback);
}
@@ -4878,6 +4900,70 @@
}
@Test
+ public void testNullUnderlyingNetworks() {
+ final int uid = Process.myUid();
+
+ final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback();
+ final NetworkRequest vpnNetworkRequest = new NetworkRequest.Builder()
+ .removeCapability(NET_CAPABILITY_NOT_VPN)
+ .addTransportType(TRANSPORT_VPN)
+ .build();
+ NetworkCapabilities nc;
+ mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
+ vpnNetworkCallback.assertNoCallback();
+
+ final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ final ArraySet<UidRange> ranges = new ArraySet<>();
+ ranges.add(new UidRange(uid, uid));
+ mMockVpn.setNetworkAgent(vpnNetworkAgent);
+ mMockVpn.connect();
+ mMockVpn.setUids(ranges);
+ vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */);
+
+ vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
+ nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
+ assertTrue(nc.hasTransport(TRANSPORT_VPN));
+ assertFalse(nc.hasTransport(TRANSPORT_CELLULAR));
+ assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+ // By default, VPN is set to track default network (i.e. its underlying networks is null).
+ // In case of no default network, VPN is considered metered.
+ assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED));
+
+ // Connect to Cell; Cell is the default network.
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+
+ vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+ && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
+ && !caps.hasCapability(NET_CAPABILITY_NOT_METERED),
+ vpnNetworkAgent);
+
+ // Connect to WiFi; WiFi is the new default.
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+ mWiFiNetworkAgent.connect(true);
+
+ vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+ && !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
+ && caps.hasCapability(NET_CAPABILITY_NOT_METERED),
+ vpnNetworkAgent);
+
+ // Disconnect Cell. The default network did not change, so there shouldn't be any changes in
+ // the capabilities.
+ mCellNetworkAgent.disconnect();
+
+ // Disconnect wifi too. Now we have no default network.
+ mWiFiNetworkAgent.disconnect();
+
+ vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+ && !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
+ && !caps.hasCapability(NET_CAPABILITY_NOT_METERED),
+ vpnNetworkAgent);
+
+ mMockVpn.disconnect();
+ }
+
+ @Test
public void testNetworkBlockedStatus() {
final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
final NetworkRequest cellRequest = new NetworkRequest.Builder()
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index 9578ded..aa6cbda 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -37,7 +37,6 @@
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkMisc;
-import android.net.NetworkStack;
import android.os.INetworkManagementService;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -74,16 +73,12 @@
@Mock NetworkMisc mMisc;
@Mock NetworkNotificationManager mNotifier;
@Mock Resources mResources;
- @Mock NetworkStack mNetworkStack;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mCtx.getResources()).thenReturn(mResources);
when(mCtx.getPackageName()).thenReturn("com.android.server.connectivity");
- when(mCtx.getSystemServiceName(NetworkStack.class))
- .thenReturn(Context.NETWORK_STACK_SERVICE);
- when(mCtx.getSystemService(Context.NETWORK_STACK_SERVICE)).thenReturn(mNetworkStack);
mMonitor = new TestableLingerMonitor(mCtx, mNotifier, HIGH_DAILY_LIMIT, HIGH_RATE_LIMIT);
}
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 46de3d0..b5d1ff9 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -28,6 +28,7 @@
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.RouteInfo.RTN_UNREACHABLE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -89,6 +90,7 @@
import org.mockito.MockitoAnnotations;
import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -566,7 +568,7 @@
final NetworkCapabilities caps = new NetworkCapabilities();
- Vpn.updateCapabilities(
+ Vpn.applyUnderlyingCapabilities(
mConnectivityManager, new Network[] {}, caps, false /* isAlwaysMetered */);
assertTrue(caps.hasTransport(TRANSPORT_VPN));
assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
@@ -577,7 +579,7 @@
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
- Vpn.updateCapabilities(
+ Vpn.applyUnderlyingCapabilities(
mConnectivityManager,
new Network[] {mobile},
caps,
@@ -591,7 +593,7 @@
assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
- Vpn.updateCapabilities(
+ Vpn.applyUnderlyingCapabilities(
mConnectivityManager, new Network[] {wifi}, caps, false /* isAlwaysMetered */);
assertTrue(caps.hasTransport(TRANSPORT_VPN));
assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
@@ -602,7 +604,7 @@
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
- Vpn.updateCapabilities(
+ Vpn.applyUnderlyingCapabilities(
mConnectivityManager, new Network[] {wifi}, caps, true /* isAlwaysMetered */);
assertTrue(caps.hasTransport(TRANSPORT_VPN));
assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
@@ -613,7 +615,7 @@
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
- Vpn.updateCapabilities(
+ Vpn.applyUnderlyingCapabilities(
mConnectivityManager,
new Network[] {mobile, wifi},
caps,
@@ -775,6 +777,16 @@
// V4 does not, but V6 has sufficient coverage again
lp.addRoute(new RouteInfo(new IpPrefix("::/1")));
assertTrue(Vpn.providesRoutesToMostDestinations(lp));
+
+ lp.clear();
+ // V4-unreachable route should not be treated as sufficient coverage
+ lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
+ assertFalse(Vpn.providesRoutesToMostDestinations(lp));
+
+ lp.clear();
+ // V6-unreachable route should not be treated as sufficient coverage
+ lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
+ assertFalse(Vpn.providesRoutesToMostDestinations(lp));
}
@Test