Merge "Add separate user consent for Platform VPNs"
This commit is contained in:
@@ -116,7 +116,7 @@ interface IConnectivityManager
|
|||||||
|
|
||||||
boolean prepareVpn(String oldPackage, String newPackage, int userId);
|
boolean prepareVpn(String oldPackage, String newPackage, int userId);
|
||||||
|
|
||||||
void setVpnPackageAuthorization(String packageName, int userId, boolean authorized);
|
void setVpnPackageAuthorization(String packageName, int userId, int vpnType);
|
||||||
|
|
||||||
ParcelFileDescriptor establishVpn(in VpnConfig config);
|
ParcelFileDescriptor establishVpn(in VpnConfig config);
|
||||||
|
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ import android.net.SocketKeepalive;
|
|||||||
import android.net.TetheringManager;
|
import android.net.TetheringManager;
|
||||||
import android.net.UidRange;
|
import android.net.UidRange;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.net.VpnManager;
|
||||||
import android.net.VpnService;
|
import android.net.VpnService;
|
||||||
import android.net.metrics.IpConnectivityLog;
|
import android.net.metrics.IpConnectivityLog;
|
||||||
import android.net.metrics.NetworkEvent;
|
import android.net.metrics.NetworkEvent;
|
||||||
@@ -4310,7 +4311,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
throwIfLockdownEnabled();
|
throwIfLockdownEnabled();
|
||||||
Vpn vpn = mVpns.get(userId);
|
Vpn vpn = mVpns.get(userId);
|
||||||
if (vpn != null) {
|
if (vpn != null) {
|
||||||
return vpn.prepare(oldPackage, newPackage, false);
|
return vpn.prepare(oldPackage, newPackage, VpnManager.TYPE_VPN_SERVICE);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -4318,26 +4319,29 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set whether the VPN package has the ability to launch VPNs without user intervention.
|
* Set whether the VPN package has the ability to launch VPNs without user intervention. This
|
||||||
* This method is used by system-privileged apps.
|
* method is used by system-privileged apps. VPN permissions are checked in the {@link Vpn}
|
||||||
* VPN permissions are checked in the {@link Vpn} class. If the caller is not {@code userId},
|
* class. If the caller is not {@code userId}, {@link
|
||||||
* {@link android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
|
* android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
|
||||||
*
|
*
|
||||||
* @param packageName The package for which authorization state should change.
|
* @param packageName The package for which authorization state should change.
|
||||||
* @param userId User for whom {@code packageName} is installed.
|
* @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
|
* @param authorized {@code true} if this app should be able to start a VPN connection without
|
||||||
* explicit user approval, {@code false} if not.
|
* 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
|
* @hide
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void setVpnPackageAuthorization(String packageName, int userId, boolean authorized) {
|
public void setVpnPackageAuthorization(
|
||||||
|
String packageName, int userId, @VpnManager.VpnType int vpnType) {
|
||||||
enforceCrossUserPermission(userId);
|
enforceCrossUserPermission(userId);
|
||||||
|
|
||||||
synchronized (mVpns) {
|
synchronized (mVpns) {
|
||||||
Vpn vpn = mVpns.get(userId);
|
Vpn vpn = mVpns.get(userId);
|
||||||
if (vpn != null) {
|
if (vpn != null) {
|
||||||
vpn.setPackageAuthorization(packageName, authorized);
|
vpn.setPackageAuthorization(packageName, vpnType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7239,7 +7243,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
final String alwaysOnPackage = getAlwaysOnVpnPackage(userId);
|
final String alwaysOnPackage = getAlwaysOnVpnPackage(userId);
|
||||||
if (alwaysOnPackage != null) {
|
if (alwaysOnPackage != null) {
|
||||||
setAlwaysOnVpnPackage(userId, null, false, null);
|
setAlwaysOnVpnPackage(userId, null, false, null);
|
||||||
setVpnPackageAuthorization(alwaysOnPackage, userId, false);
|
setVpnPackageAuthorization(alwaysOnPackage, userId, VpnManager.TYPE_VPN_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Turn Always-on VPN off
|
// Turn Always-on VPN off
|
||||||
@@ -7262,7 +7266,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
} else {
|
} else {
|
||||||
// Prevent this app (packagename = vpnConfig.user) from initiating
|
// Prevent this app (packagename = vpnConfig.user) from initiating
|
||||||
// VPN connections in the future without user intervention.
|
// VPN connections in the future without user intervention.
|
||||||
setVpnPackageAuthorization(vpnConfig.user, userId, false);
|
setVpnPackageAuthorization(
|
||||||
|
vpnConfig.user, userId, VpnManager.TYPE_VPN_NONE);
|
||||||
|
|
||||||
prepareVpn(null, VpnConfig.LEGACY_VPN, userId);
|
prepareVpn(null, VpnConfig.LEGACY_VPN, userId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package android.net;
|
package android.net;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.mockito.Matchers.any;
|
import static org.mockito.Matchers.any;
|
||||||
@@ -24,6 +25,8 @@ import static org.mockito.Mockito.mock;
|
|||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Intent;
|
||||||
import android.test.mock.MockContext;
|
import android.test.mock.MockContext;
|
||||||
|
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
@@ -78,7 +81,13 @@ public class VpnManagerTest {
|
|||||||
when(mMockCs.provisionVpnProfile(any(VpnProfile.class), eq(PKG_NAME))).thenReturn(false);
|
when(mMockCs.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.
|
||||||
assertNotNull(mVpnManager.provisionVpnProfile(profile));
|
final Intent intent = mVpnManager.provisionVpnProfile(profile);
|
||||||
|
assertNotNull(intent);
|
||||||
|
|
||||||
|
final ComponentName expectedComponentName =
|
||||||
|
ComponentName.unflattenFromString(
|
||||||
|
"com.android.vpndialogs/com.android.vpndialogs.PlatformVpnConfirmDialog");
|
||||||
|
assertEquals(expectedComponentName, intent.getComponent());
|
||||||
verify(mMockCs).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME));
|
verify(mMockCs).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ import android.net.Network;
|
|||||||
import android.net.NetworkCapabilities;
|
import android.net.NetworkCapabilities;
|
||||||
import android.net.NetworkInfo.DetailedState;
|
import android.net.NetworkInfo.DetailedState;
|
||||||
import android.net.UidRange;
|
import android.net.UidRange;
|
||||||
|
import android.net.VpnManager;
|
||||||
import android.net.VpnService;
|
import android.net.VpnService;
|
||||||
import android.os.Build.VERSION_CODES;
|
import android.os.Build.VERSION_CODES;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@@ -471,12 +472,12 @@ public class VpnTest {
|
|||||||
order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(entireUser));
|
order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(entireUser));
|
||||||
|
|
||||||
// When a new VPN package is set the rules should change to cover that package.
|
// When a new VPN package is set the rules should change to cover that package.
|
||||||
vpn.prepare(null, PKGS[0], false /* isPlatformVpn */);
|
vpn.prepare(null, PKGS[0], VpnManager.TYPE_VPN_SERVICE);
|
||||||
order.verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(entireUser));
|
order.verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(entireUser));
|
||||||
order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(exceptPkg0));
|
order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(exceptPkg0));
|
||||||
|
|
||||||
// When that VPN package is unset, everything should be undone again in reverse.
|
// When that VPN package is unset, everything should be undone again in reverse.
|
||||||
vpn.prepare(null, VpnConfig.LEGACY_VPN, false /* isPlatformVpn */);
|
vpn.prepare(null, VpnConfig.LEGACY_VPN, VpnManager.TYPE_VPN_SERVICE);
|
||||||
order.verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(exceptPkg0));
|
order.verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(exceptPkg0));
|
||||||
order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(entireUser));
|
order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(entireUser));
|
||||||
}
|
}
|
||||||
@@ -817,6 +818,51 @@ public class VpnTest {
|
|||||||
eq(TEST_VPN_PKG));
|
eq(TEST_VPN_PKG));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetPackageAuthorizationVpnService() throws Exception {
|
||||||
|
final Vpn vpn = createVpnAndSetupUidChecks();
|
||||||
|
|
||||||
|
assertTrue(vpn.setPackageAuthorization(TEST_VPN_PKG, VpnManager.TYPE_VPN_SERVICE));
|
||||||
|
verify(mAppOps)
|
||||||
|
.setMode(
|
||||||
|
eq(AppOpsManager.OP_ACTIVATE_VPN),
|
||||||
|
eq(Process.myUid()),
|
||||||
|
eq(TEST_VPN_PKG),
|
||||||
|
eq(AppOpsManager.MODE_ALLOWED));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetPackageAuthorizationPlatformVpn() throws Exception {
|
||||||
|
final Vpn vpn = createVpnAndSetupUidChecks();
|
||||||
|
|
||||||
|
assertTrue(vpn.setPackageAuthorization(TEST_VPN_PKG, VpnManager.TYPE_VPN_PLATFORM));
|
||||||
|
verify(mAppOps)
|
||||||
|
.setMode(
|
||||||
|
eq(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN),
|
||||||
|
eq(Process.myUid()),
|
||||||
|
eq(TEST_VPN_PKG),
|
||||||
|
eq(AppOpsManager.MODE_ALLOWED));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetPackageAuthorizationRevokeAuthorization() throws Exception {
|
||||||
|
final Vpn vpn = createVpnAndSetupUidChecks();
|
||||||
|
|
||||||
|
assertTrue(vpn.setPackageAuthorization(TEST_VPN_PKG, VpnManager.TYPE_VPN_NONE));
|
||||||
|
verify(mAppOps)
|
||||||
|
.setMode(
|
||||||
|
eq(AppOpsManager.OP_ACTIVATE_VPN),
|
||||||
|
eq(Process.myUid()),
|
||||||
|
eq(TEST_VPN_PKG),
|
||||||
|
eq(AppOpsManager.MODE_IGNORED));
|
||||||
|
verify(mAppOps)
|
||||||
|
.setMode(
|
||||||
|
eq(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN),
|
||||||
|
eq(Process.myUid()),
|
||||||
|
eq(TEST_VPN_PKG),
|
||||||
|
eq(AppOpsManager.MODE_IGNORED));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mock some methods of vpn object.
|
* Mock some methods of vpn object.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user