Merge changes Ia68f482a,I4911e214,Ied379654,I66d18512,Ie8e1bd63

* changes:
  Check registering system default callback needs NETWORK_SETTINGS.
  Move VPN code from ConnectivityService to VpnManagerService.
  Add a skeleton VpnManagerService, and start it on boot.
  Convert LockdownVpnTracker to NetworkCallbacks.
  Minor fixes to VpnTransportInfo.
This commit is contained in:
Lorenzo Colitti
2021-02-15 00:00:47 +00:00
committed by Gerrit Code Review
10 changed files with 427 additions and 947 deletions

View File

@@ -49,17 +49,6 @@ public final class ConnectivityFrameworkInitializer {
} }
); );
// TODO: move outside of the connectivity JAR
SystemServiceRegistry.registerContextAwareService(
Context.VPN_MANAGEMENT_SERVICE,
VpnManager.class,
(context) -> {
final ConnectivityManager cm = context.getSystemService(
ConnectivityManager.class);
return cm.createVpnManager();
}
);
SystemServiceRegistry.registerContextAwareService( SystemServiceRegistry.registerContextAwareService(
Context.CONNECTIVITY_DIAGNOSTICS_SERVICE, Context.CONNECTIVITY_DIAGNOSTICS_SERVICE,
ConnectivityDiagnosticsManager.class, ConnectivityDiagnosticsManager.class,

View File

@@ -824,6 +824,7 @@ public class ConnectivityManager {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562) @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
private final IConnectivityManager mService; private final IConnectivityManager mService;
/** /**
* A kludge to facilitate static access where a Context pointer isn't available, like in the * A kludge to facilitate static access where a Context pointer isn't available, like in the
* case of the static set/getProcessDefaultNetwork methods and from the Network class. * case of the static set/getProcessDefaultNetwork methods and from the Network class.
@@ -1069,106 +1070,55 @@ public class ConnectivityManager {
} }
/** /**
* Checks if a VPN app supports always-on mode. * Calls VpnManager#isAlwaysOnVpnPackageSupportedForUser.
* * @deprecated TODO: remove when callers have migrated to VpnManager.
* In order to support the always-on feature, an app has to
* <ul>
* <li>target {@link VERSION_CODES#N API 24} or above, and
* <li>not opt out through the {@link VpnService#SERVICE_META_DATA_SUPPORTS_ALWAYS_ON}
* meta-data field.
* </ul>
*
* @param userId The identifier of the user for whom the VPN app is installed.
* @param vpnPackage The canonical package name of the VPN app.
* @return {@code true} if and only if the VPN app exists and supports always-on mode.
* @hide * @hide
*/ */
@Deprecated
public boolean isAlwaysOnVpnPackageSupportedForUser(int userId, @Nullable String vpnPackage) { public boolean isAlwaysOnVpnPackageSupportedForUser(int userId, @Nullable String vpnPackage) {
try { return getVpnManager().isAlwaysOnVpnPackageSupportedForUser(userId, vpnPackage);
return mService.isAlwaysOnVpnPackageSupported(userId, vpnPackage);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} }
/** /**
* Configures an always-on VPN connection through a specific application. * Calls VpnManager#setAlwaysOnVpnPackageForUser.
* This connection is automatically granted and persisted after a reboot. * @deprecated TODO: remove when callers have migrated to VpnManager.
*
* <p>The designated package should declare a {@link VpnService} in its
* manifest guarded by {@link android.Manifest.permission.BIND_VPN_SERVICE},
* otherwise the call will fail.
*
* @param userId The identifier of the user to set an always-on VPN for.
* @param vpnPackage The package name for an installed VPN app on the device, or {@code null}
* to remove an existing always-on VPN configuration.
* @param lockdownEnabled {@code true} to disallow networking when the VPN is not connected or
* {@code false} otherwise.
* @param lockdownAllowlist The list of packages that are allowed to access network directly
* when VPN is in lockdown mode but is not running. Non-existent packages are ignored so
* this method must be called when a package that should be allowed is installed or
* uninstalled.
* @return {@code true} if the package is set as always-on VPN controller;
* {@code false} otherwise.
* @hide * @hide
*/ */
@RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN) @Deprecated
public boolean setAlwaysOnVpnPackageForUser(int userId, @Nullable String vpnPackage, public boolean setAlwaysOnVpnPackageForUser(int userId, @Nullable String vpnPackage,
boolean lockdownEnabled, @Nullable List<String> lockdownAllowlist) { boolean lockdownEnabled, @Nullable List<String> lockdownAllowlist) {
try { return getVpnManager().setAlwaysOnVpnPackageForUser(userId, vpnPackage, lockdownEnabled,
return mService.setAlwaysOnVpnPackage( lockdownAllowlist);
userId, vpnPackage, lockdownEnabled, lockdownAllowlist);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} }
/** /**
* Returns the package name of the currently set always-on VPN application. * Calls VpnManager#getAlwaysOnVpnPackageForUser.
* If there is no always-on VPN set, or the VPN is provided by the system instead * @deprecated TODO: remove when callers have migrated to VpnManager.
* of by an app, {@code null} will be returned.
*
* @return Package name of VPN controller responsible for always-on VPN,
* or {@code null} if none is set.
* @hide * @hide
*/ */
@RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN) @Deprecated
public String getAlwaysOnVpnPackageForUser(int userId) { public String getAlwaysOnVpnPackageForUser(int userId) {
try { return getVpnManager().getAlwaysOnVpnPackageForUser(userId);
return mService.getAlwaysOnVpnPackage(userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} }
/** /**
* @return whether always-on VPN is in lockdown mode. * Calls VpnManager#isVpnLockdownEnabled.
* * @deprecated TODO: remove when callers have migrated to VpnManager.
* @hide * @hide
**/ */
@RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN) @Deprecated
public boolean isVpnLockdownEnabled(int userId) { public boolean isVpnLockdownEnabled(int userId) {
try { return getVpnManager().isVpnLockdownEnabled(userId);
return mService.isVpnLockdownEnabled(userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} }
/** /**
* @return the list of packages that are allowed to access network when always-on VPN is in * Calls VpnManager#getVpnLockdownAllowlist.
* lockdown mode but not connected. Returns {@code null} when VPN lockdown is not active. * @deprecated TODO: remove when callers have migrated to VpnManager.
*
* @hide * @hide
**/ */
@RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN) @Deprecated
public List<String> getVpnLockdownWhitelist(int userId) { public List<String> getVpnLockdownWhitelist(int userId) {
try { return getVpnManager().getVpnLockdownAllowlist(userId);
return mService.getVpnLockdownWhitelist(userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} }
/** /**
@@ -1220,6 +1170,45 @@ public class ConnectivityManager {
} }
} }
/**
* Informs ConnectivityService of whether the legacy lockdown VPN, as implemented by
* LockdownVpnTracker, is in use. This is deprecated for new devices starting from Android 12
* but is still supported for backwards compatibility.
* <p>
* This type of VPN is assumed always to use the system default network, and must always declare
* exactly one underlying network, which is the network that was the default when the VPN
* connected.
* <p>
* Calling this method with {@code true} enables legacy behaviour, specifically:
* <ul>
* <li>Any VPN that applies to userId 0 behaves specially with respect to deprecated
* {@link #CONNECTIVITY_ACTION} broadcasts. Any such broadcasts will have the state in the
* {@link #EXTRA_NETWORK_INFO} replaced by state of the VPN network. Also, any time the VPN
* connects, a {@link #CONNECTIVITY_ACTION} broadcast will be sent for the network
* underlying the VPN.</li>
* <li>Deprecated APIs that return {@link NetworkInfo} objects will have their state
* similarly replaced by the VPN network state.</li>
* <li>Information on current network interfaces passed to NetworkStatsService will not
* include any VPN interfaces.</li>
* </ul>
*
* @param enabled whether legacy lockdown VPN is enabled or disabled
*
* TODO: @SystemApi(client = MODULE_LIBRARIES)
*
* @hide
*/
@RequiresPermission(anyOf = {
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
android.Manifest.permission.NETWORK_SETTINGS})
public void setLegacyLockdownVpnEnabled(boolean enabled) {
try {
mService.setLegacyLockdownVpnEnabled(enabled);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/** /**
* Returns details about the currently active default data network * Returns details about the currently active default data network
* for a given uid. This is for internal use only to avoid spying * for a given uid. This is for internal use only to avoid spying
@@ -3180,20 +3169,13 @@ public class ConnectivityManager {
} }
/** /**
* If the LockdownVpn mechanism is enabled, updates the vpn * Calls VpnManager#updateLockdownVpn.
* with a reload of its profile. * @deprecated TODO: remove when callers have migrated to VpnManager.
* * @hide
* @return a boolean with {@code} indicating success
*
* <p>This method can only be called by the system UID
* {@hide}
*/ */
@Deprecated
public boolean updateLockdownVpn() { public boolean updateLockdownVpn() {
try { return getVpnManager().updateLockdownVpn();
return mService.updateLockdownVpn();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} }
/** /**
@@ -4557,6 +4539,8 @@ public class ConnectivityManager {
try { try {
mService.factoryReset(); mService.factoryReset();
mTetheringManager.stopAllTethering(); mTetheringManager.stopAllTethering();
// TODO: Migrate callers to VpnManager#factoryReset.
getVpnManager().factoryReset();
} catch (RemoteException e) { } catch (RemoteException e) {
throw e.rethrowFromSystemServer(); throw e.rethrowFromSystemServer();
} }
@@ -4850,9 +4834,13 @@ public class ConnectivityManager {
return new TestNetworkManager(ITestNetworkManager.Stub.asInterface(tnBinder)); return new TestNetworkManager(ITestNetworkManager.Stub.asInterface(tnBinder));
} }
/** @hide */ /**
public VpnManager createVpnManager() { * Temporary hack to shim calls from ConnectivityManager to VpnManager. We cannot store a
return new VpnManager(mContext, mService); * private final mVpnManager because ConnectivityManager is initialized before VpnManager.
* @hide TODO: remove.
*/
public VpnManager getVpnManager() {
return mContext.getSystemService(VpnManager.class);
} }
/** @hide */ /** @hide */

View File

@@ -43,9 +43,6 @@ import android.os.PersistableBundle;
import android.os.ResultReceiver; import android.os.ResultReceiver;
import com.android.connectivity.aidl.INetworkAgent; import com.android.connectivity.aidl.INetworkAgent;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
/** /**
* Interface that answers queries about, and allows changing, the * Interface that answers queries about, and allows changing, the
@@ -123,35 +120,8 @@ interface IConnectivityManager
ProxyInfo getProxyForNetwork(in Network nework); ProxyInfo getProxyForNetwork(in Network nework);
boolean prepareVpn(String oldPackage, String newPackage, int userId);
void setVpnPackageAuthorization(String packageName, int userId, int vpnType);
ParcelFileDescriptor establishVpn(in VpnConfig config);
boolean provisionVpnProfile(in VpnProfile profile, String packageName);
void deleteVpnProfile(String packageName);
void startVpnProfile(String packageName);
void stopVpnProfile(String packageName);
VpnConfig getVpnConfig(int userId);
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
void startLegacyVpn(in VpnProfile profile);
LegacyVpnInfo getLegacyVpnInfo(int userId);
boolean updateLockdownVpn();
boolean isAlwaysOnVpnPackageSupported(int userId, String packageName);
boolean setAlwaysOnVpnPackage(int userId, String packageName, boolean lockdown,
in List<String> lockdownWhitelist);
String getAlwaysOnVpnPackage(int userId);
boolean isVpnLockdownEnabled(int userId);
List<String> getVpnLockdownWhitelist(int userId);
void setRequireVpnForUids(boolean requireVpn, in UidRange[] ranges); void setRequireVpnForUids(boolean requireVpn, in UidRange[] ranges);
void setLegacyLockdownVpnEnabled(boolean enabled);
void setProvisioningNotificationVisible(boolean visible, int networkType, in String action); void setProvisioningNotificationVisible(boolean visible, int networkType, in String action);
@@ -200,10 +170,6 @@ interface IConnectivityManager
int getRestoreDefaultNetworkDelay(int networkType); int getRestoreDefaultNetworkDelay(int networkType);
boolean addVpnAddress(String address, int prefixLength);
boolean removeVpnAddress(String address, int prefixLength);
boolean setUnderlyingNetworksForVpn(in Network[] networks);
void factoryReset(); void factoryReset();
void startNattKeepalive(in Network network, int intervalSeconds, void startNattKeepalive(in Network network, int intervalSeconds,
@@ -223,8 +189,6 @@ interface IConnectivityManager
byte[] getNetworkWatchlistConfigHash(); byte[] getNetworkWatchlistConfigHash();
int getConnectionOwnerUid(in ConnectionInfo connectionInfo); int getConnectionOwnerUid(in ConnectionInfo connectionInfo);
boolean isCallerCurrentAlwaysOnVpnApp();
boolean isCallerCurrentAlwaysOnVpnLockdownApp();
void registerConnectivityDiagnosticsCallback(in IConnectivityDiagnosticsCallback callback, void registerConnectivityDiagnosticsCallback(in IConnectivityDiagnosticsCallback callback,
in NetworkRequest request, String callingPackageName); in NetworkRequest request, String callingPackageName);

View File

@@ -21,6 +21,7 @@ import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.IntDef; import android.annotation.IntDef;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.UserIdInt; import android.annotation.UserIdInt;
import android.app.Activity; import android.app.Activity;
import android.content.ComponentName; import android.content.ComponentName;
@@ -37,6 +38,7 @@ import java.io.IOException;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.util.List;
/** /**
* This class provides an interface for apps to manage platform VPN profiles * This class provides an interface for apps to manage platform VPN profiles
@@ -76,13 +78,19 @@ public class VpnManager {
@Deprecated @Deprecated
public static final int TYPE_VPN_LEGACY = 3; public static final int TYPE_VPN_LEGACY = 3;
/**
* Channel for VPN notifications.
* @hide
*/
public static final String NOTIFICATION_CHANNEL_VPN = "VPN";
/** @hide */ /** @hide */
@IntDef(value = {TYPE_VPN_NONE, TYPE_VPN_SERVICE, TYPE_VPN_PLATFORM, TYPE_VPN_LEGACY}) @IntDef(value = {TYPE_VPN_NONE, TYPE_VPN_SERVICE, TYPE_VPN_PLATFORM, TYPE_VPN_LEGACY})
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface VpnType {} public @interface VpnType {}
@NonNull private final Context mContext; @NonNull private final Context mContext;
@NonNull private final IConnectivityManager mService; @NonNull private final IVpnManager mService;
private static Intent getIntentForConfirmation() { private static Intent getIntentForConfirmation() {
final Intent intent = new Intent(); final Intent intent = new Intent();
@@ -101,9 +109,9 @@ public class VpnManager {
* *
* @hide * @hide
*/ */
public VpnManager(@NonNull Context ctx, @NonNull IConnectivityManager service) { public VpnManager(@NonNull Context ctx, @NonNull IVpnManager service) {
mContext = checkNotNull(ctx, "missing Context"); mContext = checkNotNull(ctx, "missing Context");
mService = checkNotNull(service, "missing IConnectivityManager"); mService = checkNotNull(service, "missing IVpnManager");
} }
/** /**
@@ -194,6 +202,19 @@ public class VpnManager {
} }
} }
/**
* Resets all VPN settings back to factory defaults.
* @hide
*/
@RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
public void factoryReset() {
try {
mService.factoryReset();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/** /**
* Prepare for a VPN application. * Prepare for a VPN application.
* VPN permissions are checked in the {@link Vpn} class. If the caller is not {@code userId}, * VPN permissions are checked in the {@link Vpn} class. If the caller is not {@code userId},
@@ -239,6 +260,108 @@ public class VpnManager {
} }
} }
/**
* Checks if a VPN app supports always-on mode.
*
* In order to support the always-on feature, an app has to
* <ul>
* <li>target {@link VERSION_CODES#N API 24} or above, and
* <li>not opt out through the {@link VpnService#SERVICE_META_DATA_SUPPORTS_ALWAYS_ON}
* meta-data field.
* </ul>
*
* @param userId The identifier of the user for whom the VPN app is installed.
* @param vpnPackage The canonical package name of the VPN app.
* @return {@code true} if and only if the VPN app exists and supports always-on mode.
* @hide
*/
public boolean isAlwaysOnVpnPackageSupportedForUser(int userId, @Nullable String vpnPackage) {
try {
return mService.isAlwaysOnVpnPackageSupported(userId, vpnPackage);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Configures an always-on VPN connection through a specific application.
* This connection is automatically granted and persisted after a reboot.
*
* <p>The designated package should declare a {@link VpnService} in its
* manifest guarded by {@link android.Manifest.permission.BIND_VPN_SERVICE},
* otherwise the call will fail.
*
* @param userId The identifier of the user to set an always-on VPN for.
* @param vpnPackage The package name for an installed VPN app on the device, or {@code null}
* to remove an existing always-on VPN configuration.
* @param lockdownEnabled {@code true} to disallow networking when the VPN is not connected or
* {@code false} otherwise.
* @param lockdownAllowlist The list of packages that are allowed to access network directly
* when VPN is in lockdown mode but is not running. Non-existent packages are ignored so
* this method must be called when a package that should be allowed is installed or
* uninstalled.
* @return {@code true} if the package is set as always-on VPN controller;
* {@code false} otherwise.
* @hide
*/
@RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
public boolean setAlwaysOnVpnPackageForUser(int userId, @Nullable String vpnPackage,
boolean lockdownEnabled, @Nullable List<String> lockdownAllowlist) {
try {
return mService.setAlwaysOnVpnPackage(
userId, vpnPackage, lockdownEnabled, lockdownAllowlist);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Returns the package name of the currently set always-on VPN application.
* If there is no always-on VPN set, or the VPN is provided by the system instead
* of by an app, {@code null} will be returned.
*
* @return Package name of VPN controller responsible for always-on VPN,
* or {@code null} if none is set.
* @hide
*/
@RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
public String getAlwaysOnVpnPackageForUser(int userId) {
try {
return mService.getAlwaysOnVpnPackage(userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* @return whether always-on VPN is in lockdown mode.
*
* @hide
**/
@RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
public boolean isVpnLockdownEnabled(int userId) {
try {
return mService.isVpnLockdownEnabled(userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* @return the list of packages that are allowed to access network when always-on VPN is in
* lockdown mode but not connected. Returns {@code null} when VPN lockdown is not active.
*
* @hide
**/
@RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
public List<String> getVpnLockdownAllowlist(int userId) {
try {
return mService.getVpnLockdownAllowlist(userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/** /**
* Return the legacy VPN information for the specified user ID. * Return the legacy VPN information for the specified user ID.
* @hide * @hide

View File

@@ -170,12 +170,11 @@ public class VpnService extends Service {
"android.net.VpnService.SUPPORTS_ALWAYS_ON"; "android.net.VpnService.SUPPORTS_ALWAYS_ON";
/** /**
* Use IConnectivityManager since those methods are hidden and not * Use IVpnManager since those methods are hidden and not available in VpnManager.
* available in ConnectivityManager.
*/ */
private static IConnectivityManager getService() { private static IVpnManager getService() {
return IConnectivityManager.Stub.asInterface( return IVpnManager.Stub.asInterface(
ServiceManager.getService(Context.CONNECTIVITY_SERVICE)); ServiceManager.getService(Context.VPN_MANAGEMENT_SERVICE));
} }
/** /**
@@ -226,15 +225,15 @@ public class VpnService extends Service {
@SystemApi @SystemApi
@RequiresPermission(android.Manifest.permission.CONTROL_VPN) @RequiresPermission(android.Manifest.permission.CONTROL_VPN)
public static void prepareAndAuthorize(Context context) { public static void prepareAndAuthorize(Context context) {
IConnectivityManager cm = getService(); IVpnManager vm = getService();
String packageName = context.getPackageName(); String packageName = context.getPackageName();
try { try {
// Only prepare if we're not already prepared. // Only prepare if we're not already prepared.
int userId = context.getUserId(); int userId = context.getUserId();
if (!cm.prepareVpn(packageName, null, userId)) { if (!vm.prepareVpn(packageName, null, userId)) {
cm.prepareVpn(null, packageName, userId); vm.prepareVpn(null, packageName, userId);
} }
cm.setVpnPackageAuthorization(packageName, userId, VpnManager.TYPE_VPN_SERVICE); vm.setVpnPackageAuthorization(packageName, userId, VpnManager.TYPE_VPN_SERVICE);
} catch (RemoteException e) { } catch (RemoteException e) {
// ignore // ignore
} }

View File

@@ -79,7 +79,6 @@ public class NetworkNotificationManager {
// server. // server.
public static final String NOTIFICATION_CHANNEL_NETWORK_STATUS = "NETWORK_STATUS"; public static final String NOTIFICATION_CHANNEL_NETWORK_STATUS = "NETWORK_STATUS";
public static final String NOTIFICATION_CHANNEL_NETWORK_ALERTS = "NETWORK_ALERTS"; public static final String NOTIFICATION_CHANNEL_NETWORK_ALERTS = "NETWORK_ALERTS";
public static final String NOTIFICATION_CHANNEL_VPN = "VPN";
// The context is for the current user (system server) // The context is for the current user (system server)
private final Context mContext; private final Context mContext;

View File

@@ -49,7 +49,7 @@ public class VpnManagerTest {
private static final String IDENTITY_STRING = "Identity"; private static final String IDENTITY_STRING = "Identity";
private static final byte[] PSK_BYTES = "preSharedKey".getBytes(); private static final byte[] PSK_BYTES = "preSharedKey".getBytes();
private IConnectivityManager mMockCs; private IVpnManager mMockService;
private VpnManager mVpnManager; private VpnManager mVpnManager;
private final MockContext mMockContext = private final MockContext mMockContext =
new MockContext() { new MockContext() {
@@ -61,24 +61,26 @@ public class VpnManagerTest {
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
mMockCs = mock(IConnectivityManager.class); mMockService = mock(IVpnManager.class);
mVpnManager = new VpnManager(mMockContext, mMockCs); mVpnManager = new VpnManager(mMockContext, mMockService);
} }
@Test @Test
public void testProvisionVpnProfilePreconsented() throws Exception { public void testProvisionVpnProfilePreconsented() throws Exception {
final PlatformVpnProfile profile = getPlatformVpnProfile(); final PlatformVpnProfile profile = getPlatformVpnProfile();
when(mMockCs.provisionVpnProfile(any(VpnProfile.class), eq(PKG_NAME))).thenReturn(true); when(mMockService.provisionVpnProfile(any(VpnProfile.class), eq(PKG_NAME)))
.thenReturn(true);
// Expect there to be no intent returned, as consent has already been granted. // Expect there to be no intent returned, as consent has already been granted.
assertNull(mVpnManager.provisionVpnProfile(profile)); assertNull(mVpnManager.provisionVpnProfile(profile));
verify(mMockCs).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME)); verify(mMockService).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME));
} }
@Test @Test
public void testProvisionVpnProfileNeedsConsent() throws Exception { public void testProvisionVpnProfileNeedsConsent() throws Exception {
final PlatformVpnProfile profile = getPlatformVpnProfile(); final PlatformVpnProfile profile = getPlatformVpnProfile();
when(mMockCs.provisionVpnProfile(any(VpnProfile.class), eq(PKG_NAME))).thenReturn(false); when(mMockService.provisionVpnProfile(any(VpnProfile.class), eq(PKG_NAME)))
.thenReturn(false);
// Expect intent to be returned, as consent has not already been granted. // Expect intent to be returned, as consent has not already been granted.
final Intent intent = mVpnManager.provisionVpnProfile(profile); final Intent intent = mVpnManager.provisionVpnProfile(profile);
@@ -88,25 +90,25 @@ public class VpnManagerTest {
ComponentName.unflattenFromString( ComponentName.unflattenFromString(
"com.android.vpndialogs/com.android.vpndialogs.PlatformVpnConfirmDialog"); "com.android.vpndialogs/com.android.vpndialogs.PlatformVpnConfirmDialog");
assertEquals(expectedComponentName, intent.getComponent()); assertEquals(expectedComponentName, intent.getComponent());
verify(mMockCs).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME)); verify(mMockService).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME));
} }
@Test @Test
public void testDeleteProvisionedVpnProfile() throws Exception { public void testDeleteProvisionedVpnProfile() throws Exception {
mVpnManager.deleteProvisionedVpnProfile(); mVpnManager.deleteProvisionedVpnProfile();
verify(mMockCs).deleteVpnProfile(eq(PKG_NAME)); verify(mMockService).deleteVpnProfile(eq(PKG_NAME));
} }
@Test @Test
public void testStartProvisionedVpnProfile() throws Exception { public void testStartProvisionedVpnProfile() throws Exception {
mVpnManager.startProvisionedVpnProfile(); mVpnManager.startProvisionedVpnProfile();
verify(mMockCs).startVpnProfile(eq(PKG_NAME)); verify(mMockService).startVpnProfile(eq(PKG_NAME));
} }
@Test @Test
public void testStopProvisionedVpnProfile() throws Exception { public void testStopProvisionedVpnProfile() throws Exception {
mVpnManager.stopProvisionedVpnProfile(); mVpnManager.stopProvisionedVpnProfile();
verify(mMockCs).stopVpnProfile(eq(PKG_NAME)); verify(mMockService).stopVpnProfile(eq(PKG_NAME));
} }
private Ikev2VpnProfile getPlatformVpnProfile() throws Exception { private Ikev2VpnProfile getPlatformVpnProfile() throws Exception {

View File

@@ -17,7 +17,6 @@
package android.net; package android.net;
import static com.android.testutils.ParcelUtils.assertParcelSane; import static com.android.testutils.ParcelUtils.assertParcelSane;
import static com.android.testutils.ParcelUtils.assertParcelingIsLossless;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotEquals;
@@ -36,7 +35,6 @@ public class VpnTransportInfoTest {
public void testParceling() { public void testParceling() {
VpnTransportInfo v = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM); VpnTransportInfo v = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM);
assertParcelSane(v, 1 /* fieldCount */); assertParcelSane(v, 1 /* fieldCount */);
assertParcelingIsLossless(v);
} }
@Test @Test

View File

@@ -200,6 +200,7 @@ import android.net.ResolverParamsParcel;
import android.net.RouteInfo; import android.net.RouteInfo;
import android.net.RouteInfoParcel; import android.net.RouteInfoParcel;
import android.net.SocketKeepalive; import android.net.SocketKeepalive;
import android.net.TransportInfo;
import android.net.UidRange; import android.net.UidRange;
import android.net.UidRangeParcel; import android.net.UidRangeParcel;
import android.net.UnderlyingNetworkInfo; import android.net.UnderlyingNetworkInfo;
@@ -376,6 +377,7 @@ public class ConnectivityServiceTest {
private MockContext mServiceContext; private MockContext mServiceContext;
private HandlerThread mCsHandlerThread; private HandlerThread mCsHandlerThread;
private HandlerThread mVMSHandlerThread;
private ConnectivityService.Dependencies mDeps; private ConnectivityService.Dependencies mDeps;
private ConnectivityService mService; private ConnectivityService mService;
private WrappedConnectivityManager mCm; private WrappedConnectivityManager mCm;
@@ -390,6 +392,7 @@ public class ConnectivityServiceTest {
private TestNetIdManager mNetIdManager; private TestNetIdManager mNetIdManager;
private QosCallbackMockHelper mQosCallbackMockHelper; private QosCallbackMockHelper mQosCallbackMockHelper;
private QosCallbackTracker mQosCallbackTracker; private QosCallbackTracker mQosCallbackTracker;
private VpnManagerService mVpnManagerService;
// State variables required to emulate NetworkPolicyManagerService behaviour. // State variables required to emulate NetworkPolicyManagerService behaviour.
private int mUidRules = RULE_NONE; private int mUidRules = RULE_NONE;
@@ -1262,24 +1265,57 @@ public class ConnectivityServiceTest {
r -> new UidRangeParcel(r.start, r.stop)).toArray(UidRangeParcel[]::new); r -> new UidRangeParcel(r.start, r.stop)).toArray(UidRangeParcel[]::new);
} }
private void mockVpn(int uid) { private VpnManagerService makeVpnManagerService() {
synchronized (mService.mVpns) { final VpnManagerService.Dependencies deps = new VpnManagerService.Dependencies() {
int userId = UserHandle.getUserId(uid); public int getCallingUid() {
mMockVpn = new MockVpn(userId); return mDeps.getCallingUid();
// This has no effect unless the VPN is actually connected, because things like }
// getActiveNetworkForUidInternal call getNetworkAgentInfoForNetId on the VPN
// netId, and check if that network is actually connected. public HandlerThread makeHandlerThread() {
mService.mVpns.put(userId, mMockVpn); return mVMSHandlerThread;
} }
public KeyStore getKeyStore() {
return mKeyStore;
}
public INetd getNetd() {
return mMockNetd;
}
public INetworkManagementService getINetworkManagementService() {
return mNetworkManagementService;
}
};
return new VpnManagerService(mServiceContext, deps);
}
private void assertVpnTransportInfo(NetworkCapabilities nc, int type) {
assertNotNull(nc);
final TransportInfo ti = nc.getTransportInfo();
assertTrue("VPN TransportInfo is not a VpnTransportInfo: " + ti,
ti instanceof VpnTransportInfo);
assertEquals(type, ((VpnTransportInfo) ti).type);
} }
private void processBroadcastForVpn(Intent intent) { private void processBroadcastForVpn(Intent intent) {
// The BroadcastReceiver for this broadcast checks it is being run on the handler thread. // The BroadcastReceiver for this broadcast checks it is being run on the handler thread.
final Handler handler = new Handler(mCsHandlerThread.getLooper()); final Handler handler = new Handler(mVMSHandlerThread.getLooper());
handler.post(() -> mServiceContext.sendBroadcast(intent)); handler.post(() -> mServiceContext.sendBroadcast(intent));
HandlerUtils.waitForIdle(handler, TIMEOUT_MS);
waitForIdle(); waitForIdle();
} }
private void mockVpn(int uid) {
synchronized (mVpnManagerService.mVpns) {
int userId = UserHandle.getUserId(uid);
mMockVpn = new MockVpn(userId);
// Every running user always has a Vpn in the mVpns array, even if no VPN is running.
mVpnManagerService.mVpns.put(userId, mMockVpn);
}
}
private void mockUidNetworkingBlocked() { private void mockUidNetworkingBlocked() {
doAnswer(i -> mContext.getSystemService(NetworkPolicyManager.class) doAnswer(i -> mContext.getSystemService(NetworkPolicyManager.class)
.checkUidNetworkingBlocked(i.getArgument(0) /* uid */, mUidRules, .checkUidNetworkingBlocked(i.getArgument(0) /* uid */, mUidRules,
@@ -1403,6 +1439,7 @@ public class ConnectivityServiceTest {
initAlarmManager(mAlarmManager, mAlarmManagerThread.getThreadHandler()); initAlarmManager(mAlarmManager, mAlarmManagerThread.getThreadHandler());
mCsHandlerThread = new HandlerThread("TestConnectivityService"); mCsHandlerThread = new HandlerThread("TestConnectivityService");
mVMSHandlerThread = new HandlerThread("TestVpnManagerService");
mDeps = makeDependencies(); mDeps = makeDependencies();
returnRealCallingUid(); returnRealCallingUid();
mService = new ConnectivityService(mServiceContext, mService = new ConnectivityService(mServiceContext,
@@ -1425,6 +1462,8 @@ public class ConnectivityServiceTest {
// getSystemService() correctly. // getSystemService() correctly.
mCm = new WrappedConnectivityManager(InstrumentationRegistry.getContext(), mService); mCm = new WrappedConnectivityManager(InstrumentationRegistry.getContext(), mService);
mService.systemReadyInternal(); mService.systemReadyInternal();
mVpnManagerService = makeVpnManagerService();
mVpnManagerService.systemReady();
mockVpn(Process.myUid()); mockVpn(Process.myUid());
mCm.bindProcessToNetwork(null); mCm.bindProcessToNetwork(null);
mQosCallbackTracker = mock(QosCallbackTracker.class); mQosCallbackTracker = mock(QosCallbackTracker.class);
@@ -1452,7 +1491,6 @@ 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));
@@ -3873,6 +3911,24 @@ public class ConnectivityServiceTest {
mCm.unregisterNetworkCallback(cellNetworkCallback); mCm.unregisterNetworkCallback(cellNetworkCallback);
} }
@Test
public void testRegisterSystemDefaultCallbackRequiresNetworkSettings() throws Exception {
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(false /* validated */);
final Handler handler = new Handler(ConnectivityThread.getInstanceLooper());
final TestNetworkCallback callback = new TestNetworkCallback();
assertThrows(SecurityException.class,
() -> mCm.registerSystemDefaultNetworkCallback(callback, handler));
callback.assertNoCallback();
mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS,
PERMISSION_GRANTED);
mCm.registerSystemDefaultNetworkCallback(callback, handler);
callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
mCm.unregisterNetworkCallback(callback);
}
private void setCaptivePortalMode(int mode) { private void setCaptivePortalMode(int mode) {
ContentResolver cr = mServiceContext.getContentResolver(); ContentResolver cr = mServiceContext.getContentResolver();
Settings.Global.putInt(cr, Settings.Global.CAPTIVE_PORTAL_MODE, mode); Settings.Global.putInt(cr, Settings.Global.CAPTIVE_PORTAL_MODE, mode);
@@ -6484,6 +6540,8 @@ public class ConnectivityServiceTest {
assertTrue(nc.hasCapability(NET_CAPABILITY_VALIDATED)); assertTrue(nc.hasCapability(NET_CAPABILITY_VALIDATED));
assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED)); assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED));
assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
assertVpnTransportInfo(nc, VpnManager.TYPE_VPN_SERVICE);
} }
private void assertDefaultNetworkCapabilities(int userId, NetworkAgentWrapper... networks) { private void assertDefaultNetworkCapabilities(int userId, NetworkAgentWrapper... networks) {
@@ -6523,6 +6581,7 @@ public class ConnectivityServiceTest {
assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED)); assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED));
// A VPN without underlying networks is not suspended. // A VPN without underlying networks is not suspended.
assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
assertVpnTransportInfo(nc, VpnManager.TYPE_VPN_SERVICE);
final int userId = UserHandle.getUserId(Process.myUid()); final int userId = UserHandle.getUserId(Process.myUid());
assertDefaultNetworkCapabilities(userId /* no networks */); assertDefaultNetworkCapabilities(userId /* no networks */);
@@ -6686,6 +6745,7 @@ public class ConnectivityServiceTest {
// By default, VPN is set to track default network (i.e. its underlying networks is null). // 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. // In case of no default network, VPN is considered metered.
assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED)); assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED));
assertVpnTransportInfo(nc, VpnManager.TYPE_VPN_SERVICE);
// Connect to Cell; Cell is the default network. // Connect to Cell; Cell is the default network.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
@@ -6743,6 +6803,7 @@ public class ConnectivityServiceTest {
NetworkCapabilities nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); NetworkCapabilities nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
assertNotNull("nc=" + nc, nc.getUids()); assertNotNull("nc=" + nc, nc.getUids());
assertEquals(nc.getUids(), uidRangesForUid(uid)); assertEquals(nc.getUids(), uidRangesForUid(uid));
assertVpnTransportInfo(nc, VpnManager.TYPE_VPN_SERVICE);
// Set an underlying network and expect to see the VPN transports change. // Set an underlying network and expect to see the VPN transports change.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
@@ -6825,8 +6886,8 @@ public class ConnectivityServiceTest {
// 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(PRIMARY_USER, ALWAYS_ON_PACKAGE, true /* lockdown */, mVpnManagerService.setAlwaysOnVpnPackage(PRIMARY_USER, ALWAYS_ON_PACKAGE,
allowList); 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. // This is arguably overspecified: a UID that is not running doesn't have an active network.
@@ -6856,7 +6917,8 @@ public class ConnectivityServiceTest {
assertNull(mCm.getActiveNetworkForUid(uid)); assertNull(mCm.getActiveNetworkForUid(uid));
assertNotNull(mCm.getActiveNetworkForUid(restrictedUid)); assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
mService.setAlwaysOnVpnPackage(PRIMARY_USER, null, false /* lockdown */, allowList); mVpnManagerService.setAlwaysOnVpnPackage(PRIMARY_USER, null, false /* lockdown */,
allowList);
waitForIdle(); waitForIdle();
} }
@@ -7232,7 +7294,8 @@ public class ConnectivityServiceTest {
final int uid = Process.myUid(); final int uid = Process.myUid();
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); mVpnManagerService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */,
allowList);
waitForIdle(); waitForIdle();
UidRangeParcel firstHalf = new UidRangeParcel(1, VPN_UID - 1); UidRangeParcel firstHalf = new UidRangeParcel(1, VPN_UID - 1);
@@ -7254,7 +7317,7 @@ public class ConnectivityServiceTest {
assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
// Disable lockdown, expect to see the network unblocked. // Disable lockdown, expect to see the network unblocked.
mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList); mVpnManagerService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
callback.expectBlockedStatusCallback(false, mWiFiNetworkAgent); callback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent); defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
vpnUidCallback.assertNoCallback(); vpnUidCallback.assertNoCallback();
@@ -7267,7 +7330,8 @@ public class ConnectivityServiceTest {
// Add our UID to the allowlist and re-enable lockdown, expect network is not blocked. // Add our UID to the allowlist and re-enable lockdown, expect network is not blocked.
allowList.add(TEST_PACKAGE_NAME); allowList.add(TEST_PACKAGE_NAME);
mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList); mVpnManagerService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */,
allowList);
callback.assertNoCallback(); callback.assertNoCallback();
defaultCallback.assertNoCallback(); defaultCallback.assertNoCallback();
vpnUidCallback.assertNoCallback(); vpnUidCallback.assertNoCallback();
@@ -7300,11 +7364,12 @@ 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); mVpnManagerService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
waitForIdle(); 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); mVpnManagerService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */,
allowList);
waitForIdle(); waitForIdle();
expectNetworkRejectNonSecureVpn(inOrder, true, firstHalf, secondHalf); expectNetworkRejectNonSecureVpn(inOrder, true, firstHalf, secondHalf);
defaultCallback.expectBlockedStatusCallback(true, mWiFiNetworkAgent); defaultCallback.expectBlockedStatusCallback(true, mWiFiNetworkAgent);
@@ -7317,7 +7382,7 @@ public class ConnectivityServiceTest {
assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
// Disable lockdown. Everything is unblocked. // Disable lockdown. Everything is unblocked.
mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList); mVpnManagerService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent); defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
assertBlockedCallbackInAnyOrder(callback, false, mWiFiNetworkAgent, mCellNetworkAgent); assertBlockedCallbackInAnyOrder(callback, false, mWiFiNetworkAgent, mCellNetworkAgent);
vpnUidCallback.assertNoCallback(); vpnUidCallback.assertNoCallback();
@@ -7329,7 +7394,8 @@ public class ConnectivityServiceTest {
// Enable and disable an always-on VPN package without lockdown. Expect no changes. // Enable and disable an always-on VPN package without lockdown. Expect no changes.
reset(mMockNetd); reset(mMockNetd);
mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, false /* lockdown */, allowList); mVpnManagerService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, false /* lockdown */,
allowList);
inOrder.verify(mMockNetd, never()).networkRejectNonSecureVpn(anyBoolean(), any()); inOrder.verify(mMockNetd, never()).networkRejectNonSecureVpn(anyBoolean(), any());
callback.assertNoCallback(); callback.assertNoCallback();
defaultCallback.assertNoCallback(); defaultCallback.assertNoCallback();
@@ -7340,7 +7406,7 @@ public class ConnectivityServiceTest {
assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList); mVpnManagerService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
inOrder.verify(mMockNetd, never()).networkRejectNonSecureVpn(anyBoolean(), any()); inOrder.verify(mMockNetd, never()).networkRejectNonSecureVpn(anyBoolean(), any());
callback.assertNoCallback(); callback.assertNoCallback();
defaultCallback.assertNoCallback(); defaultCallback.assertNoCallback();
@@ -7352,7 +7418,8 @@ public class ConnectivityServiceTest {
assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
// Enable lockdown and connect a VPN. The VPN is not blocked. // Enable lockdown and connect a VPN. The VPN is not blocked.
mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList); mVpnManagerService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */,
allowList);
defaultCallback.expectBlockedStatusCallback(true, mWiFiNetworkAgent); defaultCallback.expectBlockedStatusCallback(true, mWiFiNetworkAgent);
assertBlockedCallbackInAnyOrder(callback, true, mWiFiNetworkAgent, mCellNetworkAgent); assertBlockedCallbackInAnyOrder(callback, true, mWiFiNetworkAgent, mCellNetworkAgent);
vpnUidCallback.assertNoCallback(); vpnUidCallback.assertNoCallback();
@@ -7398,11 +7465,14 @@ public class ConnectivityServiceTest {
when(mKeyStore.get(Credentials.VPN + profileName)).thenReturn(encodedProfile); when(mKeyStore.get(Credentials.VPN + profileName)).thenReturn(encodedProfile);
} }
private void establishLegacyLockdownVpn() throws Exception { private void establishLegacyLockdownVpn(Network underlying) throws Exception {
// The legacy lockdown VPN only supports userId 0, and must have an underlying network.
assertNotNull(underlying);
mMockVpn.setVpnType(VpnManager.TYPE_VPN_LEGACY); mMockVpn.setVpnType(VpnManager.TYPE_VPN_LEGACY);
// The legacy lockdown VPN only supports userId 0. // The legacy lockdown VPN only supports userId 0.
final Set<UidRange> ranges = Collections.singleton(UidRange.createForUser(PRIMARY_USER)); final Set<UidRange> ranges = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
mMockVpn.registerAgent(ranges); mMockVpn.registerAgent(ranges);
mMockVpn.setUnderlyingNetworks(new Network[]{underlying});
mMockVpn.connect(true); mMockVpn.connect(true);
} }
@@ -7410,6 +7480,9 @@ public class ConnectivityServiceTest {
public void testLegacyLockdownVpn() throws Exception { public void testLegacyLockdownVpn() throws Exception {
mServiceContext.setPermission( mServiceContext.setPermission(
Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED); Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED);
// For LockdownVpnTracker to call registerSystemDefaultNetworkCallback.
mServiceContext.setPermission(
Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED);
final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build(); final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
final TestNetworkCallback callback = new TestNetworkCallback(); final TestNetworkCallback callback = new TestNetworkCallback();
@@ -7418,6 +7491,10 @@ public class ConnectivityServiceTest {
final TestNetworkCallback defaultCallback = new TestNetworkCallback(); final TestNetworkCallback defaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(defaultCallback); mCm.registerDefaultNetworkCallback(defaultCallback);
final TestNetworkCallback systemDefaultCallback = new TestNetworkCallback();
mCm.registerSystemDefaultNetworkCallback(systemDefaultCallback,
new Handler(ConnectivityThread.getInstanceLooper()));
// Pretend lockdown VPN was configured. // Pretend lockdown VPN was configured.
setupLegacyLockdownVpn(); setupLegacyLockdownVpn();
@@ -7447,6 +7524,7 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.connect(false /* validated */); mCellNetworkAgent.connect(false /* validated */);
callback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent); callback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent); defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
systemDefaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
waitForIdle(); waitForIdle();
assertNull(mMockVpn.getAgent()); assertNull(mMockVpn.getAgent());
@@ -7458,6 +7536,8 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.sendLinkProperties(cellLp); mCellNetworkAgent.sendLinkProperties(cellLp);
callback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); callback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
defaultCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); defaultCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
systemDefaultCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED,
mCellNetworkAgent);
waitForIdle(); waitForIdle();
assertNull(mMockVpn.getAgent()); assertNull(mMockVpn.getAgent());
@@ -7467,6 +7547,7 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.disconnect(); mCellNetworkAgent.disconnect();
callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
systemDefaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
b1.expectBroadcast(); b1.expectBroadcast();
// When lockdown VPN is active, the NetworkInfo state in CONNECTIVITY_ACTION is overwritten // When lockdown VPN is active, the NetworkInfo state in CONNECTIVITY_ACTION is overwritten
@@ -7476,6 +7557,7 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.connect(false /* validated */); mCellNetworkAgent.connect(false /* validated */);
callback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent); callback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent); defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
systemDefaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
b1.expectBroadcast(); b1.expectBroadcast();
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
@@ -7498,9 +7580,10 @@ public class ConnectivityServiceTest {
mMockVpn.expectStartLegacyVpnRunner(); mMockVpn.expectStartLegacyVpnRunner();
b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED); b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
ExpectedBroadcast b2 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED); ExpectedBroadcast b2 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED);
establishLegacyLockdownVpn(); establishLegacyLockdownVpn(mCellNetworkAgent.getNetwork());
callback.expectAvailableThenValidatedCallbacks(mMockVpn); callback.expectAvailableThenValidatedCallbacks(mMockVpn);
defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn); defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
systemDefaultCallback.assertNoCallback();
NetworkCapabilities vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); NetworkCapabilities vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
b1.expectBroadcast(); b1.expectBroadcast();
b2.expectBroadcast(); b2.expectBroadcast();
@@ -7512,9 +7595,7 @@ public class ConnectivityServiceTest {
assertTrue(vpnNc.hasTransport(TRANSPORT_CELLULAR)); assertTrue(vpnNc.hasTransport(TRANSPORT_CELLULAR));
assertFalse(vpnNc.hasTransport(TRANSPORT_WIFI)); assertFalse(vpnNc.hasTransport(TRANSPORT_WIFI));
assertFalse(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED)); assertFalse(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED));
VpnTransportInfo ti = (VpnTransportInfo) vpnNc.getTransportInfo(); assertVpnTransportInfo(vpnNc, VpnManager.TYPE_VPN_LEGACY);
assertNotNull(ti);
assertEquals(VpnManager.TYPE_VPN_LEGACY, ti.type);
// Switch default network from cell to wifi. Expect VPN to disconnect and reconnect. // Switch default network from cell to wifi. Expect VPN to disconnect and reconnect.
final LinkProperties wifiLp = new LinkProperties(); final LinkProperties wifiLp = new LinkProperties();
@@ -7542,11 +7623,10 @@ public class ConnectivityServiceTest {
// fact that a VPN is connected should only result in the VPN itself being unblocked, not // fact that a VPN is connected should only result in the VPN itself being unblocked, not
// any other network. Bug in isUidBlockedByVpn? // any other network. Bug in isUidBlockedByVpn?
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
callback.expectCapabilitiesThat(mMockVpn, nc -> nc.hasTransport(TRANSPORT_WIFI));
callback.expectCallback(CallbackEntry.LOST, mMockVpn); callback.expectCallback(CallbackEntry.LOST, mMockVpn);
defaultCallback.expectCapabilitiesThat(mMockVpn, nc -> nc.hasTransport(TRANSPORT_WIFI));
defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn); defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent); defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent);
systemDefaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
// While the VPN is reconnecting on the new network, everything is blocked. // While the VPN is reconnecting on the new network, everything is blocked.
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
@@ -7557,9 +7637,10 @@ public class ConnectivityServiceTest {
// The VPN comes up again on wifi. // The VPN comes up again on wifi.
b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED); b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
b2 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED); b2 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
establishLegacyLockdownVpn(); establishLegacyLockdownVpn(mWiFiNetworkAgent.getNetwork());
callback.expectAvailableThenValidatedCallbacks(mMockVpn); callback.expectAvailableThenValidatedCallbacks(mMockVpn);
defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn); defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
systemDefaultCallback.assertNoCallback();
b1.expectBroadcast(); b1.expectBroadcast();
b2.expectBroadcast(); b2.expectBroadcast();
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
@@ -7573,14 +7654,10 @@ public class ConnectivityServiceTest {
assertTrue(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED)); assertTrue(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED));
// Disconnect cell. Nothing much happens since it's not the default network. // 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(); mCellNetworkAgent.disconnect();
b1.expectBroadcast();
callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
defaultCallback.assertNoCallback(); defaultCallback.assertNoCallback();
systemDefaultCallback.assertNoCallback();
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
@@ -7590,6 +7667,7 @@ public class ConnectivityServiceTest {
b1 = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED); b1 = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED);
mWiFiNetworkAgent.disconnect(); mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
systemDefaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
b1.expectBroadcast(); b1.expectBroadcast();
callback.expectCapabilitiesThat(mMockVpn, nc -> !nc.hasTransport(TRANSPORT_WIFI)); callback.expectCapabilitiesThat(mMockVpn, nc -> !nc.hasTransport(TRANSPORT_WIFI));
b2 = expectConnectivityAction(TYPE_VPN, DetailedState.DISCONNECTED); b2 = expectConnectivityAction(TYPE_VPN, DetailedState.DISCONNECTED);