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:
@@ -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(
|
||||
Context.CONNECTIVITY_DIAGNOSTICS_SERVICE,
|
||||
ConnectivityDiagnosticsManager.class,
|
||||
|
||||
@@ -824,6 +824,7 @@ public class ConnectivityManager {
|
||||
|
||||
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
|
||||
private final IConnectivityManager mService;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@@ -1069,106 +1070,55 @@ public class ConnectivityManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Calls VpnManager#isAlwaysOnVpnPackageSupportedForUser.
|
||||
* @deprecated TODO: remove when callers have migrated to VpnManager.
|
||||
* @hide
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean isAlwaysOnVpnPackageSupportedForUser(int userId, @Nullable String vpnPackage) {
|
||||
try {
|
||||
return mService.isAlwaysOnVpnPackageSupported(userId, vpnPackage);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
return getVpnManager().isAlwaysOnVpnPackageSupportedForUser(userId, vpnPackage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Calls VpnManager#setAlwaysOnVpnPackageForUser.
|
||||
* @deprecated TODO: remove when callers have migrated to VpnManager.
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
|
||||
@Deprecated
|
||||
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();
|
||||
}
|
||||
return getVpnManager().setAlwaysOnVpnPackageForUser(userId, vpnPackage, lockdownEnabled,
|
||||
lockdownAllowlist);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
/**
|
||||
* Calls VpnManager#getAlwaysOnVpnPackageForUser.
|
||||
* @deprecated TODO: remove when callers have migrated to VpnManager.
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
|
||||
@Deprecated
|
||||
public String getAlwaysOnVpnPackageForUser(int userId) {
|
||||
try {
|
||||
return mService.getAlwaysOnVpnPackage(userId);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
return getVpnManager().getAlwaysOnVpnPackageForUser(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether always-on VPN is in lockdown mode.
|
||||
*
|
||||
* Calls VpnManager#isVpnLockdownEnabled.
|
||||
* @deprecated TODO: remove when callers have migrated to VpnManager.
|
||||
* @hide
|
||||
**/
|
||||
@RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean isVpnLockdownEnabled(int userId) {
|
||||
try {
|
||||
return mService.isVpnLockdownEnabled(userId);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
|
||||
return getVpnManager().isVpnLockdownEnabled(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*
|
||||
* Calls VpnManager#getVpnLockdownAllowlist.
|
||||
* @deprecated TODO: remove when callers have migrated to VpnManager.
|
||||
* @hide
|
||||
**/
|
||||
@RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
|
||||
*/
|
||||
@Deprecated
|
||||
public List<String> getVpnLockdownWhitelist(int userId) {
|
||||
try {
|
||||
return mService.getVpnLockdownWhitelist(userId);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
return getVpnManager().getVpnLockdownAllowlist(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3219,20 +3169,13 @@ public class ConnectivityManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* If the LockdownVpn mechanism is enabled, updates the vpn
|
||||
* with a reload of its profile.
|
||||
*
|
||||
* @return a boolean with {@code} indicating success
|
||||
*
|
||||
* <p>This method can only be called by the system UID
|
||||
* {@hide}
|
||||
* Calls VpnManager#updateLockdownVpn.
|
||||
* @deprecated TODO: remove when callers have migrated to VpnManager.
|
||||
* @hide
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean updateLockdownVpn() {
|
||||
try {
|
||||
return mService.updateLockdownVpn();
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
return getVpnManager().updateLockdownVpn();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4596,6 +4539,8 @@ public class ConnectivityManager {
|
||||
try {
|
||||
mService.factoryReset();
|
||||
mTetheringManager.stopAllTethering();
|
||||
// TODO: Migrate callers to VpnManager#factoryReset.
|
||||
getVpnManager().factoryReset();
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
@@ -4889,9 +4834,13 @@ public class ConnectivityManager {
|
||||
return new TestNetworkManager(ITestNetworkManager.Stub.asInterface(tnBinder));
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public VpnManager createVpnManager() {
|
||||
return new VpnManager(mContext, mService);
|
||||
/**
|
||||
* Temporary hack to shim calls from ConnectivityManager to VpnManager. We cannot store a
|
||||
* private final mVpnManager because ConnectivityManager is initialized before VpnManager.
|
||||
* @hide TODO: remove.
|
||||
*/
|
||||
public VpnManager getVpnManager() {
|
||||
return mContext.getSystemService(VpnManager.class);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
|
||||
@@ -42,9 +42,6 @@ import android.os.PersistableBundle;
|
||||
import android.os.ResultReceiver;
|
||||
|
||||
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
|
||||
@@ -122,34 +119,6 @@ interface IConnectivityManager
|
||||
|
||||
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 setLegacyLockdownVpnEnabled(boolean enabled);
|
||||
|
||||
@@ -200,10 +169,6 @@ interface IConnectivityManager
|
||||
|
||||
int getRestoreDefaultNetworkDelay(int networkType);
|
||||
|
||||
boolean addVpnAddress(String address, int prefixLength);
|
||||
boolean removeVpnAddress(String address, int prefixLength);
|
||||
boolean setUnderlyingNetworksForVpn(in Network[] networks);
|
||||
|
||||
void factoryReset();
|
||||
|
||||
void startNattKeepalive(in Network network, int intervalSeconds,
|
||||
@@ -223,8 +188,6 @@ interface IConnectivityManager
|
||||
byte[] getNetworkWatchlistConfigHash();
|
||||
|
||||
int getConnectionOwnerUid(in ConnectionInfo connectionInfo);
|
||||
boolean isCallerCurrentAlwaysOnVpnApp();
|
||||
boolean isCallerCurrentAlwaysOnVpnLockdownApp();
|
||||
|
||||
void registerConnectivityDiagnosticsCallback(in IConnectivityDiagnosticsCallback callback,
|
||||
in NetworkRequest request, String callingPackageName);
|
||||
|
||||
@@ -21,6 +21,7 @@ import static com.android.internal.util.Preconditions.checkNotNull;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.Activity;
|
||||
import android.content.ComponentName;
|
||||
@@ -37,6 +38,7 @@ import java.io.IOException;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This class provides an interface for apps to manage platform VPN profiles
|
||||
@@ -88,7 +90,7 @@ public class VpnManager {
|
||||
public @interface VpnType {}
|
||||
|
||||
@NonNull private final Context mContext;
|
||||
@NonNull private final IConnectivityManager mService;
|
||||
@NonNull private final IVpnManager mService;
|
||||
|
||||
private static Intent getIntentForConfirmation() {
|
||||
final Intent intent = new Intent();
|
||||
@@ -107,9 +109,9 @@ public class VpnManager {
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public VpnManager(@NonNull Context ctx, @NonNull IConnectivityManager service) {
|
||||
public VpnManager(@NonNull Context ctx, @NonNull IVpnManager service) {
|
||||
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.
|
||||
* 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.
|
||||
* @hide
|
||||
|
||||
@@ -170,12 +170,11 @@ public class VpnService extends Service {
|
||||
"android.net.VpnService.SUPPORTS_ALWAYS_ON";
|
||||
|
||||
/**
|
||||
* Use IConnectivityManager since those methods are hidden and not
|
||||
* available in ConnectivityManager.
|
||||
* Use IVpnManager since those methods are hidden and not available in VpnManager.
|
||||
*/
|
||||
private static IConnectivityManager getService() {
|
||||
return IConnectivityManager.Stub.asInterface(
|
||||
ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
|
||||
private static IVpnManager getService() {
|
||||
return IVpnManager.Stub.asInterface(
|
||||
ServiceManager.getService(Context.VPN_MANAGEMENT_SERVICE));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,15 +225,15 @@ public class VpnService extends Service {
|
||||
@SystemApi
|
||||
@RequiresPermission(android.Manifest.permission.CONTROL_VPN)
|
||||
public static void prepareAndAuthorize(Context context) {
|
||||
IConnectivityManager cm = getService();
|
||||
IVpnManager vm = getService();
|
||||
String packageName = context.getPackageName();
|
||||
try {
|
||||
// Only prepare if we're not already prepared.
|
||||
int userId = context.getUserId();
|
||||
if (!cm.prepareVpn(packageName, null, userId)) {
|
||||
cm.prepareVpn(null, packageName, userId);
|
||||
if (!vm.prepareVpn(packageName, null, 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) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.android.server;
|
||||
|
||||
import static android.Manifest.permission.NETWORK_STACK;
|
||||
import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
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.Uri;
|
||||
import android.net.VpnManager;
|
||||
import android.net.VpnService;
|
||||
import android.net.VpnTransportInfo;
|
||||
import android.net.metrics.INetdEventListener;
|
||||
import android.net.metrics.IpConnectivityLog;
|
||||
@@ -170,8 +168,6 @@ import android.os.SystemProperties;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
import android.security.Credentials;
|
||||
import android.security.KeyStore;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArraySet;
|
||||
@@ -187,9 +183,6 @@ import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.app.IBatteryStats;
|
||||
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.AsyncChannel;
|
||||
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.ProxyTracker;
|
||||
import com.android.server.connectivity.QosCallbackTracker;
|
||||
import com.android.server.connectivity.Vpn;
|
||||
import com.android.server.net.BaseNetworkObserver;
|
||||
import com.android.server.net.LockdownVpnTracker;
|
||||
import com.android.server.net.NetworkPolicyManagerInternal;
|
||||
import com.android.server.utils.PriorityDump;
|
||||
|
||||
@@ -309,18 +300,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
|
||||
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;
|
||||
@GuardedBy("mVpns")
|
||||
private LockdownVpnTracker mLockdownTracker;
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a reference to the system keystore.
|
||||
*/
|
||||
public KeyStore getKeyStore() {
|
||||
return KeyStore.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ProxyTracker
|
||||
*/
|
||||
@@ -1113,7 +1086,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
mProxyTracker = mDeps.makeProxyTracker(mContext, mHandler);
|
||||
|
||||
mNetd = netd;
|
||||
mKeyStore = mDeps.getKeyStore();
|
||||
mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
|
||||
mLocationPermissionChecker = new LocationPermissionChecker(mContext);
|
||||
@@ -1202,43 +1174,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
|
||||
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.
|
||||
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_REMOVED);
|
||||
intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
|
||||
|
||||
mUserAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
|
||||
mUserAllContext.registerReceiver(
|
||||
mIntentReceiver,
|
||||
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);
|
||||
mUserAllContext.registerReceiver(mIntentReceiver, intentFilter,
|
||||
null /* broadcastPermission */, mHandler);
|
||||
|
||||
mNetworkActivityTracker = new LegacyNetworkActivityTracker(mContext, mNMS);
|
||||
|
||||
@@ -1416,9 +1360,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
}
|
||||
|
||||
private Network[] getVpnUnderlyingNetworks(int uid) {
|
||||
synchronized (mVpns) {
|
||||
if (mLockdownEnabled) return null;
|
||||
}
|
||||
if (mLockdownEnabled) return null;
|
||||
final NetworkAgentInfo nai = getVpnForUid(uid);
|
||||
if (nai != null) return nai.declaredUnderlyingNetworks;
|
||||
return null;
|
||||
@@ -2185,22 +2127,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
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) {
|
||||
for (String permission : permissions) {
|
||||
if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
|
||||
@@ -2281,12 +2207,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, pid, uid);
|
||||
}
|
||||
|
||||
private void enforceControlAlwaysOnVpnPermission() {
|
||||
mContext.enforceCallingOrSelfPermission(
|
||||
android.Manifest.permission.CONTROL_ALWAYS_ON_VPN,
|
||||
"ConnectivityService");
|
||||
}
|
||||
|
||||
private void enforceNetworkStackOrSettingsPermission() {
|
||||
enforceAnyPermissionOf(
|
||||
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.
|
||||
mHandler.sendMessage(mHandler.obtainMessage(EVENT_CONFIGURE_ALWAYS_ON_NETWORKS));
|
||||
}
|
||||
@@ -4739,183 +4655,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
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.
|
||||
*
|
||||
@@ -4925,10 +4664,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
*/
|
||||
private UnderlyingNetworkInfo[] getAllVpnInfo() {
|
||||
ensureRunningOnConnectivityServiceThread();
|
||||
synchronized (mVpns) {
|
||||
if (mLockdownEnabled) {
|
||||
return new UnderlyingNetworkInfo[0];
|
||||
}
|
||||
if (mLockdownEnabled) {
|
||||
return new UnderlyingNetworkInfo[0];
|
||||
}
|
||||
List<UnderlyingNetworkInfo> infoList = new ArrayList<>();
|
||||
for (NetworkAgentInfo nai : mNetworkAgentInfos) {
|
||||
@@ -4984,25 +4721,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
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.
|
||||
private Network[] underlyingNetworksOrDefault(final int ownerUid,
|
||||
Network[] underlyingNetworks) {
|
||||
@@ -5096,11 +4814,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
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) {
|
||||
return mLockdownEnabled
|
||||
&& getVpnType(nai) == VpnManager.TYPE_VPN_LEGACY
|
||||
@@ -5143,193 +4856,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
: 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
|
||||
public void setProvisioningNotificationVisible(boolean visible, int networkType,
|
||||
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) {
|
||||
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) {
|
||||
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() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
ensureRunningOnConnectivityServiceThread();
|
||||
final String action = intent.getAction();
|
||||
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
|
||||
final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
|
||||
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.
|
||||
if (userId == UserHandle.USER_NULL) return;
|
||||
|
||||
if (Intent.ACTION_USER_STARTED.equals(action)) {
|
||||
onUserStarted(userId);
|
||||
} else if (Intent.ACTION_USER_STOPPED.equals(action)) {
|
||||
onUserStopped(userId);
|
||||
} else if (Intent.ACTION_USER_ADDED.equals(action)) {
|
||||
if (Intent.ACTION_USER_ADDED.equals(action)) {
|
||||
onUserAdded(userId);
|
||||
} else if (Intent.ACTION_USER_REMOVED.equals(action)) {
|
||||
onUserRemoved(userId);
|
||||
} else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
|
||||
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 {
|
||||
} else {
|
||||
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<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
|
||||
public String getCaptivePortalServerUrl() {
|
||||
enforceNetworkStackOrSettingsPermission();
|
||||
@@ -8367,8 +7733,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
return;
|
||||
}
|
||||
|
||||
final int userId = UserHandle.getCallingUserId();
|
||||
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
final IpMemoryStore ipMemoryStore = IpMemoryStore.getMemoryStore(mContext);
|
||||
@@ -8380,44 +7744,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
// Turn airplane mode off
|
||||
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)
|
||||
if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_PRIVATE_DNS)) {
|
||||
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) {
|
||||
if (vpn == null) return VpnManager.TYPE_VPN_NONE;
|
||||
final TransportInfo ti = vpn.networkCapabilities.getTransportInfo();
|
||||
@@ -8564,22 +7871,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -49,7 +49,7 @@ public class VpnManagerTest {
|
||||
private static final String IDENTITY_STRING = "Identity";
|
||||
private static final byte[] PSK_BYTES = "preSharedKey".getBytes();
|
||||
|
||||
private IConnectivityManager mMockCs;
|
||||
private IVpnManager mMockService;
|
||||
private VpnManager mVpnManager;
|
||||
private final MockContext mMockContext =
|
||||
new MockContext() {
|
||||
@@ -61,24 +61,26 @@ public class VpnManagerTest {
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mMockCs = mock(IConnectivityManager.class);
|
||||
mVpnManager = new VpnManager(mMockContext, mMockCs);
|
||||
mMockService = mock(IVpnManager.class);
|
||||
mVpnManager = new VpnManager(mMockContext, mMockService);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProvisionVpnProfilePreconsented() throws Exception {
|
||||
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.
|
||||
assertNull(mVpnManager.provisionVpnProfile(profile));
|
||||
verify(mMockCs).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME));
|
||||
verify(mMockService).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProvisionVpnProfileNeedsConsent() throws Exception {
|
||||
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.
|
||||
final Intent intent = mVpnManager.provisionVpnProfile(profile);
|
||||
@@ -88,25 +90,25 @@ public class VpnManagerTest {
|
||||
ComponentName.unflattenFromString(
|
||||
"com.android.vpndialogs/com.android.vpndialogs.PlatformVpnConfirmDialog");
|
||||
assertEquals(expectedComponentName, intent.getComponent());
|
||||
verify(mMockCs).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME));
|
||||
verify(mMockService).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteProvisionedVpnProfile() throws Exception {
|
||||
mVpnManager.deleteProvisionedVpnProfile();
|
||||
verify(mMockCs).deleteVpnProfile(eq(PKG_NAME));
|
||||
verify(mMockService).deleteVpnProfile(eq(PKG_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartProvisionedVpnProfile() throws Exception {
|
||||
mVpnManager.startProvisionedVpnProfile();
|
||||
verify(mMockCs).startVpnProfile(eq(PKG_NAME));
|
||||
verify(mMockService).startVpnProfile(eq(PKG_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStopProvisionedVpnProfile() throws Exception {
|
||||
mVpnManager.stopProvisionedVpnProfile();
|
||||
verify(mMockCs).stopVpnProfile(eq(PKG_NAME));
|
||||
verify(mMockService).stopVpnProfile(eq(PKG_NAME));
|
||||
}
|
||||
|
||||
private Ikev2VpnProfile getPlatformVpnProfile() throws Exception {
|
||||
|
||||
@@ -370,6 +370,7 @@ public class ConnectivityServiceTest {
|
||||
|
||||
private MockContext mServiceContext;
|
||||
private HandlerThread mCsHandlerThread;
|
||||
private HandlerThread mVMSHandlerThread;
|
||||
private ConnectivityService.Dependencies mDeps;
|
||||
private ConnectivityService mService;
|
||||
private WrappedConnectivityManager mCm;
|
||||
@@ -384,6 +385,7 @@ public class ConnectivityServiceTest {
|
||||
private TestNetIdManager mNetIdManager;
|
||||
private QosCallbackMockHelper mQosCallbackMockHelper;
|
||||
private QosCallbackTracker mQosCallbackTracker;
|
||||
private VpnManagerService mVpnManagerService;
|
||||
|
||||
// State variables required to emulate NetworkPolicyManagerService behaviour.
|
||||
private int mUidRules = RULE_NONE;
|
||||
@@ -1256,15 +1258,29 @@ public class ConnectivityServiceTest {
|
||||
r -> new UidRangeParcel(r.start, r.stop)).toArray(UidRangeParcel[]::new);
|
||||
}
|
||||
|
||||
private void mockVpn(int uid) {
|
||||
synchronized (mService.mVpns) {
|
||||
int userId = UserHandle.getUserId(uid);
|
||||
mMockVpn = new MockVpn(userId);
|
||||
// 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.
|
||||
mService.mVpns.put(userId, mMockVpn);
|
||||
}
|
||||
private VpnManagerService makeVpnManagerService() {
|
||||
final VpnManagerService.Dependencies deps = new VpnManagerService.Dependencies() {
|
||||
public int getCallingUid() {
|
||||
return mDeps.getCallingUid();
|
||||
}
|
||||
|
||||
public HandlerThread makeHandlerThread() {
|
||||
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) {
|
||||
@@ -1278,11 +1294,21 @@ public class ConnectivityServiceTest {
|
||||
|
||||
private void processBroadcastForVpn(Intent intent) {
|
||||
// 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));
|
||||
HandlerUtils.waitForIdle(handler, TIMEOUT_MS);
|
||||
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() {
|
||||
doAnswer(i -> mContext.getSystemService(NetworkPolicyManager.class)
|
||||
.checkUidNetworkingBlocked(i.getArgument(0) /* uid */, mUidRules,
|
||||
@@ -1406,6 +1432,7 @@ public class ConnectivityServiceTest {
|
||||
initAlarmManager(mAlarmManager, mAlarmManagerThread.getThreadHandler());
|
||||
|
||||
mCsHandlerThread = new HandlerThread("TestConnectivityService");
|
||||
mVMSHandlerThread = new HandlerThread("TestVpnManagerService");
|
||||
mDeps = makeDependencies();
|
||||
returnRealCallingUid();
|
||||
mService = new ConnectivityService(mServiceContext,
|
||||
@@ -1428,6 +1455,8 @@ public class ConnectivityServiceTest {
|
||||
// getSystemService() correctly.
|
||||
mCm = new WrappedConnectivityManager(InstrumentationRegistry.getContext(), mService);
|
||||
mService.systemReadyInternal();
|
||||
mVpnManagerService = makeVpnManagerService();
|
||||
mVpnManagerService.systemReady();
|
||||
mockVpn(Process.myUid());
|
||||
mCm.bindProcessToNetwork(null);
|
||||
mQosCallbackTracker = mock(QosCallbackTracker.class);
|
||||
@@ -1455,7 +1484,6 @@ public class ConnectivityServiceTest {
|
||||
doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any());
|
||||
doReturn(true).when(deps).queryUserAccess(anyInt(), anyInt());
|
||||
doReturn(mBatteryStatsService).when(deps).getBatteryStatsService();
|
||||
doReturn(mKeyStore).when(deps).getKeyStore();
|
||||
doAnswer(inv -> {
|
||||
mPolicyTracker = new WrappedMultinetworkPolicyTracker(
|
||||
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.
|
||||
final ArrayList<String> allowList = new ArrayList<>();
|
||||
mService.setAlwaysOnVpnPackage(PRIMARY_USER, ALWAYS_ON_PACKAGE, true /* lockdown */,
|
||||
allowList);
|
||||
mVpnManagerService.setAlwaysOnVpnPackage(PRIMARY_USER, ALWAYS_ON_PACKAGE,
|
||||
true /* lockdown */, allowList);
|
||||
waitForIdle();
|
||||
assertNull(mCm.getActiveNetworkForUid(uid));
|
||||
// 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));
|
||||
assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
|
||||
|
||||
mService.setAlwaysOnVpnPackage(PRIMARY_USER, null, false /* lockdown */, allowList);
|
||||
mVpnManagerService.setAlwaysOnVpnPackage(PRIMARY_USER, null, false /* lockdown */,
|
||||
allowList);
|
||||
waitForIdle();
|
||||
}
|
||||
|
||||
@@ -7173,7 +7202,8 @@ public class ConnectivityServiceTest {
|
||||
final int uid = Process.myUid();
|
||||
final int userId = UserHandle.getUserId(uid);
|
||||
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();
|
||||
|
||||
UidRangeParcel firstHalf = new UidRangeParcel(1, VPN_UID - 1);
|
||||
@@ -7195,7 +7225,7 @@ public class ConnectivityServiceTest {
|
||||
assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
|
||||
|
||||
// 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);
|
||||
defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
|
||||
vpnUidCallback.assertNoCallback();
|
||||
@@ -7208,7 +7238,8 @@ public class ConnectivityServiceTest {
|
||||
|
||||
// Add our UID to the allowlist and re-enable lockdown, expect network is not blocked.
|
||||
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();
|
||||
defaultCallback.assertNoCallback();
|
||||
vpnUidCallback.assertNoCallback();
|
||||
@@ -7241,11 +7272,12 @@ public class ConnectivityServiceTest {
|
||||
|
||||
// Disable lockdown, remove our UID from the allowlist, and re-enable lockdown.
|
||||
// Everything should now be blocked.
|
||||
mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
|
||||
mVpnManagerService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
|
||||
waitForIdle();
|
||||
expectNetworkRejectNonSecureVpn(inOrder, false, piece1, piece2, piece3);
|
||||
allowList.clear();
|
||||
mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
|
||||
mVpnManagerService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */,
|
||||
allowList);
|
||||
waitForIdle();
|
||||
expectNetworkRejectNonSecureVpn(inOrder, true, firstHalf, secondHalf);
|
||||
defaultCallback.expectBlockedStatusCallback(true, mWiFiNetworkAgent);
|
||||
@@ -7258,7 +7290,7 @@ public class ConnectivityServiceTest {
|
||||
assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
|
||||
|
||||
// Disable lockdown. Everything is unblocked.
|
||||
mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
|
||||
mVpnManagerService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
|
||||
defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
|
||||
assertBlockedCallbackInAnyOrder(callback, false, mWiFiNetworkAgent, mCellNetworkAgent);
|
||||
vpnUidCallback.assertNoCallback();
|
||||
@@ -7270,7 +7302,8 @@ public class ConnectivityServiceTest {
|
||||
|
||||
// Enable and disable an always-on VPN package without lockdown. Expect no changes.
|
||||
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());
|
||||
callback.assertNoCallback();
|
||||
defaultCallback.assertNoCallback();
|
||||
@@ -7281,7 +7314,7 @@ public class ConnectivityServiceTest {
|
||||
assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
|
||||
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());
|
||||
callback.assertNoCallback();
|
||||
defaultCallback.assertNoCallback();
|
||||
@@ -7293,7 +7326,8 @@ public class ConnectivityServiceTest {
|
||||
assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
|
||||
|
||||
// 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);
|
||||
assertBlockedCallbackInAnyOrder(callback, true, mWiFiNetworkAgent, mCellNetworkAgent);
|
||||
vpnUidCallback.assertNoCallback();
|
||||
|
||||
Reference in New Issue
Block a user