Merge "Opt-out for always-on VPN"
This commit is contained in:
@@ -834,6 +834,29 @@ 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#METADATA_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.
|
||||
|
||||
@@ -123,6 +123,7 @@ interface IConnectivityManager
|
||||
VpnInfo[] getAllVpnInfo();
|
||||
|
||||
boolean updateLockdownVpn();
|
||||
boolean isAlwaysOnVpnPackageSupported(int userId, String packageName);
|
||||
boolean setAlwaysOnVpnPackage(int userId, String packageName, boolean lockdown);
|
||||
String getAlwaysOnVpnPackage(int userId);
|
||||
|
||||
|
||||
@@ -40,7 +40,6 @@ import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.ConnectivityManager;
|
||||
@@ -52,10 +51,10 @@ import android.net.INetworkPolicyManager;
|
||||
import android.net.INetworkStatsService;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.LinkProperties.CompareResult;
|
||||
import android.net.MatchAllNetworkSpecifier;
|
||||
import android.net.Network;
|
||||
import android.net.NetworkAgent;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.MatchAllNetworkSpecifier;
|
||||
import android.net.NetworkConfig;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.NetworkInfo.DetailedState;
|
||||
@@ -124,13 +123,12 @@ import com.android.internal.util.IndentingPrintWriter;
|
||||
import com.android.internal.util.MessageUtils;
|
||||
import com.android.internal.util.WakeupMessage;
|
||||
import com.android.internal.util.XmlUtils;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.am.BatteryStatsService;
|
||||
import com.android.server.connectivity.DataConnectionStats;
|
||||
import com.android.server.connectivity.KeepaliveTracker;
|
||||
import com.android.server.connectivity.LingerMonitor;
|
||||
import com.android.server.connectivity.MockableSystemProperties;
|
||||
import com.android.server.connectivity.Nat464Xlat;
|
||||
import com.android.server.connectivity.LingerMonitor;
|
||||
import com.android.server.connectivity.NetworkAgentInfo;
|
||||
import com.android.server.connectivity.NetworkDiagnostics;
|
||||
import com.android.server.connectivity.NetworkMonitor;
|
||||
@@ -139,8 +137,8 @@ import com.android.server.connectivity.NetworkNotificationManager.NotificationTy
|
||||
import com.android.server.connectivity.PacManager;
|
||||
import com.android.server.connectivity.PermissionMonitor;
|
||||
import com.android.server.connectivity.Tethering;
|
||||
import com.android.server.connectivity.tethering.TetheringDependencies;
|
||||
import com.android.server.connectivity.Vpn;
|
||||
import com.android.server.connectivity.tethering.TetheringDependencies;
|
||||
import com.android.server.net.BaseNetworkObserver;
|
||||
import com.android.server.net.LockdownVpnTracker;
|
||||
import com.android.server.net.NetworkPolicyManagerInternal;
|
||||
@@ -1494,6 +1492,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
ConnectivityManager.enforceChangePermission(mContext);
|
||||
}
|
||||
|
||||
private void enforceSettingsPermission() {
|
||||
mContext.enforceCallingOrSelfPermission(
|
||||
android.Manifest.permission.NETWORK_SETTINGS,
|
||||
"ConnectivityService");
|
||||
}
|
||||
|
||||
private void enforceTetherAccessPermission() {
|
||||
mContext.enforceCallingOrSelfPermission(
|
||||
android.Manifest.permission.ACCESS_NETWORK_STATE,
|
||||
@@ -3624,6 +3628,21 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAlwaysOnVpnPackageSupported(int userId, String packageName) {
|
||||
enforceSettingsPermission();
|
||||
enforceCrossUserPermission(userId);
|
||||
|
||||
synchronized (mVpns) {
|
||||
Vpn vpn = mVpns.get(userId);
|
||||
if (vpn == null) {
|
||||
Slog.w(TAG, "User " + userId + " has no Vpn configuration");
|
||||
return false;
|
||||
}
|
||||
return vpn.isAlwaysOnPackageSupported(packageName);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setAlwaysOnVpnPackage(int userId, String packageName, boolean lockdown) {
|
||||
enforceConnectivityInternalPermission();
|
||||
|
||||
@@ -27,13 +27,16 @@ import android.annotation.UserIdInt;
|
||||
import android.app.AppOpsManager;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.net.NetworkInfo.DetailedState;
|
||||
import android.net.UidRange;
|
||||
import android.os.Build;
|
||||
import android.net.VpnService;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.os.Bundle;
|
||||
import android.os.INetworkManagementService;
|
||||
import android.os.Looper;
|
||||
import android.os.UserHandle;
|
||||
@@ -45,22 +48,22 @@ import android.util.ArraySet;
|
||||
|
||||
import com.android.internal.net.VpnConfig;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Tests for {@link Vpn}.
|
||||
*
|
||||
* Build, install and run with:
|
||||
* runtest --path src/com/android/server/connectivity/VpnTest.java
|
||||
* runtest --path java/com/android/server/connectivity/VpnTest.java
|
||||
*/
|
||||
public class VpnTest extends AndroidTestCase {
|
||||
private static final String TAG = "VpnTest";
|
||||
@@ -116,7 +119,7 @@ public class VpnTest extends AndroidTestCase {
|
||||
|
||||
// Used by {@link Notification.Builder}
|
||||
ApplicationInfo applicationInfo = new ApplicationInfo();
|
||||
applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
|
||||
applicationInfo.targetSdkVersion = VERSION_CODES.CUR_DEVELOPMENT;
|
||||
when(mContext.getApplicationInfo()).thenReturn(applicationInfo);
|
||||
|
||||
doNothing().when(mNetService).registerObserver(any());
|
||||
@@ -314,6 +317,40 @@ public class VpnTest extends AndroidTestCase {
|
||||
order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(entireUser));
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testIsAlwaysOnPackageSupported() throws Exception {
|
||||
final Vpn vpn = createVpn(primaryUser.id);
|
||||
|
||||
ApplicationInfo appInfo = new ApplicationInfo();
|
||||
when(mPackageManager.getApplicationInfoAsUser(eq(PKGS[0]), anyInt(), eq(primaryUser.id)))
|
||||
.thenReturn(appInfo);
|
||||
|
||||
ServiceInfo svcInfo = new ServiceInfo();
|
||||
ResolveInfo resInfo = new ResolveInfo();
|
||||
resInfo.serviceInfo = svcInfo;
|
||||
when(mPackageManager.queryIntentServicesAsUser(any(), eq(PackageManager.GET_META_DATA),
|
||||
eq(primaryUser.id)))
|
||||
.thenReturn(Collections.singletonList(resInfo));
|
||||
|
||||
// null package name should return false
|
||||
assertFalse(vpn.isAlwaysOnPackageSupported(null));
|
||||
|
||||
// Pre-N apps are not supported
|
||||
appInfo.targetSdkVersion = VERSION_CODES.M;
|
||||
assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0]));
|
||||
|
||||
// N+ apps are supported by default
|
||||
appInfo.targetSdkVersion = VERSION_CODES.N;
|
||||
assertTrue(vpn.isAlwaysOnPackageSupported(PKGS[0]));
|
||||
|
||||
// Apps that opt out explicitly are not supported
|
||||
appInfo.targetSdkVersion = VERSION_CODES.CUR_DEVELOPMENT;
|
||||
Bundle metaData = new Bundle();
|
||||
metaData.putBoolean(VpnService.METADATA_SUPPORTS_ALWAYS_ON, false);
|
||||
svcInfo.metaData = metaData;
|
||||
assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0]));
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testNotificationShownForAlwaysOnApp() {
|
||||
final UserHandle userHandle = UserHandle.of(primaryUser.id);
|
||||
|
||||
Reference in New Issue
Block a user