From c5bf088ced4c729c5a11c4d29e2c0bb9077a4c14 Mon Sep 17 00:00:00 2001 From: lucaslin Date: Mon, 25 Apr 2022 23:12:26 +0800 Subject: [PATCH] Test VpnManager event for CATEGORY_EVENT_DEACTIVATED_BY_USER Bug: 191413541 Test: atest FrameworksNetTests:VpnTest Change-Id: Idc876275b88c2f4ddeb85c43d37d5f657b0f307b --- .../android/server/connectivity/VpnTest.java | 119 ++++++++++++++---- 1 file changed, 92 insertions(+), 27 deletions(-) diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java index 33c0868c17..52a61de1cf 100644 --- a/tests/unit/java/com/android/server/connectivity/VpnTest.java +++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java @@ -27,6 +27,7 @@ import static android.content.pm.UserInfo.FLAG_RESTRICTED; import static android.net.ConnectivityManager.NetworkCallback; import static android.net.INetd.IF_STATE_DOWN; import static android.net.INetd.IF_STATE_UP; +import static android.net.VpnManager.TYPE_VPN_PLATFORM; import static android.os.UserHandle.PER_USER_RANGE; import static com.android.modules.utils.build.SdkLevel.isAtLeastT; @@ -54,6 +55,7 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -65,6 +67,7 @@ import android.app.AppOpsManager; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; +import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; @@ -118,6 +121,7 @@ import com.android.server.IpSecService; import com.android.testutils.DevSdkIgnoreRule; import com.android.testutils.DevSdkIgnoreRunner; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -271,6 +275,11 @@ public class VpnTest { doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(any()); } + @After + public void tearDown() throws Exception { + doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(CONTROL_VPN); + } + private void mockService(Class clazz, String name, T service) { doReturn(service).when(mContext).getSystemService(name); doReturn(name).when(mContext).getSystemServiceName(clazz); @@ -783,6 +792,30 @@ public class VpnTest { verify(mVpnProfileStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG))); } + private void verifyPlatformVpnIsActivated(String packageName) { + verify(mAppOps).noteOpNoThrow( + eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN), + eq(Process.myUid()), + eq(packageName), + eq(null) /* attributionTag */, + eq(null) /* message */); + verify(mAppOps).startOp( + eq(AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER), + eq(Process.myUid()), + eq(packageName), + eq(null) /* attributionTag */, + eq(null) /* message */); + } + + private void verifyPlatformVpnIsDeactivated(String packageName) { + // Add a small delay to double confirm that finishOp is only called once. + verify(mAppOps, after(100)).finishOp( + eq(AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER), + eq(Process.myUid()), + eq(packageName), + eq(null) /* attributionTag */); + } + @Test public void testStartVpnProfile() throws Exception { final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); @@ -793,13 +826,7 @@ public class VpnTest { vpn.startVpnProfile(TEST_VPN_PKG); verify(mVpnProfileStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG))); - verify(mAppOps) - .noteOpNoThrow( - eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN), - eq(Process.myUid()), - eq(TEST_VPN_PKG), - eq(null) /* attributionTag */, - eq(null) /* message */); + verifyPlatformVpnIsActivated(TEST_VPN_PKG); } @Test @@ -811,7 +838,7 @@ public class VpnTest { vpn.startVpnProfile(TEST_VPN_PKG); - // Verify that the the ACTIVATE_VPN appop was checked, but no error was thrown. + // Verify that the ACTIVATE_VPN appop was checked, but no error was thrown. verify(mAppOps).noteOpNoThrow(AppOpsManager.OPSTR_ACTIVATE_VPN, Process.myUid(), TEST_VPN_PKG, null /* attributionTag */, null /* message */); } @@ -896,18 +923,7 @@ public class VpnTest { when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG))) .thenReturn(mVpnProfile.encode()); vpn.startVpnProfile(TEST_VPN_PKG); - verify(mAppOps).noteOpNoThrow( - eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN), - eq(Process.myUid()), - eq(TEST_VPN_PKG), - eq(null) /* attributionTag */, - eq(null) /* message */); - verify(mAppOps).startOp( - eq(AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER), - eq(Process.myUid()), - eq(TEST_VPN_PKG), - eq(null) /* attributionTag */, - eq(null) /* message */); + verifyPlatformVpnIsActivated(TEST_VPN_PKG); // Add a small delay to make sure that startOp is only called once. verify(mAppOps, after(100).times(1)).startOp( eq(AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER), @@ -923,12 +939,7 @@ public class VpnTest { eq(null) /* attributionTag */, eq(null) /* message */); vpn.stopVpnProfile(TEST_VPN_PKG); - // Add a small delay to double confirm that startOp is only called once. - verify(mAppOps, after(100)).finishOp( - eq(AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER), - eq(Process.myUid()), - eq(TEST_VPN_PKG), - eq(null) /* attributionTag */); + verifyPlatformVpnIsDeactivated(TEST_VPN_PKG); } @Test @@ -964,6 +975,60 @@ public class VpnTest { eq(null) /* message */); } + private void verifyVpnManagerEvent(String sessionKey, String category, int errorClass, + int errorCode) { + final Context userContext = + mContext.createContextAsUser(UserHandle.of(primaryUser.id), 0 /* flags */); + final ArgumentCaptor intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class); + + verify(userContext, timeout(TEST_TIMEOUT_MS)).startService( + intentArgumentCaptor.capture()); + + final Intent intent = intentArgumentCaptor.getValue(); + assertEquals(sessionKey, intent.getStringExtra(VpnManager.EXTRA_SESSION_KEY)); + assertTrue(intent.getCategories().contains(category)); + assertEquals(errorClass, + intent.getIntExtra(VpnManager.EXTRA_ERROR_CLASS, -1 /* defaultValue */)); + assertEquals(errorCode, + intent.getIntExtra(VpnManager.EXTRA_ERROR_CODE, -1 /* defaultValue */)); + reset(userContext); + } + + @Test + public void testVpnManagerEventForUserDeactivated() throws Exception { + assumeTrue(SdkLevel.isAtLeastT()); + // For security reasons, Vpn#prepare() will check that oldPackage and newPackage are either + // null or the package of the caller. This test will call Vpn#prepare() to pretend the old + // VPN is replaced by a new one. But only Settings can change to some other packages, and + // this is checked with CONTROL_VPN so simulate holding CONTROL_VPN in order to pass the + // security checks. + doReturn(PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission(CONTROL_VPN); + final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); + when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG))) + .thenReturn(mVpnProfile.encode()); + + // Test the case that the user deactivates the vpn in vpn app. + final String sessionKey1 = vpn.startVpnProfile(TEST_VPN_PKG); + verifyPlatformVpnIsActivated(TEST_VPN_PKG); + vpn.stopVpnProfile(TEST_VPN_PKG); + verifyPlatformVpnIsDeactivated(TEST_VPN_PKG); + // CATEGORY_EVENT_DEACTIVATED_BY_USER is not an error event, so both of errorClass and + // errorCode won't be set. + verifyVpnManagerEvent(sessionKey1, VpnManager.CATEGORY_EVENT_DEACTIVATED_BY_USER, + -1 /* errorClass */, -1 /* errorCode */); + reset(mAppOps); + + // Test the case that the user chooses another vpn and the original one is replaced. + final String sessionKey2 = vpn.startVpnProfile(TEST_VPN_PKG); + verifyPlatformVpnIsActivated(TEST_VPN_PKG); + vpn.prepare(TEST_VPN_PKG, "com.new.vpn" /* newPackage */, TYPE_VPN_PLATFORM); + verifyPlatformVpnIsDeactivated(TEST_VPN_PKG); + // CATEGORY_EVENT_DEACTIVATED_BY_USER is not an error event, so both of errorClass and + // errorCode won't be set. + verifyVpnManagerEvent(sessionKey2, VpnManager.CATEGORY_EVENT_DEACTIVATED_BY_USER, + -1 /* errorClass */, -1 /* errorCode */); + } + @Test public void testSetPackageAuthorizationVpnService() throws Exception { final Vpn vpn = createVpnAndSetupUidChecks(); @@ -981,7 +1046,7 @@ public class VpnTest { public void testSetPackageAuthorizationPlatformVpn() throws Exception { final Vpn vpn = createVpnAndSetupUidChecks(); - assertTrue(vpn.setPackageAuthorization(TEST_VPN_PKG, VpnManager.TYPE_VPN_PLATFORM)); + assertTrue(vpn.setPackageAuthorization(TEST_VPN_PKG, TYPE_VPN_PLATFORM)); verify(mAppOps) .setMode( eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN),