Move VPN code from ConnectivityService to VpnManagerService.

ConnectivityService itself does not depend on mVpns or the Vpn
class any more. Most of this CL is simply moving code from one
class to another:

- Move the AIDL calls from IConnectivityManager to IVpnManager.
- Move the implementation from ConnectivityService to
  the new VpnManagerService.
- Move the APIs from ConnectivityManager to VpnManager, but
  temporarily maintain some shims in ConnectivityManager for the
  moved calls so that existing callers do not have to be modified
  in this CL.
- Update VpnService to call IVpnManager instead of
  IConnectivityManager.
- Move the code that registers the VpnManager service from
  ConnectivityFrameworkInitializer to SystemServiceRegistry.

Bug: 173331190
Test: atest HostsideVpnTests FrameworksNetTests CtsNetTestCases
Change-Id: I4911e2144df721a94fa00da9edf0dc372a7091c2
This commit is contained in:
Lorenzo Colitti
2021-02-04 17:32:07 +09:00
parent ee1740ddf1
commit cd67529417
8 changed files with 244 additions and 900 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();
}
} }
/** /**
@@ -3219,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();
}
} }
/** /**
@@ -4596,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();
} }
@@ -4889,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

@@ -42,9 +42,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
@@ -122,34 +119,6 @@ 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 setLegacyLockdownVpnEnabled(boolean enabled);
@@ -200,10 +169,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 +188,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
@@ -88,7 +90,7 @@ public class VpnManager {
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();
@@ -107,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");
} }
/** /**
@@ -200,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},
@@ -245,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

@@ -16,7 +16,6 @@
package com.android.server; package com.android.server;
import static android.Manifest.permission.NETWORK_STACK;
import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE; import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_ATTEMPTED_BITMASK; import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_ATTEMPTED_BITMASK;
@@ -138,7 +137,6 @@ import android.net.UidRangeParcel;
import android.net.UnderlyingNetworkInfo; import android.net.UnderlyingNetworkInfo;
import android.net.Uri; import android.net.Uri;
import android.net.VpnManager; import android.net.VpnManager;
import android.net.VpnService;
import android.net.VpnTransportInfo; import android.net.VpnTransportInfo;
import android.net.metrics.INetdEventListener; import android.net.metrics.INetdEventListener;
import android.net.metrics.IpConnectivityLog; import android.net.metrics.IpConnectivityLog;
@@ -170,8 +168,6 @@ import android.os.SystemProperties;
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.telephony.TelephonyManager; import android.telephony.TelephonyManager;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.ArraySet; import android.util.ArraySet;
@@ -187,9 +183,6 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats; import com.android.internal.app.IBatteryStats;
import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsLogger;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
import com.android.internal.util.ArrayUtils; import com.android.internal.util.ArrayUtils;
import com.android.internal.util.AsyncChannel; import com.android.internal.util.AsyncChannel;
import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.IndentingPrintWriter;
@@ -214,9 +207,7 @@ import com.android.server.connectivity.NetworkRanker;
import com.android.server.connectivity.PermissionMonitor; import com.android.server.connectivity.PermissionMonitor;
import com.android.server.connectivity.ProxyTracker; import com.android.server.connectivity.ProxyTracker;
import com.android.server.connectivity.QosCallbackTracker; import com.android.server.connectivity.QosCallbackTracker;
import com.android.server.connectivity.Vpn;
import com.android.server.net.BaseNetworkObserver; import com.android.server.net.BaseNetworkObserver;
import com.android.server.net.LockdownVpnTracker;
import com.android.server.net.NetworkPolicyManagerInternal; import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.utils.PriorityDump; import com.android.server.utils.PriorityDump;
@@ -309,18 +300,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
private final PerUidCounter mNetworkRequestCounter; private final PerUidCounter mNetworkRequestCounter;
private KeyStore mKeyStore;
@VisibleForTesting
@GuardedBy("mVpns")
protected final SparseArray<Vpn> mVpns = new SparseArray<>();
// TODO: investigate if mLockdownEnabled can be removed and replaced everywhere by
// a direct call to LockdownVpnTracker.isEnabled().
@GuardedBy("mVpns")
private volatile boolean mLockdownEnabled; private volatile boolean mLockdownEnabled;
@GuardedBy("mVpns")
private LockdownVpnTracker mLockdownTracker;
/** /**
* Stale copy of uid rules provided by NPMS. As long as they are accessed only in internal * Stale copy of uid rules provided by NPMS. As long as they are accessed only in internal
@@ -997,13 +977,6 @@ 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
*/ */
@@ -1113,7 +1086,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
mProxyTracker = mDeps.makeProxyTracker(mContext, mHandler); mProxyTracker = mDeps.makeProxyTracker(mContext, mHandler);
mNetd = netd; mNetd = netd;
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);
@@ -1202,43 +1174,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
mPermissionMonitor = new PermissionMonitor(mContext, mNetd); mPermissionMonitor = new PermissionMonitor(mContext, mNetd);
// Set up the listener for user state for creating user VPNs. // Listen for user add/removes to inform PermissionMonitor.
// Should run on mHandler to avoid any races. // Should run on mHandler to avoid any races.
IntentFilter intentFilter = new IntentFilter(); IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_USER_STARTED);
intentFilter.addAction(Intent.ACTION_USER_STOPPED);
intentFilter.addAction(Intent.ACTION_USER_ADDED); intentFilter.addAction(Intent.ACTION_USER_ADDED);
intentFilter.addAction(Intent.ACTION_USER_REMOVED); intentFilter.addAction(Intent.ACTION_USER_REMOVED);
intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
mUserAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */); mUserAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
mUserAllContext.registerReceiver( mUserAllContext.registerReceiver(mIntentReceiver, intentFilter,
mIntentReceiver, null /* broadcastPermission */, mHandler);
intentFilter,
null /* broadcastPermission */,
mHandler);
mContext.createContextAsUser(UserHandle.SYSTEM, 0 /* flags */).registerReceiver(
mUserPresentReceiver,
new IntentFilter(Intent.ACTION_USER_PRESENT),
null /* broadcastPermission */,
null /* scheduler */);
// Listen to package add and removal events for all users.
intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
intentFilter.addDataScheme("package");
mUserAllContext.registerReceiver(
mIntentReceiver,
intentFilter,
null /* broadcastPermission */,
mHandler);
// Listen to lockdown VPN reset.
intentFilter = new IntentFilter();
intentFilter.addAction(LockdownVpnTracker.ACTION_LOCKDOWN_RESET);
mUserAllContext.registerReceiver(
mIntentReceiver, intentFilter, NETWORK_STACK, mHandler);
mNetworkActivityTracker = new LegacyNetworkActivityTracker(mContext, mNMS); mNetworkActivityTracker = new LegacyNetworkActivityTracker(mContext, mNMS);
@@ -1416,9 +1360,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
} }
private Network[] getVpnUnderlyingNetworks(int uid) { private Network[] getVpnUnderlyingNetworks(int uid) {
synchronized (mVpns) { if (mLockdownEnabled) return null;
if (mLockdownEnabled) return null;
}
final NetworkAgentInfo nai = getVpnForUid(uid); final NetworkAgentInfo nai = getVpnForUid(uid);
if (nai != null) return nai.declaredUnderlyingNetworks; if (nai != null) return nai.declaredUnderlyingNetworks;
return null; return null;
@@ -2185,22 +2127,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
isBackgroundRestricted); isBackgroundRestricted);
} }
/**
* Require that the caller is either in the same user or has appropriate permission to interact
* across users.
*
* @param userId Target user for whatever operation the current IPC is supposed to perform.
*/
private void enforceCrossUserPermission(int userId) {
if (userId == UserHandle.getCallingUserId()) {
// Not a cross-user call.
return;
}
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
"ConnectivityService");
}
private boolean checkAnyPermissionOf(String... permissions) { private boolean checkAnyPermissionOf(String... permissions) {
for (String permission : permissions) { for (String permission : permissions) {
if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) { if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
@@ -2281,12 +2207,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, pid, uid); NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, pid, uid);
} }
private void enforceControlAlwaysOnVpnPermission() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CONTROL_ALWAYS_ON_VPN,
"ConnectivityService");
}
private void enforceNetworkStackOrSettingsPermission() { private void enforceNetworkStackOrSettingsPermission() {
enforceAnyPermissionOf( enforceAnyPermissionOf(
android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETTINGS,
@@ -2452,10 +2372,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
} }
} }
// Try bringing up tracker, but KeyStore won't be ready yet for secondary users so wait
// for user to unlock device too.
updateLockdownVpn();
// Create network requests for always-on networks. // Create network requests for always-on networks.
mHandler.sendMessage(mHandler.obtainMessage(EVENT_CONFIGURE_ALWAYS_ON_NETWORKS)); mHandler.sendMessage(mHandler.obtainMessage(EVENT_CONFIGURE_ALWAYS_ON_NETWORKS));
} }
@@ -4739,183 +4655,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
Log.e(TAG, s, t); Log.e(TAG, s, t);
} }
/**
* Prepare for a VPN application.
* VPN permissions are checked in the {@link Vpn} class. If the caller is not {@code userId},
* {@link android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
*
* @param oldPackage Package name of the application which currently controls VPN, which will
* be replaced. If there is no such application, this should should either be
* {@code null} or {@link VpnConfig.LEGACY_VPN}.
* @param newPackage Package name of the application which should gain control of VPN, or
* {@code null} to disable.
* @param userId User for whom to prepare the new VPN.
*
* @hide
*/
@Override
public boolean prepareVpn(@Nullable String oldPackage, @Nullable String newPackage,
int userId) {
enforceCrossUserPermission(userId);
synchronized (mVpns) {
throwIfLockdownEnabled();
Vpn vpn = mVpns.get(userId);
if (vpn != null) {
return vpn.prepare(oldPackage, newPackage, VpnManager.TYPE_VPN_SERVICE);
} else {
return false;
}
}
}
/**
* Set whether the VPN package has the ability to launch VPNs without user intervention. This
* method is used by system-privileged apps. VPN permissions are checked in the {@link Vpn}
* class. If the caller is not {@code userId}, {@link
* android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
*
* @param packageName The package for which authorization state should change.
* @param userId User for whom {@code packageName} is installed.
* @param authorized {@code true} if this app should be able to start a VPN connection without
* explicit user approval, {@code false} if not.
* @param vpnType The {@link VpnManager.VpnType} constant representing what class of VPN
* permissions should be granted. When unauthorizing an app, {@link
* VpnManager.TYPE_VPN_NONE} should be used.
* @hide
*/
@Override
public void setVpnPackageAuthorization(
String packageName, int userId, @VpnManager.VpnType int vpnType) {
enforceCrossUserPermission(userId);
synchronized (mVpns) {
Vpn vpn = mVpns.get(userId);
if (vpn != null) {
vpn.setPackageAuthorization(packageName, vpnType);
}
}
}
/**
* Configure a TUN interface and return its file descriptor. Parameters
* are encoded and opaque to this class. This method is used by VpnBuilder
* and not available in ConnectivityManager. Permissions are checked in
* Vpn class.
* @hide
*/
@Override
public ParcelFileDescriptor establishVpn(VpnConfig config) {
int user = UserHandle.getUserId(mDeps.getCallingUid());
synchronized (mVpns) {
throwIfLockdownEnabled();
return mVpns.get(user).establish(config);
}
}
/**
* Stores the given VPN profile based on the provisioning package name.
*
* <p>If there is already a VPN profile stored for the provisioning package, this call will
* overwrite the profile.
*
* <p>This is designed to serve the VpnManager only; settings-based VPN profiles are managed
* exclusively by the Settings app, and passed into the platform at startup time.
*
* @return {@code true} if user consent has already been granted, {@code false} otherwise.
* @hide
*/
@Override
public boolean provisionVpnProfile(@NonNull VpnProfile profile, @NonNull String packageName) {
final int user = UserHandle.getUserId(mDeps.getCallingUid());
synchronized (mVpns) {
return mVpns.get(user).provisionVpnProfile(packageName, profile, mKeyStore);
}
}
/**
* Deletes the stored VPN profile for the provisioning package
*
* <p>If there are no profiles for the given package, this method will silently succeed.
*
* <p>This is designed to serve the VpnManager only; settings-based VPN profiles are managed
* exclusively by the Settings app, and passed into the platform at startup time.
*
* @hide
*/
@Override
public void deleteVpnProfile(@NonNull String packageName) {
final int user = UserHandle.getUserId(mDeps.getCallingUid());
synchronized (mVpns) {
mVpns.get(user).deleteVpnProfile(packageName, mKeyStore);
}
}
/**
* Starts the VPN based on the stored profile for the given package
*
* <p>This is designed to serve the VpnManager only; settings-based VPN profiles are managed
* exclusively by the Settings app, and passed into the platform at startup time.
*
* @throws IllegalArgumentException if no profile was found for the given package name.
* @hide
*/
@Override
public void startVpnProfile(@NonNull String packageName) {
final int user = UserHandle.getUserId(mDeps.getCallingUid());
synchronized (mVpns) {
throwIfLockdownEnabled();
mVpns.get(user).startVpnProfile(packageName, mKeyStore);
}
}
/**
* Stops the Platform VPN if the provided package is running one.
*
* <p>This is designed to serve the VpnManager only; settings-based VPN profiles are managed
* exclusively by the Settings app, and passed into the platform at startup time.
*
* @hide
*/
@Override
public void stopVpnProfile(@NonNull String packageName) {
final int user = UserHandle.getUserId(mDeps.getCallingUid());
synchronized (mVpns) {
mVpns.get(user).stopVpnProfile(packageName);
}
}
/**
* Start legacy VPN, controlling native daemons as needed. Creates a
* secondary thread to perform connection work, returning quickly.
*/
@Override
public void startLegacyVpn(VpnProfile profile) {
int user = UserHandle.getUserId(mDeps.getCallingUid());
final LinkProperties egress = getActiveLinkProperties();
if (egress == null) {
throw new IllegalStateException("Missing active network connection");
}
synchronized (mVpns) {
throwIfLockdownEnabled();
mVpns.get(user).startLegacyVpn(profile, mKeyStore, null /* underlying */, egress);
}
}
/**
* Return the information of the ongoing legacy VPN. This method is used
* by VpnSettings and not available in ConnectivityManager. Permissions
* are checked in Vpn class.
*/
@Override
public LegacyVpnInfo getLegacyVpnInfo(int userId) {
enforceCrossUserPermission(userId);
synchronized (mVpns) {
return mVpns.get(userId).getLegacyVpnInfo();
}
}
/** /**
* Return the information of all ongoing VPNs. * Return the information of all ongoing VPNs.
* *
@@ -4925,10 +4664,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
*/ */
private UnderlyingNetworkInfo[] getAllVpnInfo() { private UnderlyingNetworkInfo[] getAllVpnInfo() {
ensureRunningOnConnectivityServiceThread(); ensureRunningOnConnectivityServiceThread();
synchronized (mVpns) { if (mLockdownEnabled) {
if (mLockdownEnabled) { return new UnderlyingNetworkInfo[0];
return new UnderlyingNetworkInfo[0];
}
} }
List<UnderlyingNetworkInfo> infoList = new ArrayList<>(); List<UnderlyingNetworkInfo> infoList = new ArrayList<>();
for (NetworkAgentInfo nai : mNetworkAgentInfos) { for (NetworkAgentInfo nai : mNetworkAgentInfos) {
@@ -4984,25 +4721,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
nai.linkProperties.getInterfaceName(), interfaces); nai.linkProperties.getInterfaceName(), interfaces);
} }
/**
* Returns the information of the ongoing VPN for {@code userId}. This method is used by
* VpnDialogs and not available in ConnectivityManager.
* Permissions are checked in Vpn class.
* @hide
*/
@Override
public VpnConfig getVpnConfig(int userId) {
enforceCrossUserPermission(userId);
synchronized (mVpns) {
Vpn vpn = mVpns.get(userId);
if (vpn != null) {
return vpn.getVpnConfig();
} else {
return null;
}
}
}
// TODO This needs to be the default network that applies to the NAI. // TODO This needs to be the default network that applies to the NAI.
private Network[] underlyingNetworksOrDefault(final int ownerUid, private Network[] underlyingNetworksOrDefault(final int ownerUid,
Network[] underlyingNetworks) { Network[] underlyingNetworks) {
@@ -5096,11 +4814,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
mHandler.post(() -> mLockdownEnabled = enabled); mHandler.post(() -> mLockdownEnabled = enabled);
} }
// TODO: remove when the VPN code moves out.
private boolean isLockdownVpnEnabled() {
return mKeyStore.contains(Credentials.LOCKDOWN_VPN);
}
private boolean isLegacyLockdownNai(NetworkAgentInfo nai) { private boolean isLegacyLockdownNai(NetworkAgentInfo nai) {
return mLockdownEnabled return mLockdownEnabled
&& getVpnType(nai) == VpnManager.TYPE_VPN_LEGACY && getVpnType(nai) == VpnManager.TYPE_VPN_LEGACY
@@ -5143,193 +4856,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
: DetailedState.CONNECTED; : DetailedState.CONNECTED;
} }
@Override
public boolean updateLockdownVpn() {
// Allow the system UID for the system server and for Settings.
// Also, for unit tests, allow the process that ConnectivityService is running in.
if (mDeps.getCallingUid() != Process.SYSTEM_UID
&& Binder.getCallingPid() != Process.myPid()) {
logw("Lockdown VPN only available to system process or AID_SYSTEM");
return false;
}
synchronized (mVpns) {
// Tear down existing lockdown if profile was removed
if (!isLockdownVpnEnabled()) {
setLockdownTracker(null);
return true;
}
byte[] profileTag = mKeyStore.get(Credentials.LOCKDOWN_VPN);
if (profileTag == null) {
loge("Lockdown VPN configured but cannot be read from keystore");
return false;
}
String profileName = new String(profileTag);
final VpnProfile profile = VpnProfile.decode(
profileName, mKeyStore.get(Credentials.VPN + profileName));
if (profile == null) {
loge("Lockdown VPN configured invalid profile " + profileName);
setLockdownTracker(null);
return true;
}
int user = UserHandle.getUserId(mDeps.getCallingUid());
Vpn vpn = mVpns.get(user);
if (vpn == null) {
logw("VPN for user " + user + " not ready yet. Skipping lockdown");
return false;
}
setLockdownTracker(
new LockdownVpnTracker(mContext, mHandler, mKeyStore, vpn, profile));
}
return true;
}
/**
* Internally set new {@link LockdownVpnTracker}, shutting down any existing
* {@link LockdownVpnTracker}. Can be {@code null} to disable lockdown.
*/
@GuardedBy("mVpns")
private void setLockdownTracker(LockdownVpnTracker tracker) {
// Shutdown any existing tracker
final LockdownVpnTracker existing = mLockdownTracker;
// TODO: Add a trigger when the always-on VPN enable/disable to reevaluate and send the
// necessary onBlockedStatusChanged callbacks.
mLockdownTracker = null;
if (existing != null) {
existing.shutdown();
}
if (tracker != null) {
mLockdownTracker = tracker;
mLockdownTracker.init();
}
}
/**
* Throws if there is any currently running, always-on Legacy VPN.
*
* <p>The LockdownVpnTracker and mLockdownEnabled both track whether an always-on Legacy VPN is
* running across the entire system. Tracking for app-based VPNs is done on a per-user,
* per-package basis in Vpn.java
*/
@GuardedBy("mVpns")
private void throwIfLockdownEnabled() {
if (mLockdownEnabled) {
throw new IllegalStateException("Unavailable in lockdown mode");
}
}
/**
* Starts the always-on VPN {@link VpnService} for user {@param userId}, which should perform
* some setup and then call {@code establish()} to connect.
*
* @return {@code true} if the service was started, the service was already connected, or there
* was no always-on VPN to start. {@code false} otherwise.
*/
private boolean startAlwaysOnVpn(int userId) {
synchronized (mVpns) {
Vpn vpn = mVpns.get(userId);
if (vpn == null) {
// Shouldn't happen as all code paths that point here should have checked the Vpn
// exists already.
Log.wtf(TAG, "User " + userId + " has no Vpn configuration");
return false;
}
return vpn.startAlwaysOnVpn(mKeyStore);
}
}
@Override
public boolean isAlwaysOnVpnPackageSupported(int userId, String packageName) {
enforceSettingsPermission();
enforceCrossUserPermission(userId);
synchronized (mVpns) {
Vpn vpn = mVpns.get(userId);
if (vpn == null) {
logw("User " + userId + " has no Vpn configuration");
return false;
}
return vpn.isAlwaysOnPackageSupported(packageName, mKeyStore);
}
}
@Override
public boolean setAlwaysOnVpnPackage(
int userId, String packageName, boolean lockdown, List<String> lockdownWhitelist) {
enforceControlAlwaysOnVpnPermission();
enforceCrossUserPermission(userId);
synchronized (mVpns) {
// Can't set always-on VPN if legacy VPN is already in lockdown mode.
if (isLockdownVpnEnabled()) {
return false;
}
Vpn vpn = mVpns.get(userId);
if (vpn == null) {
logw("User " + userId + " has no Vpn configuration");
return false;
}
if (!vpn.setAlwaysOnPackage(packageName, lockdown, lockdownWhitelist, mKeyStore)) {
return false;
}
if (!startAlwaysOnVpn(userId)) {
vpn.setAlwaysOnPackage(null, false, null, mKeyStore);
return false;
}
}
return true;
}
@Override
public String getAlwaysOnVpnPackage(int userId) {
enforceControlAlwaysOnVpnPermission();
enforceCrossUserPermission(userId);
synchronized (mVpns) {
Vpn vpn = mVpns.get(userId);
if (vpn == null) {
logw("User " + userId + " has no Vpn configuration");
return null;
}
return vpn.getAlwaysOnPackage();
}
}
@Override
public boolean isVpnLockdownEnabled(int userId) {
enforceControlAlwaysOnVpnPermission();
enforceCrossUserPermission(userId);
synchronized (mVpns) {
Vpn vpn = mVpns.get(userId);
if (vpn == null) {
logw("User " + userId + " has no Vpn configuration");
return false;
}
return vpn.getLockdown();
}
}
@Override
public List<String> getVpnLockdownWhitelist(int userId) {
enforceControlAlwaysOnVpnPermission();
enforceCrossUserPermission(userId);
synchronized (mVpns) {
Vpn vpn = mVpns.get(userId);
if (vpn == null) {
logw("User " + userId + " has no Vpn configuration");
return null;
}
return vpn.getLockdownAllowlist();
}
}
@Override @Override
public void setProvisioningNotificationVisible(boolean visible, int networkType, public void setProvisioningNotificationVisible(boolean visible, int networkType,
String action) { String action) {
@@ -5362,165 +4888,33 @@ public class ConnectivityService extends IConnectivityManager.Stub
} }
} }
private void onUserStarted(int userId) {
synchronized (mVpns) {
Vpn userVpn = mVpns.get(userId);
if (userVpn != null) {
loge("Starting user already has a VPN");
return;
}
userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, mNetd, userId, mKeyStore);
mVpns.put(userId, userVpn);
if (mUserManager.getUserInfo(userId).isPrimary() && isLockdownVpnEnabled()) {
updateLockdownVpn();
}
}
}
private void onUserStopped(int userId) {
synchronized (mVpns) {
Vpn userVpn = mVpns.get(userId);
if (userVpn == null) {
loge("Stopped user has no VPN");
return;
}
userVpn.onUserStopped();
mVpns.delete(userId);
}
}
private void onUserAdded(int userId) { private void onUserAdded(int userId) {
mPermissionMonitor.onUserAdded(userId); mPermissionMonitor.onUserAdded(userId);
synchronized (mVpns) {
final int vpnsSize = mVpns.size();
for (int i = 0; i < vpnsSize; i++) {
Vpn vpn = mVpns.valueAt(i);
vpn.onUserAdded(userId);
}
}
} }
private void onUserRemoved(int userId) { private void onUserRemoved(int userId) {
mPermissionMonitor.onUserRemoved(userId); mPermissionMonitor.onUserRemoved(userId);
synchronized (mVpns) {
final int vpnsSize = mVpns.size();
for (int i = 0; i < vpnsSize; i++) {
Vpn vpn = mVpns.valueAt(i);
vpn.onUserRemoved(userId);
}
}
}
private void onPackageReplaced(String packageName, int uid) {
if (TextUtils.isEmpty(packageName) || uid < 0) {
Log.wtf(TAG, "Invalid package in onPackageReplaced: " + packageName + " | " + uid);
return;
}
final int userId = UserHandle.getUserId(uid);
synchronized (mVpns) {
final Vpn vpn = mVpns.get(userId);
if (vpn == null) {
return;
}
// Legacy always-on VPN won't be affected since the package name is not set.
if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName)) {
log("Restarting always-on VPN package " + packageName + " for user "
+ userId);
vpn.startAlwaysOnVpn(mKeyStore);
}
}
}
private void onPackageRemoved(String packageName, int uid, boolean isReplacing) {
if (TextUtils.isEmpty(packageName) || uid < 0) {
Log.wtf(TAG, "Invalid package in onPackageRemoved: " + packageName + " | " + uid);
return;
}
final int userId = UserHandle.getUserId(uid);
synchronized (mVpns) {
final Vpn vpn = mVpns.get(userId);
if (vpn == null) {
return;
}
// Legacy always-on VPN won't be affected since the package name is not set.
if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName) && !isReplacing) {
log("Removing always-on VPN package " + packageName + " for user "
+ userId);
vpn.setAlwaysOnPackage(null, false, null, mKeyStore);
}
}
}
private void onUserUnlocked(int userId) {
synchronized (mVpns) {
// User present may be sent because of an unlock, which might mean an unlocked keystore.
if (mUserManager.getUserInfo(userId).isPrimary() && isLockdownVpnEnabled()) {
updateLockdownVpn();
} else {
startAlwaysOnVpn(userId);
}
}
}
private void onVpnLockdownReset() {
synchronized (mVpns) {
if (mLockdownTracker != null) mLockdownTracker.reset();
}
} }
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
ensureRunningOnConnectivityServiceThread();
final String action = intent.getAction(); final String action = intent.getAction();
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
final Uri packageData = intent.getData();
final String packageName =
packageData != null ? packageData.getSchemeSpecificPart() : null;
if (LockdownVpnTracker.ACTION_LOCKDOWN_RESET.equals(action)) {
onVpnLockdownReset();
}
// UserId should be filled for below intents, check the existence. // UserId should be filled for below intents, check the existence.
if (userId == UserHandle.USER_NULL) return; if (userId == UserHandle.USER_NULL) return;
if (Intent.ACTION_USER_STARTED.equals(action)) { if (Intent.ACTION_USER_ADDED.equals(action)) {
onUserStarted(userId);
} else if (Intent.ACTION_USER_STOPPED.equals(action)) {
onUserStopped(userId);
} else if (Intent.ACTION_USER_ADDED.equals(action)) {
onUserAdded(userId); onUserAdded(userId);
} else if (Intent.ACTION_USER_REMOVED.equals(action)) { } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
onUserRemoved(userId); onUserRemoved(userId);
} else if (Intent.ACTION_USER_UNLOCKED.equals(action)) { } else {
onUserUnlocked(userId);
} else if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
onPackageReplaced(packageName, uid);
} else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
final boolean isReplacing = intent.getBooleanExtra(
Intent.EXTRA_REPLACING, false);
onPackageRemoved(packageName, uid, isReplacing);
} else {
Log.wtf(TAG, "received unexpected intent: " + action); Log.wtf(TAG, "received unexpected intent: " + action);
} }
} }
}; };
private BroadcastReceiver mUserPresentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// Try creating lockdown tracker, since user present usually means
// unlocked keystore.
updateLockdownVpn();
// Use the same context that registered receiver before to unregister it. Because use
// different context to unregister receiver will cause exception.
context.unregisterReceiver(this);
}
};
private final HashMap<Messenger, NetworkProviderInfo> mNetworkProviderInfos = new HashMap<>(); private final HashMap<Messenger, NetworkProviderInfo> mNetworkProviderInfos = new HashMap<>();
private final HashMap<NetworkRequest, NetworkRequestInfo> mNetworkRequests = new HashMap<>(); private final HashMap<NetworkRequest, NetworkRequestInfo> mNetworkRequests = new HashMap<>();
@@ -8260,34 +7654,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
} }
} }
@Override
public boolean addVpnAddress(String address, int prefixLength) {
int user = UserHandle.getUserId(mDeps.getCallingUid());
synchronized (mVpns) {
throwIfLockdownEnabled();
return mVpns.get(user).addAddress(address, prefixLength);
}
}
@Override
public boolean removeVpnAddress(String address, int prefixLength) {
int user = UserHandle.getUserId(mDeps.getCallingUid());
synchronized (mVpns) {
throwIfLockdownEnabled();
return mVpns.get(user).removeAddress(address, prefixLength);
}
}
@Override
public boolean setUnderlyingNetworksForVpn(Network[] networks) {
int user = UserHandle.getUserId(mDeps.getCallingUid());
final boolean success;
synchronized (mVpns) {
success = mVpns.get(user).setUnderlyingNetworks(networks);
}
return success;
}
@Override @Override
public String getCaptivePortalServerUrl() { public String getCaptivePortalServerUrl() {
enforceNetworkStackOrSettingsPermission(); enforceNetworkStackOrSettingsPermission();
@@ -8367,8 +7733,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
return; return;
} }
final int userId = UserHandle.getCallingUserId();
final long token = Binder.clearCallingIdentity(); final long token = Binder.clearCallingIdentity();
try { try {
final IpMemoryStore ipMemoryStore = IpMemoryStore.getMemoryStore(mContext); final IpMemoryStore ipMemoryStore = IpMemoryStore.getMemoryStore(mContext);
@@ -8380,44 +7744,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
// Turn airplane mode off // Turn airplane mode off
setAirplaneMode(false); setAirplaneMode(false);
if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) {
// Remove always-on package
synchronized (mVpns) {
final String alwaysOnPackage = getAlwaysOnVpnPackage(userId);
if (alwaysOnPackage != null) {
setAlwaysOnVpnPackage(userId, null, false, null);
setVpnPackageAuthorization(alwaysOnPackage, userId, VpnManager.TYPE_VPN_NONE);
}
// Turn Always-on VPN off
if (mLockdownEnabled && userId == UserHandle.USER_SYSTEM) {
final long ident = Binder.clearCallingIdentity();
try {
mKeyStore.delete(Credentials.LOCKDOWN_VPN);
mLockdownEnabled = false;
setLockdownTracker(null);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
// Turn VPN off
VpnConfig vpnConfig = getVpnConfig(userId);
if (vpnConfig != null) {
if (vpnConfig.legacy) {
prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, userId);
} else {
// Prevent this app (packagename = vpnConfig.user) from initiating
// VPN connections in the future without user intervention.
setVpnPackageAuthorization(
vpnConfig.user, userId, VpnManager.TYPE_VPN_NONE);
prepareVpn(null, VpnConfig.LEGACY_VPN, userId);
}
}
}
}
// restore private DNS settings to default mode (opportunistic) // restore private DNS settings to default mode (opportunistic)
if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_PRIVATE_DNS)) { if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_PRIVATE_DNS)) {
Settings.Global.putString(mContext.getContentResolver(), Settings.Global.putString(mContext.getContentResolver(),
@@ -8509,25 +7835,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
} }
} }
@GuardedBy("mVpns")
private Vpn getVpnIfOwner() {
return getVpnIfOwner(mDeps.getCallingUid());
}
// TODO: stop calling into Vpn.java and get this information from data in this class.
@GuardedBy("mVpns")
private Vpn getVpnIfOwner(int uid) {
final int user = UserHandle.getUserId(uid);
final Vpn vpn = mVpns.get(user);
if (vpn == null) {
return null;
} else {
final UnderlyingNetworkInfo info = vpn.getUnderlyingNetworkInfo();
return (info == null || info.ownerUid != uid) ? null : vpn;
}
}
private @VpnManager.VpnType int getVpnType(@Nullable NetworkAgentInfo vpn) { private @VpnManager.VpnType int getVpnType(@Nullable NetworkAgentInfo vpn) {
if (vpn == null) return VpnManager.TYPE_VPN_NONE; if (vpn == null) return VpnManager.TYPE_VPN_NONE;
final TransportInfo ti = vpn.networkCapabilities.getTransportInfo(); final TransportInfo ti = vpn.networkCapabilities.getTransportInfo();
@@ -8564,22 +7871,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
return uid; return uid;
} }
@Override
public boolean isCallerCurrentAlwaysOnVpnApp() {
synchronized (mVpns) {
Vpn vpn = getVpnIfOwner();
return vpn != null && vpn.getAlwaysOn();
}
}
@Override
public boolean isCallerCurrentAlwaysOnVpnLockdownApp() {
synchronized (mVpns) {
Vpn vpn = getVpnIfOwner();
return vpn != null && vpn.getLockdown();
}
}
/** /**
* Returns a IBinder to a TestNetworkService. Will be lazily created as needed. * Returns a IBinder to a TestNetworkService. Will be lazily created as needed.
* *

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

@@ -370,6 +370,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;
@@ -384,6 +385,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;
@@ -1256,15 +1258,29 @@ 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) { private void assertVpnTransportInfo(NetworkCapabilities nc, int type) {
@@ -1278,11 +1294,21 @@ public class ConnectivityServiceTest {
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,
@@ -1406,6 +1432,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,
@@ -1428,6 +1455,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);
@@ -1455,7 +1484,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));
@@ -6766,8 +6794,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.
@@ -6797,7 +6825,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();
} }
@@ -7173,7 +7202,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);
@@ -7195,7 +7225,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();
@@ -7208,7 +7238,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();
@@ -7241,11 +7272,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);
@@ -7258,7 +7290,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();
@@ -7270,7 +7302,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();
@@ -7281,7 +7314,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();
@@ -7293,7 +7326,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();