Merge changes Ic5750d4f,I9fa888c9,I4211475f
* changes: Deflake testNetworkBlockedStatusAlwaysOnVpn Simplify testVpnRestrictedUsers. Add test coverage for LockdownVpnTracker.
This commit is contained in:
@@ -887,6 +887,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
return NetworkStackClient.getInstance();
|
return NetworkStackClient.getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a reference to the system keystore.
|
||||||
|
*/
|
||||||
|
public KeyStore getKeyStore() {
|
||||||
|
return KeyStore.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see ProxyTracker
|
* @see ProxyTracker
|
||||||
*/
|
*/
|
||||||
@@ -981,7 +988,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
mProxyTracker = mDeps.makeProxyTracker(mContext, mHandler);
|
mProxyTracker = mDeps.makeProxyTracker(mContext, mHandler);
|
||||||
|
|
||||||
mNetd = netd;
|
mNetd = netd;
|
||||||
mKeyStore = KeyStore.getInstance();
|
mKeyStore = mDeps.getKeyStore();
|
||||||
mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
|
mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
|
||||||
mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
|
mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
|
||||||
mLocationPermissionChecker = new LocationPermissionChecker(mContext);
|
mLocationPermissionChecker = new LocationPermissionChecker(mContext);
|
||||||
@@ -4982,16 +4989,21 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
mVpnBlockedUidRanges = newVpnBlockedUidRanges;
|
mVpnBlockedUidRanges = newVpnBlockedUidRanges;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isLockdownVpnEnabled() {
|
||||||
|
return mKeyStore.contains(Credentials.LOCKDOWN_VPN);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean updateLockdownVpn() {
|
public boolean updateLockdownVpn() {
|
||||||
if (mDeps.getCallingUid() != Process.SYSTEM_UID) {
|
if (mDeps.getCallingUid() != Process.SYSTEM_UID
|
||||||
logw("Lockdown VPN only available to AID_SYSTEM");
|
&& Binder.getCallingPid() != Process.myPid()) {
|
||||||
|
logw("Lockdown VPN only available to system process or AID_SYSTEM");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized (mVpns) {
|
synchronized (mVpns) {
|
||||||
// Tear down existing lockdown if profile was removed
|
// Tear down existing lockdown if profile was removed
|
||||||
mLockdownEnabled = LockdownVpnTracker.isEnabled();
|
mLockdownEnabled = isLockdownVpnEnabled();
|
||||||
if (mLockdownEnabled) {
|
if (mLockdownEnabled) {
|
||||||
byte[] profileTag = mKeyStore.get(Credentials.LOCKDOWN_VPN);
|
byte[] profileTag = mKeyStore.get(Credentials.LOCKDOWN_VPN);
|
||||||
if (profileTag == null) {
|
if (profileTag == null) {
|
||||||
@@ -5012,7 +5024,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
logw("VPN for user " + user + " not ready yet. Skipping lockdown");
|
logw("VPN for user " + user + " not ready yet. Skipping lockdown");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
setLockdownTracker(new LockdownVpnTracker(mContext, this, mHandler, vpn, profile));
|
setLockdownTracker(
|
||||||
|
new LockdownVpnTracker(mContext, this, mHandler, mKeyStore, vpn, profile));
|
||||||
} else {
|
} else {
|
||||||
setLockdownTracker(null);
|
setLockdownTracker(null);
|
||||||
}
|
}
|
||||||
@@ -5100,7 +5113,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
|
|
||||||
synchronized (mVpns) {
|
synchronized (mVpns) {
|
||||||
// Can't set always-on VPN if legacy VPN is already in lockdown mode.
|
// Can't set always-on VPN if legacy VPN is already in lockdown mode.
|
||||||
if (LockdownVpnTracker.isEnabled()) {
|
if (isLockdownVpnEnabled()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5206,7 +5219,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
}
|
}
|
||||||
userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, mNetd, userId, mKeyStore);
|
userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, mNetd, userId, mKeyStore);
|
||||||
mVpns.put(userId, userVpn);
|
mVpns.put(userId, userVpn);
|
||||||
if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
|
if (mUserManager.getUserInfo(userId).isPrimary() && isLockdownVpnEnabled()) {
|
||||||
updateLockdownVpn();
|
updateLockdownVpn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5290,7 +5303,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
private void onUserUnlocked(int userId) {
|
private void onUserUnlocked(int userId) {
|
||||||
synchronized (mVpns) {
|
synchronized (mVpns) {
|
||||||
// User present may be sent because of an unlock, which might mean an unlocked keystore.
|
// User present may be sent because of an unlock, which might mean an unlocked keystore.
|
||||||
if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
|
if (mUserManager.getUserInfo(userId).isPrimary() && isLockdownVpnEnabled()) {
|
||||||
updateLockdownVpn();
|
updateLockdownVpn();
|
||||||
} else {
|
} else {
|
||||||
startAlwaysOnVpn(userId);
|
startAlwaysOnVpn(userId);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
|
|||||||
import static android.app.PendingIntent.FLAG_IMMUTABLE;
|
import static android.app.PendingIntent.FLAG_IMMUTABLE;
|
||||||
import static android.content.Intent.ACTION_USER_ADDED;
|
import static android.content.Intent.ACTION_USER_ADDED;
|
||||||
import static android.content.Intent.ACTION_USER_REMOVED;
|
import static android.content.Intent.ACTION_USER_REMOVED;
|
||||||
|
import static android.content.Intent.ACTION_USER_UNLOCKED;
|
||||||
import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
|
import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
|
||||||
import static android.content.pm.PackageManager.GET_PERMISSIONS;
|
import static android.content.pm.PackageManager.GET_PERMISSIONS;
|
||||||
import static android.content.pm.PackageManager.MATCH_ANY_USER;
|
import static android.content.pm.PackageManager.MATCH_ANY_USER;
|
||||||
@@ -221,6 +222,7 @@ import android.os.SystemClock;
|
|||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
|
import android.security.Credentials;
|
||||||
import android.security.KeyStore;
|
import android.security.KeyStore;
|
||||||
import android.system.Os;
|
import android.system.Os;
|
||||||
import android.telephony.TelephonyManager;
|
import android.telephony.TelephonyManager;
|
||||||
@@ -237,6 +239,7 @@ import androidx.test.runner.AndroidJUnit4;
|
|||||||
import com.android.internal.app.IBatteryStats;
|
import com.android.internal.app.IBatteryStats;
|
||||||
import com.android.internal.net.VpnConfig;
|
import com.android.internal.net.VpnConfig;
|
||||||
import com.android.internal.net.VpnInfo;
|
import com.android.internal.net.VpnInfo;
|
||||||
|
import com.android.internal.net.VpnProfile;
|
||||||
import com.android.internal.util.ArrayUtils;
|
import com.android.internal.util.ArrayUtils;
|
||||||
import com.android.internal.util.WakeupMessage;
|
import com.android.internal.util.WakeupMessage;
|
||||||
import com.android.internal.util.test.BroadcastInterceptingContext;
|
import com.android.internal.util.test.BroadcastInterceptingContext;
|
||||||
@@ -279,6 +282,7 @@ import java.net.Inet6Address;
|
|||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -384,6 +388,7 @@ public class ConnectivityServiceTest {
|
|||||||
@Mock MockableSystemProperties mSystemProperties;
|
@Mock MockableSystemProperties mSystemProperties;
|
||||||
@Mock EthernetManager mEthernetManager;
|
@Mock EthernetManager mEthernetManager;
|
||||||
@Mock NetworkPolicyManager mNetworkPolicyManager;
|
@Mock NetworkPolicyManager mNetworkPolicyManager;
|
||||||
|
@Mock KeyStore mKeyStore;
|
||||||
|
|
||||||
private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
|
private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
|
||||||
ArgumentCaptor.forClass(ResolverParamsParcel.class);
|
ArgumentCaptor.forClass(ResolverParamsParcel.class);
|
||||||
@@ -1062,6 +1067,15 @@ public class ConnectivityServiceTest {
|
|||||||
private int mVpnType = VpnManager.TYPE_VPN_SERVICE;
|
private int mVpnType = VpnManager.TYPE_VPN_SERVICE;
|
||||||
private VpnInfo mVpnInfo;
|
private VpnInfo mVpnInfo;
|
||||||
|
|
||||||
|
// These ConditionVariables allow tests to wait for LegacyVpnRunner to be stopped/started.
|
||||||
|
// TODO: this scheme is ad-hoc and error-prone because it does not fail if, for example, the
|
||||||
|
// test expects two starts in a row, or even if the production code calls start twice in a
|
||||||
|
// row. find a better solution. Simply putting a method to create a LegacyVpnRunner into
|
||||||
|
// Vpn.Dependencies doesn't work because LegacyVpnRunner is not a static class and has
|
||||||
|
// extensive access into the internals of Vpn.
|
||||||
|
private ConditionVariable mStartLegacyVpnCv = new ConditionVariable();
|
||||||
|
private ConditionVariable mStopVpnRunnerCv = new ConditionVariable();
|
||||||
|
|
||||||
public MockVpn(int userId) {
|
public MockVpn(int userId) {
|
||||||
super(startHandlerThreadAndReturnLooper(), mServiceContext,
|
super(startHandlerThreadAndReturnLooper(), mServiceContext,
|
||||||
new Dependencies() {
|
new Dependencies() {
|
||||||
@@ -1075,7 +1089,7 @@ public class ConnectivityServiceTest {
|
|||||||
return mDeviceIdleInternal;
|
return mDeviceIdleInternal;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mNetworkManagementService, mMockNetd, userId, mock(KeyStore.class));
|
mNetworkManagementService, mMockNetd, userId, mKeyStore);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUids(Set<UidRange> uids) {
|
public void setUids(Set<UidRange> uids) {
|
||||||
@@ -1187,9 +1201,43 @@ public class ConnectivityServiceTest {
|
|||||||
}
|
}
|
||||||
mAgentRegistered = false;
|
mAgentRegistered = false;
|
||||||
setUids(null);
|
setUids(null);
|
||||||
|
// Remove NET_CAPABILITY_INTERNET or MockNetworkAgent will refuse to connect later on.
|
||||||
|
mNetworkCapabilities.removeCapability(NET_CAPABILITY_INTERNET);
|
||||||
mInterface = null;
|
mInterface = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startLegacyVpnRunner() {
|
||||||
|
mStartLegacyVpnCv.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void expectStartLegacyVpnRunner() {
|
||||||
|
assertTrue("startLegacyVpnRunner not called after " + TIMEOUT_MS + " ms",
|
||||||
|
mStartLegacyVpnCv.block(TIMEOUT_MS));
|
||||||
|
|
||||||
|
// startLegacyVpn calls stopVpnRunnerPrivileged, which will open mStopVpnRunnerCv, just
|
||||||
|
// before calling startLegacyVpnRunner. Restore mStopVpnRunnerCv, so the test can expect
|
||||||
|
// that the VpnRunner is stopped and immediately restarted by calling
|
||||||
|
// expectStartLegacyVpnRunner() and expectStopVpnRunnerPrivileged() back-to-back.
|
||||||
|
mStopVpnRunnerCv = new ConditionVariable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stopVpnRunnerPrivileged() {
|
||||||
|
if (mVpnRunner != null) {
|
||||||
|
super.stopVpnRunnerPrivileged();
|
||||||
|
disconnect();
|
||||||
|
mStartLegacyVpnCv = new ConditionVariable();
|
||||||
|
}
|
||||||
|
mVpnRunner = null;
|
||||||
|
mStopVpnRunnerCv.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void expectStopVpnRunnerPrivileged() {
|
||||||
|
assertTrue("stopVpnRunnerPrivileged not called after " + TIMEOUT_MS + " ms",
|
||||||
|
mStopVpnRunnerCv.block(TIMEOUT_MS));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized VpnInfo getVpnInfo() {
|
public synchronized VpnInfo getVpnInfo() {
|
||||||
if (mVpnInfo != null) return mVpnInfo;
|
if (mVpnInfo != null) return mVpnInfo;
|
||||||
@@ -1271,10 +1319,19 @@ public class ConnectivityServiceTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final int VPN_USER = 0;
|
private static final int PRIMARY_USER = 0;
|
||||||
private static final int APP1_UID = UserHandle.getUid(VPN_USER, 10100);
|
private static final int APP1_UID = UserHandle.getUid(PRIMARY_USER, 10100);
|
||||||
private static final int APP2_UID = UserHandle.getUid(VPN_USER, 10101);
|
private static final int APP2_UID = UserHandle.getUid(PRIMARY_USER, 10101);
|
||||||
private static final int VPN_UID = UserHandle.getUid(VPN_USER, 10043);
|
private static final int VPN_UID = UserHandle.getUid(PRIMARY_USER, 10043);
|
||||||
|
private static final UserInfo PRIMARY_USER_INFO = new UserInfo(PRIMARY_USER, "",
|
||||||
|
UserInfo.FLAG_PRIMARY);
|
||||||
|
|
||||||
|
private static final int RESTRICTED_USER = 1;
|
||||||
|
private static final UserInfo RESTRICTED_USER_INFO = new UserInfo(RESTRICTED_USER, "",
|
||||||
|
UserInfo.FLAG_RESTRICTED);
|
||||||
|
static {
|
||||||
|
RESTRICTED_USER_INFO.restrictedProfileParentId = PRIMARY_USER;
|
||||||
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
@@ -1284,10 +1341,13 @@ public class ConnectivityServiceTest {
|
|||||||
|
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
|
|
||||||
when(mUserManager.getAliveUsers()).thenReturn(
|
when(mUserManager.getAliveUsers()).thenReturn(Arrays.asList(PRIMARY_USER_INFO));
|
||||||
Arrays.asList(new UserInfo[] {
|
when(mUserManager.getUserInfo(PRIMARY_USER)).thenReturn(PRIMARY_USER_INFO);
|
||||||
new UserInfo(VPN_USER, "", 0),
|
// canHaveRestrictedProfile does not take a userId. It applies to the userId of the context
|
||||||
}));
|
// it was started from, i.e., PRIMARY_USER.
|
||||||
|
when(mUserManager.canHaveRestrictedProfile()).thenReturn(true);
|
||||||
|
when(mUserManager.getUserInfo(RESTRICTED_USER)).thenReturn(RESTRICTED_USER_INFO);
|
||||||
|
|
||||||
final ApplicationInfo applicationInfo = new ApplicationInfo();
|
final ApplicationInfo applicationInfo = new ApplicationInfo();
|
||||||
applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
|
applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
|
||||||
when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
|
when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
|
||||||
@@ -1329,6 +1389,9 @@ public class ConnectivityServiceTest {
|
|||||||
verify(mNetworkPolicyManager).registerListener(policyListenerCaptor.capture());
|
verify(mNetworkPolicyManager).registerListener(policyListenerCaptor.capture());
|
||||||
mPolicyListener = policyListenerCaptor.getValue();
|
mPolicyListener = policyListenerCaptor.getValue();
|
||||||
|
|
||||||
|
mServiceContext.setPermission(
|
||||||
|
Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED);
|
||||||
|
|
||||||
// Create local CM before sending system ready so that we can answer
|
// Create local CM before sending system ready so that we can answer
|
||||||
// getSystemService() correctly.
|
// getSystemService() correctly.
|
||||||
mCm = new WrappedConnectivityManager(InstrumentationRegistry.getContext(), mService);
|
mCm = new WrappedConnectivityManager(InstrumentationRegistry.getContext(), mService);
|
||||||
@@ -1359,6 +1422,7 @@ public class ConnectivityServiceTest {
|
|||||||
doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any());
|
doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any());
|
||||||
doReturn(true).when(deps).queryUserAccess(anyInt(), anyInt());
|
doReturn(true).when(deps).queryUserAccess(anyInt(), anyInt());
|
||||||
doReturn(mBatteryStatsService).when(deps).getBatteryStatsService();
|
doReturn(mBatteryStatsService).when(deps).getBatteryStatsService();
|
||||||
|
doReturn(mKeyStore).when(deps).getKeyStore();
|
||||||
doAnswer(inv -> {
|
doAnswer(inv -> {
|
||||||
mPolicyTracker = new WrappedMultinetworkPolicyTracker(
|
mPolicyTracker = new WrappedMultinetworkPolicyTracker(
|
||||||
inv.getArgument(0), inv.getArgument(1), inv.getArgument(2));
|
inv.getArgument(0), inv.getArgument(1), inv.getArgument(2));
|
||||||
@@ -1524,10 +1588,9 @@ public class ConnectivityServiceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void expectNoBroadcast(int timeoutMs) throws Exception {
|
public void expectNoBroadcast(int timeoutMs) throws Exception {
|
||||||
waitForIdle();
|
|
||||||
try {
|
try {
|
||||||
final Intent intent = get(timeoutMs, TimeUnit.MILLISECONDS);
|
final Intent intent = get(timeoutMs, TimeUnit.MILLISECONDS);
|
||||||
fail("Unexpected broadcast: " + intent.getAction());
|
fail("Unexpected broadcast: " + intent.getAction() + " " + intent.getExtras());
|
||||||
} catch (TimeoutException expected) {
|
} catch (TimeoutException expected) {
|
||||||
} finally {
|
} finally {
|
||||||
mServiceContext.unregisterReceiver(mReceiver);
|
mServiceContext.unregisterReceiver(mReceiver);
|
||||||
@@ -6309,7 +6372,7 @@ public class ConnectivityServiceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testVpnRestrictedUsers() throws Exception {
|
public void testRestrictedProfileAffectsVpnUidRanges() throws Exception {
|
||||||
// NETWORK_SETTINGS is necessary to see the UID ranges in NetworkCapabilities.
|
// NETWORK_SETTINGS is necessary to see the UID ranges in NetworkCapabilities.
|
||||||
mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS,
|
mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS,
|
||||||
PERMISSION_GRANTED);
|
PERMISSION_GRANTED);
|
||||||
@@ -6341,19 +6404,11 @@ public class ConnectivityServiceTest {
|
|||||||
callback.expectCapabilitiesThat(mWiFiNetworkAgent, (caps)
|
callback.expectCapabilitiesThat(mWiFiNetworkAgent, (caps)
|
||||||
-> caps.hasCapability(NET_CAPABILITY_VALIDATED));
|
-> caps.hasCapability(NET_CAPABILITY_VALIDATED));
|
||||||
|
|
||||||
// Create a fake restricted profile whose parent is our user ID.
|
when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, RESTRICTED_USER))
|
||||||
final int userId = UserHandle.getUserId(uid);
|
.thenReturn(UserHandle.getUid(RESTRICTED_USER, VPN_UID));
|
||||||
when(mUserManager.canHaveRestrictedProfile()).thenReturn(true);
|
|
||||||
final int restrictedUserId = userId + 1;
|
|
||||||
final UserInfo info = new UserInfo(restrictedUserId, "user", UserInfo.FLAG_RESTRICTED);
|
|
||||||
info.restrictedProfileParentId = userId;
|
|
||||||
assertTrue(info.isRestricted());
|
|
||||||
when(mUserManager.getUserInfo(restrictedUserId)).thenReturn(info);
|
|
||||||
when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, restrictedUserId))
|
|
||||||
.thenReturn(UserHandle.getUid(restrictedUserId, VPN_UID));
|
|
||||||
|
|
||||||
final Intent addedIntent = new Intent(ACTION_USER_ADDED);
|
final Intent addedIntent = new Intent(ACTION_USER_ADDED);
|
||||||
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, restrictedUserId);
|
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
|
||||||
|
|
||||||
// Send a USER_ADDED broadcast for it.
|
// Send a USER_ADDED broadcast for it.
|
||||||
// The BroadcastReceiver for this broadcast checks that is being run on the handler thread.
|
// The BroadcastReceiver for this broadcast checks that is being run on the handler thread.
|
||||||
@@ -6365,7 +6420,7 @@ public class ConnectivityServiceTest {
|
|||||||
callback.expectCapabilitiesThat(mMockVpn, (caps)
|
callback.expectCapabilitiesThat(mMockVpn, (caps)
|
||||||
-> caps.getUids().size() == 2
|
-> caps.getUids().size() == 2
|
||||||
&& caps.getUids().contains(new UidRange(uid, uid))
|
&& caps.getUids().contains(new UidRange(uid, uid))
|
||||||
&& caps.getUids().contains(UidRange.createForUser(restrictedUserId))
|
&& caps.getUids().contains(UidRange.createForUser(RESTRICTED_USER))
|
||||||
&& caps.hasTransport(TRANSPORT_VPN)
|
&& caps.hasTransport(TRANSPORT_VPN)
|
||||||
&& caps.hasTransport(TRANSPORT_WIFI));
|
&& caps.hasTransport(TRANSPORT_WIFI));
|
||||||
|
|
||||||
@@ -6375,13 +6430,13 @@ public class ConnectivityServiceTest {
|
|||||||
callback.expectCapabilitiesThat(mMockVpn, (caps)
|
callback.expectCapabilitiesThat(mMockVpn, (caps)
|
||||||
-> caps.getUids().size() == 2
|
-> caps.getUids().size() == 2
|
||||||
&& caps.getUids().contains(new UidRange(uid, uid))
|
&& caps.getUids().contains(new UidRange(uid, uid))
|
||||||
&& caps.getUids().contains(UidRange.createForUser(restrictedUserId))
|
&& caps.getUids().contains(UidRange.createForUser(RESTRICTED_USER))
|
||||||
&& caps.hasTransport(TRANSPORT_VPN)
|
&& caps.hasTransport(TRANSPORT_VPN)
|
||||||
&& !caps.hasTransport(TRANSPORT_WIFI));
|
&& !caps.hasTransport(TRANSPORT_WIFI));
|
||||||
|
|
||||||
// Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user.
|
// Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user.
|
||||||
final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
|
final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
|
||||||
removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, restrictedUserId);
|
removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
|
||||||
handler.post(() -> mServiceContext.sendBroadcast(removedIntent));
|
handler.post(() -> mServiceContext.sendBroadcast(removedIntent));
|
||||||
|
|
||||||
// Expect that the VPN gains the UID range for the restricted user, and that the capability
|
// Expect that the VPN gains the UID range for the restricted user, and that the capability
|
||||||
@@ -6391,53 +6446,68 @@ public class ConnectivityServiceTest {
|
|||||||
&& caps.getUids().contains(new UidRange(uid, uid))
|
&& caps.getUids().contains(new UidRange(uid, uid))
|
||||||
&& caps.hasTransport(TRANSPORT_VPN)
|
&& caps.hasTransport(TRANSPORT_VPN)
|
||||||
&& !caps.hasTransport(TRANSPORT_WIFI));
|
&& !caps.hasTransport(TRANSPORT_WIFI));
|
||||||
|
}
|
||||||
|
|
||||||
// Test lockdown with restricted profiles.
|
@Test
|
||||||
|
public void testLockdownVpnWithRestrictedProfiles() throws Exception {
|
||||||
|
// NETWORK_SETTINGS is necessary to see the UID ranges in NetworkCapabilities.
|
||||||
mServiceContext.setPermission(
|
mServiceContext.setPermission(
|
||||||
Manifest.permission.CONTROL_ALWAYS_ON_VPN, PERMISSION_GRANTED);
|
Manifest.permission.CONTROL_ALWAYS_ON_VPN, PERMISSION_GRANTED);
|
||||||
mServiceContext.setPermission(
|
|
||||||
Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED);
|
|
||||||
mServiceContext.setPermission(
|
mServiceContext.setPermission(
|
||||||
Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED);
|
Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED);
|
||||||
|
|
||||||
|
final NetworkRequest request = new NetworkRequest.Builder()
|
||||||
|
.removeCapability(NET_CAPABILITY_NOT_VPN)
|
||||||
|
.build();
|
||||||
|
final TestNetworkCallback callback = new TestNetworkCallback();
|
||||||
|
mCm.registerNetworkCallback(request, callback);
|
||||||
|
|
||||||
|
final int uid = Process.myUid();
|
||||||
|
|
||||||
// Connect wifi and check that UIDs in the main and restricted profiles have network access.
|
// Connect wifi and check that UIDs in the main and restricted profiles have network access.
|
||||||
mMockVpn.disconnect();
|
|
||||||
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
|
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
|
||||||
mWiFiNetworkAgent.connect(true /* validated */);
|
mWiFiNetworkAgent.connect(true /* validated */);
|
||||||
final int restrictedUid = UserHandle.getUid(restrictedUserId, 42 /* appId */);
|
final int restrictedUid = UserHandle.getUid(RESTRICTED_USER, 42 /* appId */);
|
||||||
assertNotNull(mCm.getActiveNetworkForUid(uid));
|
assertNotNull(mCm.getActiveNetworkForUid(uid));
|
||||||
assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
|
assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
|
||||||
|
|
||||||
// Enable always-on VPN lockdown. The main user loses network access because no VPN is up.
|
// Enable always-on VPN lockdown. The main user loses network access because no VPN is up.
|
||||||
final ArrayList<String> allowList = new ArrayList<>();
|
final ArrayList<String> allowList = new ArrayList<>();
|
||||||
mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
|
mService.setAlwaysOnVpnPackage(PRIMARY_USER, ALWAYS_ON_PACKAGE, true /* lockdown */,
|
||||||
|
allowList);
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
assertNull(mCm.getActiveNetworkForUid(uid));
|
assertNull(mCm.getActiveNetworkForUid(uid));
|
||||||
|
// This is arguably overspecified: a UID that is not running doesn't have an active network.
|
||||||
|
// But it's useful to check that non-default users do not lose network access, and to prove
|
||||||
|
// that the loss of connectivity below is indeed due to the restricted profile coming up.
|
||||||
assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
|
assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
|
||||||
|
|
||||||
// Start the restricted profile, and check that the UID within it loses network access.
|
// Start the restricted profile, and check that the UID within it loses network access.
|
||||||
when(mUserManager.getAliveUsers()).thenReturn(
|
when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, RESTRICTED_USER))
|
||||||
Arrays.asList(new UserInfo[] {
|
.thenReturn(UserHandle.getUid(RESTRICTED_USER, VPN_UID));
|
||||||
new UserInfo(userId, "", 0),
|
when(mUserManager.getAliveUsers()).thenReturn(Arrays.asList(PRIMARY_USER_INFO,
|
||||||
info
|
RESTRICTED_USER_INFO));
|
||||||
}));
|
|
||||||
// TODO: check that VPN app within restricted profile still has access, etc.
|
// TODO: check that VPN app within restricted profile still has access, etc.
|
||||||
|
final Intent addedIntent = new Intent(ACTION_USER_ADDED);
|
||||||
|
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
|
||||||
|
final Handler handler = new Handler(mCsHandlerThread.getLooper());
|
||||||
handler.post(() -> mServiceContext.sendBroadcast(addedIntent));
|
handler.post(() -> mServiceContext.sendBroadcast(addedIntent));
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
assertNull(mCm.getActiveNetworkForUid(uid));
|
assertNull(mCm.getActiveNetworkForUid(uid));
|
||||||
assertNull(mCm.getActiveNetworkForUid(restrictedUid));
|
assertNull(mCm.getActiveNetworkForUid(restrictedUid));
|
||||||
|
|
||||||
// Stop the restricted profile, and check that the UID within it has network access again.
|
// Stop the restricted profile, and check that the UID within it has network access again.
|
||||||
when(mUserManager.getAliveUsers()).thenReturn(
|
when(mUserManager.getAliveUsers()).thenReturn(Arrays.asList(PRIMARY_USER_INFO));
|
||||||
Arrays.asList(new UserInfo[] {
|
|
||||||
new UserInfo(userId, "", 0),
|
// Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user.
|
||||||
}));
|
final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
|
||||||
|
removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
|
||||||
handler.post(() -> mServiceContext.sendBroadcast(removedIntent));
|
handler.post(() -> mServiceContext.sendBroadcast(removedIntent));
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
assertNull(mCm.getActiveNetworkForUid(uid));
|
assertNull(mCm.getActiveNetworkForUid(uid));
|
||||||
assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
|
assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
|
||||||
|
|
||||||
mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
|
mService.setAlwaysOnVpnPackage(PRIMARY_USER, null, false /* lockdown */, allowList);
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6778,6 +6848,7 @@ public class ConnectivityServiceTest {
|
|||||||
final int userId = UserHandle.getUserId(uid);
|
final int userId = UserHandle.getUserId(uid);
|
||||||
final ArrayList<String> allowList = new ArrayList<>();
|
final ArrayList<String> allowList = new ArrayList<>();
|
||||||
mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
|
mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
|
||||||
|
waitForIdle();
|
||||||
|
|
||||||
UidRangeParcel firstHalf = new UidRangeParcel(1, VPN_UID - 1);
|
UidRangeParcel firstHalf = new UidRangeParcel(1, VPN_UID - 1);
|
||||||
UidRangeParcel secondHalf = new UidRangeParcel(VPN_UID + 1, 99999);
|
UidRangeParcel secondHalf = new UidRangeParcel(VPN_UID + 1, 99999);
|
||||||
@@ -6799,10 +6870,10 @@ public class ConnectivityServiceTest {
|
|||||||
|
|
||||||
// Disable lockdown, expect to see the network unblocked.
|
// Disable lockdown, expect to see the network unblocked.
|
||||||
mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
|
mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
|
||||||
expectNetworkRejectNonSecureVpn(inOrder, false, firstHalf, secondHalf);
|
|
||||||
callback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
|
callback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
|
||||||
defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
|
defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
|
||||||
vpnUidCallback.assertNoCallback();
|
vpnUidCallback.assertNoCallback();
|
||||||
|
expectNetworkRejectNonSecureVpn(inOrder, false, firstHalf, secondHalf);
|
||||||
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
|
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
|
||||||
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
|
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
|
||||||
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
|
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
|
||||||
@@ -6845,9 +6916,11 @@ public class ConnectivityServiceTest {
|
|||||||
// Disable lockdown, remove our UID from the allowlist, and re-enable lockdown.
|
// Disable lockdown, remove our UID from the allowlist, and re-enable lockdown.
|
||||||
// Everything should now be blocked.
|
// Everything should now be blocked.
|
||||||
mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
|
mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
|
||||||
|
waitForIdle();
|
||||||
expectNetworkRejectNonSecureVpn(inOrder, false, piece1, piece2, piece3);
|
expectNetworkRejectNonSecureVpn(inOrder, false, piece1, piece2, piece3);
|
||||||
allowList.clear();
|
allowList.clear();
|
||||||
mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
|
mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
|
||||||
|
waitForIdle();
|
||||||
expectNetworkRejectNonSecureVpn(inOrder, true, firstHalf, secondHalf);
|
expectNetworkRejectNonSecureVpn(inOrder, true, firstHalf, secondHalf);
|
||||||
defaultCallback.expectBlockedStatusCallback(true, mWiFiNetworkAgent);
|
defaultCallback.expectBlockedStatusCallback(true, mWiFiNetworkAgent);
|
||||||
assertBlockedCallbackInAnyOrder(callback, true, mWiFiNetworkAgent, mCellNetworkAgent);
|
assertBlockedCallbackInAnyOrder(callback, true, mWiFiNetworkAgent, mCellNetworkAgent);
|
||||||
@@ -6925,6 +6998,194 @@ public class ConnectivityServiceTest {
|
|||||||
mCm.unregisterNetworkCallback(vpnUidCallback);
|
mCm.unregisterNetworkCallback(vpnUidCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setupLegacyLockdownVpn() {
|
||||||
|
final String profileName = "testVpnProfile";
|
||||||
|
final byte[] profileTag = profileName.getBytes(StandardCharsets.UTF_8);
|
||||||
|
when(mKeyStore.contains(Credentials.LOCKDOWN_VPN)).thenReturn(true);
|
||||||
|
when(mKeyStore.get(Credentials.LOCKDOWN_VPN)).thenReturn(profileTag);
|
||||||
|
|
||||||
|
final VpnProfile profile = new VpnProfile(profileName);
|
||||||
|
profile.name = "My VPN";
|
||||||
|
profile.server = "192.0.2.1";
|
||||||
|
profile.dnsServers = "8.8.8.8";
|
||||||
|
profile.type = VpnProfile.TYPE_IPSEC_XAUTH_PSK;
|
||||||
|
final byte[] encodedProfile = profile.encode();
|
||||||
|
when(mKeyStore.get(Credentials.VPN + profileName)).thenReturn(encodedProfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLegacyLockdownVpn() throws Exception {
|
||||||
|
final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
|
||||||
|
final TestNetworkCallback callback = new TestNetworkCallback();
|
||||||
|
mCm.registerNetworkCallback(request, callback);
|
||||||
|
|
||||||
|
final TestNetworkCallback defaultCallback = new TestNetworkCallback();
|
||||||
|
mCm.registerDefaultNetworkCallback(defaultCallback);
|
||||||
|
|
||||||
|
// Pretend lockdown VPN was configured.
|
||||||
|
setupLegacyLockdownVpn();
|
||||||
|
|
||||||
|
// LockdownVpnTracker disables the Vpn teardown code and enables lockdown.
|
||||||
|
// Check the VPN's state before it does so.
|
||||||
|
assertTrue(mMockVpn.getEnableTeardown());
|
||||||
|
assertFalse(mMockVpn.getLockdown());
|
||||||
|
|
||||||
|
// Send a USER_UNLOCKED broadcast so CS starts LockdownVpnTracker.
|
||||||
|
final int userId = UserHandle.getUserId(Process.myUid());
|
||||||
|
final Intent addedIntent = new Intent(ACTION_USER_UNLOCKED);
|
||||||
|
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
|
||||||
|
final Handler handler = new Handler(mCsHandlerThread.getLooper());
|
||||||
|
handler.post(() -> mServiceContext.sendBroadcast(addedIntent));
|
||||||
|
waitForIdle();
|
||||||
|
|
||||||
|
// Lockdown VPN disables teardown and enables lockdown.
|
||||||
|
assertFalse(mMockVpn.getEnableTeardown());
|
||||||
|
assertTrue(mMockVpn.getLockdown());
|
||||||
|
|
||||||
|
// Bring up a network.
|
||||||
|
// Expect nothing to happen because the network does not have an IPv4 default route: legacy
|
||||||
|
// VPN only supports IPv4.
|
||||||
|
final LinkProperties cellLp = new LinkProperties();
|
||||||
|
cellLp.setInterfaceName("rmnet0");
|
||||||
|
cellLp.addLinkAddress(new LinkAddress("2001:db8::1/64"));
|
||||||
|
cellLp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, "rmnet0"));
|
||||||
|
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
|
||||||
|
mCellNetworkAgent.connect(false /* validated */);
|
||||||
|
callback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
|
||||||
|
defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
|
||||||
|
waitForIdle();
|
||||||
|
assertNull(mMockVpn.getAgent());
|
||||||
|
|
||||||
|
// Add an IPv4 address. Ideally the VPN should start, but it doesn't because nothing calls
|
||||||
|
// LockdownVpnTracker#handleStateChangedLocked. This is a bug.
|
||||||
|
// TODO: consider fixing this.
|
||||||
|
cellLp.addLinkAddress(new LinkAddress("192.0.2.2/25"));
|
||||||
|
cellLp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, "rmnet0"));
|
||||||
|
mCellNetworkAgent.sendLinkProperties(cellLp);
|
||||||
|
callback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
|
||||||
|
defaultCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
|
||||||
|
waitForIdle();
|
||||||
|
assertNull(mMockVpn.getAgent());
|
||||||
|
|
||||||
|
// Disconnect, then try again with a network that supports IPv4 at connection time.
|
||||||
|
// Expect lockdown VPN to come up.
|
||||||
|
ExpectedBroadcast b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED);
|
||||||
|
mCellNetworkAgent.disconnect();
|
||||||
|
callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
|
||||||
|
defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
|
||||||
|
b1.expectBroadcast();
|
||||||
|
|
||||||
|
// When lockdown VPN is active, the NetworkInfo state in CONNECTIVITY_ACTION is overwritten
|
||||||
|
// with the state of the VPN network. So expect a CONNECTING broadcast.
|
||||||
|
b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTING);
|
||||||
|
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
|
||||||
|
mCellNetworkAgent.connect(false /* validated */);
|
||||||
|
callback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
|
||||||
|
defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
|
||||||
|
b1.expectBroadcast();
|
||||||
|
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
|
||||||
|
assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
|
||||||
|
assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
|
||||||
|
assertNetworkInfo(TYPE_VPN, DetailedState.BLOCKED);
|
||||||
|
|
||||||
|
// TODO: it would be nice if we could simply rely on the production code here, and have
|
||||||
|
// LockdownVpnTracker start the VPN, have the VPN code register its NetworkAgent with
|
||||||
|
// ConnectivityService, etc. That would require duplicating a fair bit of code from the
|
||||||
|
// Vpn tests around how to mock out LegacyVpnRunner. But even if we did that, this does not
|
||||||
|
// work for at least two reasons:
|
||||||
|
// 1. In this test, calling registerNetworkAgent does not actually result in an agent being
|
||||||
|
// registered. This is because nothing calls onNetworkMonitorCreated, which is what
|
||||||
|
// actually ends up causing handleRegisterNetworkAgent to be called. Code in this test
|
||||||
|
// that wants to register an agent must use TestNetworkAgentWrapper.
|
||||||
|
// 2. Even if we exposed Vpn#agentConnect to the test, and made MockVpn#agentConnect call
|
||||||
|
// the TestNetworkAgentWrapper code, this would deadlock because the
|
||||||
|
// TestNetworkAgentWrapper code cannot be called on the handler thread since it calls
|
||||||
|
// waitForIdle().
|
||||||
|
mMockVpn.expectStartLegacyVpnRunner();
|
||||||
|
b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
|
||||||
|
ExpectedBroadcast b2 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED);
|
||||||
|
mMockVpn.establishForMyUid();
|
||||||
|
callback.expectAvailableThenValidatedCallbacks(mMockVpn);
|
||||||
|
defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
|
||||||
|
b1.expectBroadcast();
|
||||||
|
b2.expectBroadcast();
|
||||||
|
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
|
||||||
|
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
|
||||||
|
assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
|
||||||
|
assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
|
||||||
|
|
||||||
|
// Switch default network from cell to wifi. Expect VPN to disconnect and reconnect.
|
||||||
|
final LinkProperties wifiLp = new LinkProperties();
|
||||||
|
wifiLp.setInterfaceName("wlan0");
|
||||||
|
wifiLp.addLinkAddress(new LinkAddress("192.0.2.163/25"));
|
||||||
|
wifiLp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, "wlan0"));
|
||||||
|
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp);
|
||||||
|
|
||||||
|
b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED);
|
||||||
|
// Wifi is CONNECTING because the VPN isn't up yet.
|
||||||
|
b2 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTING);
|
||||||
|
ExpectedBroadcast b3 = expectConnectivityAction(TYPE_VPN, DetailedState.DISCONNECTED);
|
||||||
|
mWiFiNetworkAgent.connect(false /* validated */);
|
||||||
|
b1.expectBroadcast();
|
||||||
|
b2.expectBroadcast();
|
||||||
|
b3.expectBroadcast();
|
||||||
|
mMockVpn.expectStopVpnRunnerPrivileged();
|
||||||
|
mMockVpn.expectStartLegacyVpnRunner();
|
||||||
|
|
||||||
|
// TODO: why is wifi not blocked? Is this because something calls prepare()?
|
||||||
|
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
|
||||||
|
callback.expectCapabilitiesThat(mMockVpn, (nc) -> nc.hasTransport(TRANSPORT_WIFI));
|
||||||
|
defaultCallback.expectCapabilitiesThat(mMockVpn, (nc) -> nc.hasTransport(TRANSPORT_WIFI));
|
||||||
|
callback.expectCallback(CallbackEntry.LOST, mMockVpn);
|
||||||
|
defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
|
||||||
|
defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent);
|
||||||
|
|
||||||
|
// While the VPN is reconnecting on the new network, everything is blocked.
|
||||||
|
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
|
||||||
|
assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
|
||||||
|
assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
|
||||||
|
assertNetworkInfo(TYPE_VPN, DetailedState.BLOCKED);
|
||||||
|
|
||||||
|
// The VPN comes up again on wifi.
|
||||||
|
b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
|
||||||
|
b2 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
|
||||||
|
mMockVpn.establishForMyUid();
|
||||||
|
callback.expectAvailableThenValidatedCallbacks(mMockVpn);
|
||||||
|
defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
|
||||||
|
b1.expectBroadcast();
|
||||||
|
b2.expectBroadcast();
|
||||||
|
|
||||||
|
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
|
||||||
|
assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
|
||||||
|
assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
|
||||||
|
assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
|
||||||
|
|
||||||
|
// Disconnect cell. Nothing much happens since it's not the default network.
|
||||||
|
// Whenever LockdownVpnTracker is connected, it will send a connected broadcast any time any
|
||||||
|
// NetworkInfo is updated. This is probably a bug.
|
||||||
|
// TODO: consider fixing this.
|
||||||
|
b1 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
|
||||||
|
mCellNetworkAgent.disconnect();
|
||||||
|
b1.expectBroadcast();
|
||||||
|
callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
|
||||||
|
defaultCallback.assertNoCallback();
|
||||||
|
|
||||||
|
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
|
||||||
|
assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
|
||||||
|
assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
|
||||||
|
assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
|
||||||
|
|
||||||
|
b1 = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED);
|
||||||
|
mWiFiNetworkAgent.disconnect();
|
||||||
|
callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
|
||||||
|
b1.expectBroadcast();
|
||||||
|
callback.expectCapabilitiesThat(mMockVpn, (nc) -> !nc.hasTransport(TRANSPORT_WIFI));
|
||||||
|
b2 = expectConnectivityAction(TYPE_VPN, DetailedState.DISCONNECTED);
|
||||||
|
mMockVpn.expectStopVpnRunnerPrivileged();
|
||||||
|
callback.expectCallback(CallbackEntry.LOST, mMockVpn);
|
||||||
|
b2.expectBroadcast();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public final void testLoseTrusted() throws Exception {
|
public final void testLoseTrusted() throws Exception {
|
||||||
final NetworkRequest trustedRequest = new NetworkRequest.Builder()
|
final NetworkRequest trustedRequest = new NetworkRequest.Builder()
|
||||||
@@ -7599,7 +7860,7 @@ public class ConnectivityServiceTest {
|
|||||||
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
|
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
|
||||||
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
|
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
|
||||||
// The uid range needs to cover the test app so the network is visible to it.
|
// The uid range needs to cover the test app so the network is visible to it.
|
||||||
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
|
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
|
||||||
mMockVpn.establish(lp, VPN_UID, vpnRange);
|
mMockVpn.establish(lp, VPN_UID, vpnRange);
|
||||||
assertVpnUidRangesUpdated(true, vpnRange, VPN_UID);
|
assertVpnUidRangesUpdated(true, vpnRange, VPN_UID);
|
||||||
|
|
||||||
@@ -7627,7 +7888,7 @@ public class ConnectivityServiceTest {
|
|||||||
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
|
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
|
||||||
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
|
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
|
||||||
// The uid range needs to cover the test app so the network is visible to it.
|
// The uid range needs to cover the test app so the network is visible to it.
|
||||||
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
|
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
|
||||||
mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
|
mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
|
||||||
assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID);
|
assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID);
|
||||||
|
|
||||||
@@ -7643,7 +7904,7 @@ public class ConnectivityServiceTest {
|
|||||||
lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, "tun0"));
|
lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, "tun0"));
|
||||||
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
|
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
|
||||||
// The uid range needs to cover the test app so the network is visible to it.
|
// The uid range needs to cover the test app so the network is visible to it.
|
||||||
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
|
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
|
||||||
mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
|
mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
|
||||||
assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID);
|
assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID);
|
||||||
|
|
||||||
@@ -7658,7 +7919,7 @@ public class ConnectivityServiceTest {
|
|||||||
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
|
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
|
||||||
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
|
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
|
||||||
// The uid range needs to cover the test app so the network is visible to it.
|
// The uid range needs to cover the test app so the network is visible to it.
|
||||||
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
|
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
|
||||||
mMockVpn.establish(lp, VPN_UID, vpnRange);
|
mMockVpn.establish(lp, VPN_UID, vpnRange);
|
||||||
assertVpnUidRangesUpdated(true, vpnRange, VPN_UID);
|
assertVpnUidRangesUpdated(true, vpnRange, VPN_UID);
|
||||||
|
|
||||||
@@ -7710,7 +7971,7 @@ public class ConnectivityServiceTest {
|
|||||||
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
|
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
|
||||||
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
|
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
|
||||||
// The uid range needs to cover the test app so the network is visible to it.
|
// The uid range needs to cover the test app so the network is visible to it.
|
||||||
final UidRange vpnRange = UidRange.createForUser(VPN_USER);
|
final UidRange vpnRange = UidRange.createForUser(PRIMARY_USER);
|
||||||
final Set<UidRange> vpnRanges = Collections.singleton(vpnRange);
|
final Set<UidRange> vpnRanges = Collections.singleton(vpnRange);
|
||||||
mMockVpn.establish(lp, VPN_UID, vpnRanges);
|
mMockVpn.establish(lp, VPN_UID, vpnRanges);
|
||||||
assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID);
|
assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID);
|
||||||
@@ -7908,7 +8169,7 @@ public class ConnectivityServiceTest {
|
|||||||
|
|
||||||
private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
|
private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
|
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
|
||||||
mMockVpn.establish(new LinkProperties(), vpnOwnerUid, vpnRange);
|
mMockVpn.establish(new LinkProperties(), vpnOwnerUid, vpnRange);
|
||||||
assertVpnUidRangesUpdated(true, vpnRange, vpnOwnerUid);
|
assertVpnUidRangesUpdated(true, vpnRange, vpnOwnerUid);
|
||||||
mMockVpn.setVpnType(vpnType);
|
mMockVpn.setVpnType(vpnType);
|
||||||
@@ -8477,7 +8738,7 @@ public class ConnectivityServiceTest {
|
|||||||
lp.setInterfaceName("tun0");
|
lp.setInterfaceName("tun0");
|
||||||
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
|
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
|
||||||
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
|
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
|
||||||
final UidRange vpnRange = UidRange.createForUser(VPN_USER);
|
final UidRange vpnRange = UidRange.createForUser(PRIMARY_USER);
|
||||||
Set<UidRange> vpnRanges = Collections.singleton(vpnRange);
|
Set<UidRange> vpnRanges = Collections.singleton(vpnRange);
|
||||||
mMockVpn.establish(lp, VPN_UID, vpnRanges);
|
mMockVpn.establish(lp, VPN_UID, vpnRanges);
|
||||||
assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID);
|
assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID);
|
||||||
|
|||||||
Reference in New Issue
Block a user