From 9f672bdae68b54f5a4eb09509ddd59f466429612 Mon Sep 17 00:00:00 2001 From: chiachangwang Date: Tue, 28 Jun 2022 08:00:35 +0000 Subject: [PATCH 1/2] Add VpnTestBase for VPN test common code This is a no-op change to move some common codes that share between different VPN unit tests to the new base class. It's helpful to reduce the code duplication for the follow up work to add the VpnManagerServiceTest and other test codes refactor. Bug: 231373589 Test: atest FrameworksNetTests Change-Id: Id5111e7ca33ea8f9eb4bcc6c13d0e681f0664d24 --- .../java/com/android/server/VpnTestBase.java | 90 +++++++++++ .../android/server/connectivity/VpnTest.java | 146 +++++++----------- 2 files changed, 148 insertions(+), 88 deletions(-) create mode 100644 tests/unit/java/com/android/server/VpnTestBase.java diff --git a/tests/unit/java/com/android/server/VpnTestBase.java b/tests/unit/java/com/android/server/VpnTestBase.java new file mode 100644 index 0000000000..3c625711b4 --- /dev/null +++ b/tests/unit/java/com/android/server/VpnTestBase.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import static android.content.pm.UserInfo.FLAG_ADMIN; +import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE; +import static android.content.pm.UserInfo.FLAG_PRIMARY; +import static android.content.pm.UserInfo.FLAG_RESTRICTED; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doAnswer; + +import android.content.pm.PackageManager; +import android.content.pm.UserInfo; +import android.os.Process; +import android.os.UserHandle; +import android.util.ArrayMap; + +import java.util.Map; + +/** Common variables or methods shared between VpnTest and VpnManagerServiceTest. */ +public class VpnTestBase { + protected static final String TEST_VPN_PKG = "com.testvpn.vpn"; + /** + * Names and UIDs for some fake packages. Important points: + * - UID is ordered increasing. + * - One pair of packages have consecutive UIDs. + */ + protected static final String[] PKGS = {"com.example", "org.example", "net.example", "web.vpn"}; + protected static final int[] PKG_UIDS = {10066, 10077, 10078, 10400}; + // Mock packages + protected static final Map sPackages = new ArrayMap<>(); + static { + for (int i = 0; i < PKGS.length; i++) { + sPackages.put(PKGS[i], PKG_UIDS[i]); + } + sPackages.put(TEST_VPN_PKG, Process.myUid()); + } + + // Mock users + protected static final int SYSTEM_USER_ID = 0; + protected static final UserInfo SYSTEM_USER = new UserInfo(0, "system", UserInfo.FLAG_PRIMARY); + protected static final UserInfo PRIMARY_USER = new UserInfo(27, "Primary", + FLAG_ADMIN | FLAG_PRIMARY); + protected static final UserInfo SECONDARY_USER = new UserInfo(15, "Secondary", FLAG_ADMIN); + protected static final UserInfo RESTRICTED_PROFILE_A = new UserInfo(40, "RestrictedA", + FLAG_RESTRICTED); + protected static final UserInfo RESTRICTED_PROFILE_B = new UserInfo(42, "RestrictedB", + FLAG_RESTRICTED); + protected static final UserInfo MANAGED_PROFILE_A = new UserInfo(45, "ManagedA", + FLAG_MANAGED_PROFILE); + static { + RESTRICTED_PROFILE_A.restrictedProfileParentId = PRIMARY_USER.id; + RESTRICTED_PROFILE_B.restrictedProfileParentId = SECONDARY_USER.id; + MANAGED_PROFILE_A.profileGroupId = PRIMARY_USER.id; + } + + // Populate a fake packageName-to-UID mapping. + protected void setMockedPackages(PackageManager mockPm, final Map packages) { + try { + doAnswer(invocation -> { + final String appName = (String) invocation.getArguments()[0]; + final int userId = (int) invocation.getArguments()[1]; + + final Integer appId = packages.get(appName); + if (appId == null) { + throw new PackageManager.NameNotFoundException(appName); + } + + return UserHandle.getUid(userId, appId); + }).when(mockPm).getPackageUidAsUser(anyString(), anyInt()); + } catch (Exception e) { + } + } +} diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java index 8f1d3b889b..75a4704cb2 100644 --- a/tests/unit/java/com/android/server/connectivity/VpnTest.java +++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java @@ -20,10 +20,6 @@ import static android.Manifest.permission.BIND_VPN_SERVICE; import static android.Manifest.permission.CONTROL_VPN; import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static android.content.pm.UserInfo.FLAG_ADMIN; -import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE; -import static android.content.pm.UserInfo.FLAG_PRIMARY; -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; @@ -145,6 +141,7 @@ import com.android.internal.util.HexDump; import com.android.modules.utils.build.SdkLevel; import com.android.server.DeviceIdleInternal; import com.android.server.IpSecService; +import com.android.server.VpnTestBase; import com.android.server.vcn.util.PersistableBundleUtils; import com.android.testutils.DevSdkIgnoreRule; import com.android.testutils.DevSdkIgnoreRunner; @@ -192,27 +189,14 @@ import java.util.stream.Stream; @RunWith(DevSdkIgnoreRunner.class) @SmallTest @IgnoreUpTo(VERSION_CODES.S_V2) -public class VpnTest { +public class VpnTest extends VpnTestBase { private static final String TAG = "VpnTest"; @Rule public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule(); - // Mock users - static final UserInfo primaryUser = new UserInfo(27, "Primary", FLAG_ADMIN | FLAG_PRIMARY); - static final UserInfo secondaryUser = new UserInfo(15, "Secondary", FLAG_ADMIN); - static final UserInfo restrictedProfileA = new UserInfo(40, "RestrictedA", FLAG_RESTRICTED); - static final UserInfo restrictedProfileB = new UserInfo(42, "RestrictedB", FLAG_RESTRICTED); - static final UserInfo managedProfileA = new UserInfo(45, "ManagedA", FLAG_MANAGED_PROFILE); - static { - restrictedProfileA.restrictedProfileParentId = primaryUser.id; - restrictedProfileB.restrictedProfileParentId = secondaryUser.id; - managedProfileA.profileGroupId = primaryUser.id; - } - static final Network EGRESS_NETWORK = new Network(101); static final String EGRESS_IFACE = "wlan0"; - static final String TEST_VPN_PKG = "com.testvpn.vpn"; private static final String TEST_VPN_CLIENT = "2.4.6.8"; private static final String TEST_VPN_SERVER = "1.2.3.4"; private static final String TEST_VPN_IDENTITY = "identity"; @@ -249,23 +233,8 @@ public class VpnTest { private static final long TEST_TIMEOUT_MS = 500L; private static final String PRIMARY_USER_APP_EXCLUDE_KEY = "VPN_APP_EXCLUDED_27_com.testvpn.vpn"; - /** - * Names and UIDs for some fake packages. Important points: - * - UID is ordered increasing. - * - One pair of packages have consecutive UIDs. - */ - static final String[] PKGS = {"com.example", "org.example", "net.example", "web.vpn"}; static final String PKGS_BYTES = getPackageByteString(List.of(PKGS)); - static final int[] PKG_UIDS = {10066, 10077, 10078, 10400}; - - // Mock packages - static final Map mPackages = new ArrayMap<>(); - static { - for (int i = 0; i < PKGS.length; i++) { - mPackages.put(PKGS[i], PKG_UIDS[i]); - } - } - private static final Range PRI_USER_RANGE = uidRangeForUser(primaryUser.id); + private static final Range PRIMARY_USER_RANGE = uidRangeForUser(PRIMARY_USER.id); @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Context mContext; @Mock private UserManager mUserManager; @@ -307,7 +276,7 @@ public class VpnTest { mTestDeps = spy(new TestDeps()); when(mContext.getPackageManager()).thenReturn(mPackageManager); - setMockedPackages(mPackages); + setMockedPackages(sPackages); when(mContext.getPackageName()).thenReturn(TEST_VPN_PKG); when(mContext.getOpPackageName()).thenReturn(TEST_VPN_PKG); @@ -412,50 +381,51 @@ public class VpnTest { @Test public void testRestrictedProfilesAreAddedToVpn() { - setMockedUsers(primaryUser, secondaryUser, restrictedProfileA, restrictedProfileB); + setMockedUsers(PRIMARY_USER, SECONDARY_USER, RESTRICTED_PROFILE_A, RESTRICTED_PROFILE_B); - final Vpn vpn = createVpn(primaryUser.id); + final Vpn vpn = createVpn(PRIMARY_USER.id); // Assume the user can have restricted profiles. doReturn(true).when(mUserManager).canHaveRestrictedProfile(); final Set> ranges = - vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, null, null); + vpn.createUserAndRestrictedProfilesRanges(PRIMARY_USER.id, null, null); - assertEquals(rangeSet(PRI_USER_RANGE, uidRangeForUser(restrictedProfileA.id)), ranges); + assertEquals(rangeSet(PRIMARY_USER_RANGE, uidRangeForUser(RESTRICTED_PROFILE_A.id)), + ranges); } @Test public void testManagedProfilesAreNotAddedToVpn() { - setMockedUsers(primaryUser, managedProfileA); + setMockedUsers(PRIMARY_USER, MANAGED_PROFILE_A); - final Vpn vpn = createVpn(primaryUser.id); - final Set> ranges = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, - null, null); + final Vpn vpn = createVpn(PRIMARY_USER.id); + final Set> ranges = vpn.createUserAndRestrictedProfilesRanges( + PRIMARY_USER.id, null, null); - assertEquals(rangeSet(PRI_USER_RANGE), ranges); + assertEquals(rangeSet(PRIMARY_USER_RANGE), ranges); } @Test public void testAddUserToVpnOnlyAddsOneUser() { - setMockedUsers(primaryUser, restrictedProfileA, managedProfileA); + setMockedUsers(PRIMARY_USER, RESTRICTED_PROFILE_A, MANAGED_PROFILE_A); - final Vpn vpn = createVpn(primaryUser.id); + final Vpn vpn = createVpn(PRIMARY_USER.id); final Set> ranges = new ArraySet<>(); - vpn.addUserToRanges(ranges, primaryUser.id, null, null); + vpn.addUserToRanges(ranges, PRIMARY_USER.id, null, null); - assertEquals(rangeSet(PRI_USER_RANGE), ranges); + assertEquals(rangeSet(PRIMARY_USER_RANGE), ranges); } @Test public void testUidAllowAndDenylist() throws Exception { - final Vpn vpn = createVpn(primaryUser.id); - final Range user = PRI_USER_RANGE; + final Vpn vpn = createVpn(PRIMARY_USER.id); + final Range user = PRIMARY_USER_RANGE; final int userStart = user.getLower(); final int userStop = user.getUpper(); final String[] packages = {PKGS[0], PKGS[1], PKGS[2]}; // Allowed list - final Set> allow = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, + final Set> allow = vpn.createUserAndRestrictedProfilesRanges(PRIMARY_USER.id, Arrays.asList(packages), null /* disallowedApplications */); assertEquals(rangeSet( uidRange(userStart + PKG_UIDS[0], userStart + PKG_UIDS[0]), @@ -468,7 +438,7 @@ public class VpnTest { // Denied list final Set> disallow = - vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, + vpn.createUserAndRestrictedProfilesRanges(PRIMARY_USER.id, null /* allowedApplications */, Arrays.asList(packages)); assertEquals(rangeSet( uidRange(userStart, userStart + PKG_UIDS[0] - 1), @@ -490,7 +460,7 @@ public class VpnTest { @Test public void testGetAlwaysAndOnGetLockDown() throws Exception { - final Vpn vpn = createVpn(primaryUser.id); + final Vpn vpn = createVpn(PRIMARY_USER.id); // Default state. assertFalse(vpn.getAlwaysOn()); @@ -514,8 +484,8 @@ public class VpnTest { @Test public void testLockdownChangingPackage() throws Exception { - final Vpn vpn = createVpn(primaryUser.id); - final Range user = PRI_USER_RANGE; + final Vpn vpn = createVpn(PRIMARY_USER.id); + final Range user = PRIMARY_USER_RANGE; final int userStart = user.getLower(); final int userStop = user.getUpper(); // Set always-on without lockdown. @@ -548,8 +518,8 @@ public class VpnTest { @Test public void testLockdownAllowlist() throws Exception { - final Vpn vpn = createVpn(primaryUser.id); - final Range user = PRI_USER_RANGE; + final Vpn vpn = createVpn(PRIMARY_USER.id); + final Range user = PRIMARY_USER_RANGE; final int userStart = user.getLower(); final int userStop = user.getUpper(); // Set always-on with lockdown and allow app PKGS[2] from lockdown. @@ -659,9 +629,9 @@ public class VpnTest { @Test public void testLockdownRuleRepeatability() throws Exception { - final Vpn vpn = createVpn(primaryUser.id); + final Vpn vpn = createVpn(PRIMARY_USER.id); final UidRangeParcel[] primaryUserRangeParcel = new UidRangeParcel[] { - new UidRangeParcel(PRI_USER_RANGE.getLower(), PRI_USER_RANGE.getUpper())}; + new UidRangeParcel(PRIMARY_USER_RANGE.getLower(), PRIMARY_USER_RANGE.getUpper())}; // Given legacy lockdown is already enabled, vpn.setLockdown(true); verify(mConnectivityManager, times(1)).setRequireVpnForUids(true, @@ -692,9 +662,9 @@ public class VpnTest { @Test public void testLockdownRuleReversibility() throws Exception { doReturn(PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission(CONTROL_VPN); - final Vpn vpn = createVpn(primaryUser.id); + final Vpn vpn = createVpn(PRIMARY_USER.id); final UidRangeParcel[] entireUser = { - new UidRangeParcel(PRI_USER_RANGE.getLower(), PRI_USER_RANGE.getUpper()) + new UidRangeParcel(PRIMARY_USER_RANGE.getLower(), PRIMARY_USER_RANGE.getUpper()) }; final UidRangeParcel[] exceptPkg0 = { new UidRangeParcel(entireUser[0].start, entireUser[0].start + PKG_UIDS[0] - 1), @@ -744,17 +714,17 @@ public class VpnTest { @Test public void testIsAlwaysOnPackageSupported() throws Exception { - final Vpn vpn = createVpn(primaryUser.id); + final Vpn vpn = createVpn(PRIMARY_USER.id); ApplicationInfo appInfo = new ApplicationInfo(); - when(mPackageManager.getApplicationInfoAsUser(eq(PKGS[0]), anyInt(), eq(primaryUser.id))) + when(mPackageManager.getApplicationInfoAsUser(eq(PKGS[0]), anyInt(), eq(PRIMARY_USER.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))) + eq(PRIMARY_USER.id))) .thenReturn(Collections.singletonList(resInfo)); // null package name should return false @@ -778,9 +748,9 @@ public class VpnTest { @Test public void testNotificationShownForAlwaysOnApp() throws Exception { - final UserHandle userHandle = UserHandle.of(primaryUser.id); - final Vpn vpn = createVpn(primaryUser.id); - setMockedUsers(primaryUser); + final UserHandle userHandle = UserHandle.of(PRIMARY_USER.id); + final Vpn vpn = createVpn(PRIMARY_USER.id); + setMockedUsers(PRIMARY_USER); final InOrder order = inOrder(mNotificationManager); @@ -813,15 +783,15 @@ public class VpnTest { */ @Test public void testGetProfileNameForPackage() throws Exception { - final Vpn vpn = createVpn(primaryUser.id); - setMockedUsers(primaryUser); + final Vpn vpn = createVpn(PRIMARY_USER.id); + setMockedUsers(PRIMARY_USER); - final String expected = Credentials.PLATFORM_VPN + primaryUser.id + "_" + TEST_VPN_PKG; + final String expected = Credentials.PLATFORM_VPN + PRIMARY_USER.id + "_" + TEST_VPN_PKG; assertEquals(expected, vpn.getProfileNameForPackage(TEST_VPN_PKG)); } private Vpn createVpnAndSetupUidChecks(String... grantedOps) throws Exception { - return createVpnAndSetupUidChecks(primaryUser, grantedOps); + return createVpnAndSetupUidChecks(PRIMARY_USER, grantedOps); } private Vpn createVpnAndSetupUidChecks(UserInfo user, String... grantedOps) throws Exception { @@ -894,7 +864,7 @@ public class VpnTest { .put(eq(PRIMARY_USER_APP_EXCLUDE_KEY), eq(HexDump.hexStringToByteArray(PKGS_BYTES))); assertEquals(vpn.createUserAndRestrictedProfilesRanges( - primaryUser.id, null, Arrays.asList(PKGS)), + PRIMARY_USER.id, null, Arrays.asList(PKGS)), vpn.mNetworkCapabilities.getUids()); assertEquals(Arrays.asList(PKGS), vpn.getAppExclusionList(TEST_VPN_PKG)); } @@ -903,7 +873,7 @@ public class VpnTest { public void testSetAndGetAppExclusionListRestrictedUser() throws Exception { final Vpn vpn = prepareVpnForVerifyAppExclusionList(); // Mock it to restricted profile - when(mUserManager.getUserInfo(anyInt())).thenReturn(restrictedProfileA); + when(mUserManager.getUserInfo(anyInt())).thenReturn(RESTRICTED_PROFILE_A); // Restricted users cannot configure VPNs assertThrows(SecurityException.class, () -> vpn.setAppExclusionList(TEST_VPN_PKG, new ArrayList<>())); @@ -953,7 +923,7 @@ public class VpnTest { public void testProvisionVpnProfileRestrictedUser() throws Exception { final Vpn vpn = createVpnAndSetupUidChecks( - restrictedProfileA, AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); + RESTRICTED_PROFILE_A, AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); try { vpn.provisionVpnProfile(TEST_VPN_PKG, mVpnProfile); @@ -976,7 +946,7 @@ public class VpnTest { public void testDeleteVpnProfileRestrictedUser() throws Exception { final Vpn vpn = createVpnAndSetupUidChecks( - restrictedProfileA, AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); + RESTRICTED_PROFILE_A, AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); try { vpn.deleteVpnProfile(TEST_VPN_PKG); @@ -1099,7 +1069,7 @@ public class VpnTest { public void testStartVpnProfileRestrictedUser() throws Exception { final Vpn vpn = createVpnAndSetupUidChecks( - restrictedProfileA, AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); + RESTRICTED_PROFILE_A, AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); try { vpn.startVpnProfile(TEST_VPN_PKG); @@ -1112,7 +1082,7 @@ public class VpnTest { public void testStopVpnProfileRestrictedUser() throws Exception { final Vpn vpn = createVpnAndSetupUidChecks( - restrictedProfileA, AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); + RESTRICTED_PROFILE_A, AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); try { vpn.stopVpnProfile(TEST_VPN_PKG); @@ -1183,7 +1153,7 @@ public class VpnTest { private void verifyVpnManagerEvent(String sessionKey, String category, int errorClass, int errorCode, VpnProfileState... profileState) { final Context userContext = - mContext.createContextAsUser(UserHandle.of(primaryUser.id), 0 /* flags */); + mContext.createContextAsUser(UserHandle.of(PRIMARY_USER.id), 0 /* flags */); final ArgumentCaptor intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class); final int verifyTimes = (profileState == null) ? 1 : profileState.length; @@ -1250,7 +1220,7 @@ public class VpnTest { assumeTrue(SdkLevel.isAtLeastT()); // Calling setAlwaysOnPackage() needs to hold CONTROL_VPN. doReturn(PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission(CONTROL_VPN); - final Vpn vpn = createVpn(primaryUser.id); + final Vpn vpn = createVpn(PRIMARY_USER.id); // Enable VPN always-on for PKGS[1]. assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false /* lockdown */, null /* lockdownAllowlist */)); @@ -1512,7 +1482,7 @@ public class VpnTest { public void testStartPlatformVpnIllegalArgumentExceptionInSetup() throws Exception { when(mIkev2SessionCreator.createIkeSession(any(), any(), any(), any(), any(), any())) .thenThrow(new IllegalArgumentException()); - final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), mVpnProfile); + final Vpn vpn = startLegacyVpn(createVpn(PRIMARY_USER.id), mVpnProfile); final NetworkCallback cb = triggerOnAvailableAndGetCallback(); verifyInterfaceSetCfgWithFlags(IF_STATE_UP); @@ -1532,18 +1502,18 @@ public class VpnTest { eq(AppOpsManager.MODE_ALLOWED)); verify(mSystemServices).settingsSecurePutStringForUser( - eq(Settings.Secure.ALWAYS_ON_VPN_APP), eq(TEST_VPN_PKG), eq(primaryUser.id)); + eq(Settings.Secure.ALWAYS_ON_VPN_APP), eq(TEST_VPN_PKG), eq(PRIMARY_USER.id)); verify(mSystemServices).settingsSecurePutIntForUser( eq(Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN), eq(lockdownEnabled ? 1 : 0), - eq(primaryUser.id)); + eq(PRIMARY_USER.id)); verify(mSystemServices).settingsSecurePutStringForUser( - eq(Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST), eq(""), eq(primaryUser.id)); + eq(Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST), eq(""), eq(PRIMARY_USER.id)); } @Test public void testSetAndStartAlwaysOnVpn() throws Exception { - final Vpn vpn = createVpn(primaryUser.id); - setMockedUsers(primaryUser); + final Vpn vpn = createVpn(PRIMARY_USER.id); + setMockedUsers(PRIMARY_USER); // UID checks must return a different UID; otherwise it'll be treated as already prepared. final int uid = Process.myUid() + 1; @@ -1560,7 +1530,7 @@ public class VpnTest { } private Vpn startLegacyVpn(final Vpn vpn, final VpnProfile vpnProfile) throws Exception { - setMockedUsers(primaryUser); + setMockedUsers(PRIMARY_USER); // Dummy egress interface final LinkProperties lp = new LinkProperties(); @@ -1876,7 +1846,7 @@ public class VpnTest { doReturn(new Network(102)).when(mConnectivityManager).registerNetworkAgent(any(), any(), any(), any(), any(), any(), anyInt()); - final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), profile); + final Vpn vpn = startLegacyVpn(createVpn(PRIMARY_USER.id), profile); final TestDeps deps = (TestDeps) vpn.mDeps; // TODO: use import when this is merged in all branches and there's no merge conflict @@ -1928,7 +1898,7 @@ public class VpnTest { legacyRunnerReady.open(); return new Network(102); }); - final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), profile); + final Vpn vpn = startLegacyVpn(createVpn(PRIMARY_USER.id), profile); final TestDeps deps = (TestDeps) vpn.mDeps; try { // udppsk and 1701 are the values for TYPE_L2TP_IPSEC_PSK From 7f5d04962b5c74a3530b005daae2700389b5a0cc Mon Sep 17 00:00:00 2001 From: chiachangwang Date: Tue, 28 Jun 2022 09:15:12 +0000 Subject: [PATCH 2/2] Test VPN app exclusion Initialize the VpnManagerServiceTest and verify app exclusion design. Bug: 231373589 Test: atest FrameworksNetTests Change-Id: Icf3994a58de7b2fcc6fafe9712b5ac94e6c2c134 --- tests/unit/Android.bp | 1 + .../android/server/VpnManagerServiceTest.java | 149 ++++++++++++++++++ .../java/com/android/server/VpnTestBase.java | 7 + .../android/server/connectivity/VpnTest.java | 90 +++++++++-- 4 files changed, 237 insertions(+), 10 deletions(-) create mode 100644 tests/unit/java/com/android/server/VpnManagerServiceTest.java diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp index 3ea27f7a7a..a05ca92615 100644 --- a/tests/unit/Android.bp +++ b/tests/unit/Android.bp @@ -63,6 +63,7 @@ filegroup { "java/com/android/internal/net/NetworkUtilsInternalTest.java", "java/com/android/internal/net/VpnProfileTest.java", "java/com/android/server/NetworkManagementServiceTest.java", + "java/com/android/server/VpnManagerServiceTest.java", "java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java", "java/com/android/server/connectivity/IpConnectivityMetricsTest.java", "java/com/android/server/connectivity/MultipathPolicyTrackerTest.java", diff --git a/tests/unit/java/com/android/server/VpnManagerServiceTest.java b/tests/unit/java/com/android/server/VpnManagerServiceTest.java new file mode 100644 index 0000000000..1f238bb10a --- /dev/null +++ b/tests/unit/java/com/android/server/VpnManagerServiceTest.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import static android.os.Build.VERSION_CODES.R; + +import static com.android.testutils.ContextUtils.mockService; +import static com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; + +import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.annotation.UserIdInt; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.ConnectivityManager; +import android.net.INetd; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.INetworkManagementService; +import android.os.Looper; +import android.os.UserManager; + +import androidx.test.filters.SmallTest; + +import com.android.server.connectivity.Vpn; +import com.android.testutils.DevSdkIgnoreRule; +import com.android.testutils.DevSdkIgnoreRunner; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.Spy; + +@RunWith(DevSdkIgnoreRunner.class) +@IgnoreUpTo(R) // VpnManagerService is not available before R +@SmallTest +public class VpnManagerServiceTest extends VpnTestBase { + @Rule + public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule(); + + @Spy Context mContext; + private HandlerThread mHandlerThread; + @Mock private Handler mHandler; + @Mock private Vpn mVpn; + @Mock private INetworkManagementService mNms; + @Mock private ConnectivityManager mCm; + @Mock private UserManager mUserManager; + @Mock private INetd mNetd; + @Mock private PackageManager mPackageManager; + private VpnManagerServiceDependencies mDeps; + private VpnManagerService mService; + + class VpnManagerServiceDependencies extends VpnManagerService.Dependencies { + @Override + public HandlerThread makeHandlerThread() { + return mHandlerThread; + } + + @Override + public INetworkManagementService getINetworkManagementService() { + return mNms; + } + + @Override + public INetd getNetd() { + return mNetd; + } + + @Override + public Vpn createVpn(Looper looper, Context context, INetworkManagementService nms, + INetd netd, @UserIdInt int userId) { + return mVpn; + } + } + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + mHandlerThread = new HandlerThread("TestVpnManagerService"); + mDeps = new VpnManagerServiceDependencies(); + doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt()); + doReturn(mPackageManager).when(mContext).getPackageManager(); + setMockedPackages(mPackageManager, sPackages); + + mockService(mContext, ConnectivityManager.class, Context.CONNECTIVITY_SERVICE, mCm); + mockService(mContext, UserManager.class, Context.USER_SERVICE, mUserManager); + + doReturn(new Intent()).when(mContext).registerReceiver( + any() /* receiver */, + any() /* intentFilter */, + any() /* broadcastPermission */, + eq(mHandler) /* scheduler */); + doReturn(SYSTEM_USER).when(mUserManager).getUserInfo(eq(SYSTEM_USER_ID)); + mService = new VpnManagerService(mContext, mDeps); + } + + @Test + public void testUpdateAppExclusionList() { + // Add user to create vpn in mVpn + mService.onUserStarted(SYSTEM_USER_ID); + assertNotNull(mService.mVpns.get(SYSTEM_USER_ID)); + + // Start vpn + mService.startVpnProfile(TEST_VPN_PKG); + verify(mVpn).startVpnProfile(eq(TEST_VPN_PKG)); + + // Remove package due to package replaced. + mService.onPackageRemoved(PKGS[0], PKG_UIDS[0], true /* isReplacing */); + verify(mVpn, never()).refreshPlatformVpnAppExclusionList(); + + // Add package due to package replaced. + mService.onPackageAdded(PKGS[0], PKG_UIDS[0], true /* isReplacing */); + verify(mVpn, never()).refreshPlatformVpnAppExclusionList(); + + // Remove package + mService.onPackageRemoved(PKGS[0], PKG_UIDS[0], false /* isReplacing */); + verify(mVpn).refreshPlatformVpnAppExclusionList(); + + // Add the package back + mService.onPackageAdded(PKGS[0], PKG_UIDS[0], false /* isReplacing */); + verify(mVpn, times(2)).refreshPlatformVpnAppExclusionList(); + } +} diff --git a/tests/unit/java/com/android/server/VpnTestBase.java b/tests/unit/java/com/android/server/VpnTestBase.java index 3c625711b4..6113872e21 100644 --- a/tests/unit/java/com/android/server/VpnTestBase.java +++ b/tests/unit/java/com/android/server/VpnTestBase.java @@ -31,7 +31,10 @@ import android.os.Process; import android.os.UserHandle; import android.util.ArrayMap; +import java.util.Arrays; +import java.util.List; import java.util.Map; +import java.util.stream.Collectors; /** Common variables or methods shared between VpnTest and VpnManagerServiceTest. */ public class VpnTestBase { @@ -87,4 +90,8 @@ public class VpnTestBase { } catch (Exception e) { } } + + protected List toList(int[] arr) { + return Arrays.stream(arr).boxed().collect(Collectors.toList()); + } } diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java index 75a4704cb2..d24c59f54d 100644 --- a/tests/unit/java/com/android/server/connectivity/VpnTest.java +++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java @@ -92,10 +92,8 @@ import android.net.LinkProperties; import android.net.LocalSocket; import android.net.Network; import android.net.NetworkAgent; -import android.net.NetworkAgentConfig; import android.net.NetworkCapabilities; import android.net.NetworkInfo.DetailedState; -import android.net.NetworkProvider; import android.net.RouteInfo; import android.net.UidRangeParcel; import android.net.VpnManager; @@ -117,7 +115,6 @@ import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.ConditionVariable; import android.os.INetworkManagementService; -import android.os.Looper; import android.os.ParcelFileDescriptor; import android.os.PowerWhitelistManager; import android.os.Process; @@ -174,6 +171,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; @@ -188,7 +187,7 @@ import java.util.stream.Stream; */ @RunWith(DevSdkIgnoreRunner.class) @SmallTest -@IgnoreUpTo(VERSION_CODES.S_V2) +@IgnoreUpTo(S_V2) public class VpnTest extends VpnTestBase { private static final String TAG = "VpnTest"; @@ -848,14 +847,11 @@ public class VpnTest extends VpnTestBase { vpn.startVpnProfile(TEST_VPN_PKG); verify(mVpnProfileStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG))); - vpn.mNetworkAgent = new NetworkAgent(mContext, Looper.getMainLooper(), TAG, - new NetworkCapabilities.Builder().build(), new LinkProperties(), 10 /* score */, - new NetworkAgentConfig.Builder().build(), - new NetworkProvider(mContext, Looper.getMainLooper(), TAG)) {}; + vpn.mNetworkAgent = mMockNetworkAgent; return vpn; } - @Test @IgnoreUpTo(S_V2) + @Test public void testSetAndGetAppExclusionList() throws Exception { final Vpn vpn = prepareVpnForVerifyAppExclusionList(); verify(mVpnProfileStore, never()).put(eq(PRIMARY_USER_APP_EXCLUDE_KEY), any()); @@ -869,7 +865,81 @@ public class VpnTest extends VpnTestBase { assertEquals(Arrays.asList(PKGS), vpn.getAppExclusionList(TEST_VPN_PKG)); } - @Test @IgnoreUpTo(S_V2) + @Test + public void testRefreshPlatformVpnAppExclusionList_updatesExcludedUids() throws Exception { + final Vpn vpn = prepareVpnForVerifyAppExclusionList(); + vpn.setAppExclusionList(TEST_VPN_PKG, Arrays.asList(PKGS)); + verify(mMockNetworkAgent).sendNetworkCapabilities(any()); + assertEquals(Arrays.asList(PKGS), vpn.getAppExclusionList(TEST_VPN_PKG)); + + reset(mMockNetworkAgent); + + // Remove one of the package + List newExcludedUids = toList(PKG_UIDS); + newExcludedUids.remove((Integer) PKG_UIDS[0]); + sPackages.remove(PKGS[0]); + vpn.refreshPlatformVpnAppExclusionList(); + + // List in keystore is not changed, but UID for the removed packages is no longer exempted. + assertEquals(Arrays.asList(PKGS), vpn.getAppExclusionList(TEST_VPN_PKG)); + assertEquals(makeVpnUidRange(PRIMARY_USER.id, newExcludedUids), + vpn.mNetworkCapabilities.getUids()); + ArgumentCaptor ncCaptor = + ArgumentCaptor.forClass(NetworkCapabilities.class); + verify(mMockNetworkAgent).sendNetworkCapabilities(ncCaptor.capture()); + assertEquals(makeVpnUidRange(PRIMARY_USER.id, newExcludedUids), + ncCaptor.getValue().getUids()); + + reset(mMockNetworkAgent); + + // Add the package back + newExcludedUids.add(PKG_UIDS[0]); + sPackages.put(PKGS[0], PKG_UIDS[0]); + vpn.refreshPlatformVpnAppExclusionList(); + + // List in keystore is not changed and the uid list should be updated in the net cap. + assertEquals(Arrays.asList(PKGS), vpn.getAppExclusionList(TEST_VPN_PKG)); + assertEquals(makeVpnUidRange(PRIMARY_USER.id, newExcludedUids), + vpn.mNetworkCapabilities.getUids()); + verify(mMockNetworkAgent).sendNetworkCapabilities(ncCaptor.capture()); + assertEquals(makeVpnUidRange(PRIMARY_USER.id, newExcludedUids), + ncCaptor.getValue().getUids()); + } + + private Set> makeVpnUidRange(int userId, List excludedList) { + final SortedSet list = new TreeSet<>(); + + final int userBase = userId * UserHandle.PER_USER_RANGE; + for (int uid : excludedList) { + final int applicationUid = UserHandle.getUid(userId, uid); + list.add(applicationUid); + list.add(Process.toSdkSandboxUid(applicationUid)); // Add Sdk Sandbox UID + } + + final int minUid = userBase; + final int maxUid = userBase + UserHandle.PER_USER_RANGE - 1; + final Set> ranges = new ArraySet<>(); + + // Iterate the list to create the ranges between each uid. + int start = minUid; + for (int uid : list) { + if (uid == start) { + start++; + } else { + ranges.add(new Range<>(start, uid - 1)); + start = uid + 1; + } + } + + // Create the range between last uid and max uid. + if (start <= maxUid) { + ranges.add(new Range<>(start, maxUid)); + } + + return ranges; + } + + @Test public void testSetAndGetAppExclusionListRestrictedUser() throws Exception { final Vpn vpn = prepareVpnForVerifyAppExclusionList(); // Mock it to restricted profile