blob: 5d8b843bbc172065421cc7ad37a9b7d5b04138d7 [file] [log] [blame]
Robin Lee4d03abc2016-05-09 12:32:27 +01001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.connectivity;
18
19import static android.content.pm.UserInfo.FLAG_ADMIN;
20import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
21import static android.content.pm.UserInfo.FLAG_PRIMARY;
22import static android.content.pm.UserInfo.FLAG_RESTRICTED;
Robin Lee17e61832016-05-09 13:46:28 +010023import static org.mockito.AdditionalMatchers.*;
Robin Lee4d03abc2016-05-09 12:32:27 +010024import static org.mockito.Mockito.*;
25
26import android.annotation.UserIdInt;
Robin Lee17e61832016-05-09 13:46:28 +010027import android.app.AppOpsManager;
Robin Lee4d03abc2016-05-09 12:32:27 +010028import android.content.Context;
29import android.content.pm.PackageManager;
30import android.content.pm.UserInfo;
31import android.net.UidRange;
32import android.os.INetworkManagementService;
33import android.os.Looper;
34import android.os.UserHandle;
35import android.os.UserManager;
36import android.test.AndroidTestCase;
37import android.test.suitebuilder.annotation.SmallTest;
38import android.util.ArrayMap;
39import android.util.ArraySet;
40
41import java.util.ArrayList;
42import java.util.Arrays;
43import java.util.Map;
44import java.util.Set;
45
46import org.mockito.Mock;
47import org.mockito.MockitoAnnotations;
48
49/**
50 * Tests for {@link Vpn}.
51 *
52 * Build, install and run with:
53 * runtest --path src/com/android/server/connectivity/VpnTest.java
54 */
55public class VpnTest extends AndroidTestCase {
56 private static final String TAG = "VpnTest";
57
58 // Mock users
59 static final UserInfo primaryUser = new UserInfo(27, "Primary", FLAG_ADMIN | FLAG_PRIMARY);
60 static final UserInfo secondaryUser = new UserInfo(15, "Secondary", FLAG_ADMIN);
61 static final UserInfo restrictedProfileA = new UserInfo(40, "RestrictedA", FLAG_RESTRICTED);
62 static final UserInfo restrictedProfileB = new UserInfo(42, "RestrictedB", FLAG_RESTRICTED);
63 static final UserInfo managedProfileA = new UserInfo(45, "ManagedA", FLAG_MANAGED_PROFILE);
64 static {
65 restrictedProfileA.restrictedProfileParentId = primaryUser.id;
66 restrictedProfileB.restrictedProfileParentId = secondaryUser.id;
67 managedProfileA.profileGroupId = primaryUser.id;
68 }
69
Robin Lee17e61832016-05-09 13:46:28 +010070 /**
71 * Names and UIDs for some fake packages. Important points:
72 * - UID is ordered increasing.
73 * - One pair of packages have consecutive UIDs.
74 */
75 static final String[] PKGS = {"com.example", "org.example", "net.example", "web.vpn"};
76 static final int[] PKG_UIDS = {66, 77, 78, 400};
77
78 // Mock packages
79 static final Map<String, Integer> mPackages = new ArrayMap<>();
80 static {
81 for (int i = 0; i < PKGS.length; i++) {
82 mPackages.put(PKGS[i], PKG_UIDS[i]);
83 }
84 }
85
Robin Lee4d03abc2016-05-09 12:32:27 +010086 @Mock private Context mContext;
87 @Mock private UserManager mUserManager;
88 @Mock private PackageManager mPackageManager;
89 @Mock private INetworkManagementService mNetService;
Robin Lee17e61832016-05-09 13:46:28 +010090 @Mock private AppOpsManager mAppOps;
Robin Lee4d03abc2016-05-09 12:32:27 +010091
92 @Override
93 public void setUp() throws Exception {
94 MockitoAnnotations.initMocks(this);
95 when(mContext.getPackageManager()).thenReturn(mPackageManager);
Robin Lee17e61832016-05-09 13:46:28 +010096 setMockedPackages(mPackages);
Robin Lee4d03abc2016-05-09 12:32:27 +010097 when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager);
Robin Lee17e61832016-05-09 13:46:28 +010098 when(mContext.getSystemService(eq(Context.APP_OPS_SERVICE))).thenReturn(mAppOps);
Robin Lee4d03abc2016-05-09 12:32:27 +010099 doNothing().when(mNetService).registerObserver(any());
100 }
101
102 @SmallTest
103 public void testRestrictedProfilesAreAddedToVpn() {
104 setMockedUsers(primaryUser, secondaryUser, restrictedProfileA, restrictedProfileB);
105
Robin Lee17e61832016-05-09 13:46:28 +0100106 final Vpn vpn = new MockVpn(primaryUser.id);
Robin Lee4d03abc2016-05-09 12:32:27 +0100107 final Set<UidRange> ranges = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
108 null, null);
109
110 assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
111 UidRange.createForUser(primaryUser.id),
112 UidRange.createForUser(restrictedProfileA.id)
113 })), ranges);
114 }
115
116 @SmallTest
117 public void testManagedProfilesAreNotAddedToVpn() {
118 setMockedUsers(primaryUser, managedProfileA);
119
Robin Lee17e61832016-05-09 13:46:28 +0100120 final Vpn vpn = new MockVpn(primaryUser.id);
Robin Lee4d03abc2016-05-09 12:32:27 +0100121 final Set<UidRange> ranges = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
122 null, null);
123
124 assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
125 UidRange.createForUser(primaryUser.id)
126 })), ranges);
127 }
128
129 @SmallTest
130 public void testAddUserToVpnOnlyAddsOneUser() {
131 setMockedUsers(primaryUser, restrictedProfileA, managedProfileA);
132
Robin Lee17e61832016-05-09 13:46:28 +0100133 final Vpn vpn = new MockVpn(primaryUser.id);
Robin Lee4d03abc2016-05-09 12:32:27 +0100134 final Set<UidRange> ranges = new ArraySet<>();
135 vpn.addUserToRanges(ranges, primaryUser.id, null, null);
136
137 assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
138 UidRange.createForUser(primaryUser.id)
139 })), ranges);
140 }
141
142 @SmallTest
143 public void testUidWhiteAndBlacklist() throws Exception {
Robin Lee17e61832016-05-09 13:46:28 +0100144 final Vpn vpn = new MockVpn(primaryUser.id);
Robin Lee4d03abc2016-05-09 12:32:27 +0100145 final UidRange user = UidRange.createForUser(primaryUser.id);
Robin Lee17e61832016-05-09 13:46:28 +0100146 final String[] packages = {PKGS[0], PKGS[1], PKGS[2]};
Robin Lee4d03abc2016-05-09 12:32:27 +0100147
148 // Whitelist
149 final Set<UidRange> allow = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
Robin Lee17e61832016-05-09 13:46:28 +0100150 Arrays.asList(packages), null);
Robin Lee4d03abc2016-05-09 12:32:27 +0100151 assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
Robin Lee17e61832016-05-09 13:46:28 +0100152 new UidRange(user.start + PKG_UIDS[0], user.start + PKG_UIDS[0]),
153 new UidRange(user.start + PKG_UIDS[1], user.start + PKG_UIDS[2])
Robin Lee4d03abc2016-05-09 12:32:27 +0100154 })), allow);
155
156 // Blacklist
157 final Set<UidRange> disallow = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
Robin Lee17e61832016-05-09 13:46:28 +0100158 null, Arrays.asList(packages));
Robin Lee4d03abc2016-05-09 12:32:27 +0100159 assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
Robin Lee17e61832016-05-09 13:46:28 +0100160 new UidRange(user.start, user.start + PKG_UIDS[0] - 1),
161 new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
162 /* Empty range between UIDS[1] and UIDS[2], should be excluded, */
163 new UidRange(user.start + PKG_UIDS[2] + 1, user.stop)
Robin Lee4d03abc2016-05-09 12:32:27 +0100164 })), disallow);
165 }
166
Robin Lee17e61832016-05-09 13:46:28 +0100167 @SmallTest
168 public void testLockdownChangingPackage() throws Exception {
169 final MockVpn vpn = new MockVpn(primaryUser.id);
170 final UidRange user = UidRange.createForUser(primaryUser.id);
171
172 // Default state.
173 vpn.assertUnblocked(user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
174
175 // Set always-on without lockdown.
176 assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false));
177 vpn.assertUnblocked(user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
178
179 // Set always-on with lockdown.
180 assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true));
181 verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
182 new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
183 new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
184 }));
185 vpn.assertBlocked(user.start + PKG_UIDS[0], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
186 vpn.assertUnblocked(user.start + PKG_UIDS[1]);
187
188 // Switch to another app.
189 assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true));
190 verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
191 new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
192 new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
193 }));
194 verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
195 new UidRange(user.start, user.start + PKG_UIDS[3] - 1),
196 new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
197 }));
198 vpn.assertBlocked(user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]);
199 vpn.assertUnblocked(user.start + PKG_UIDS[3]);
200 }
201
202 @SmallTest
203 public void testLockdownAddingAProfile() throws Exception {
204 final MockVpn vpn = new MockVpn(primaryUser.id);
205 setMockedUsers(primaryUser);
206
207 // Make a copy of the restricted profile, as we're going to mark it deleted halfway through.
208 final UserInfo tempProfile = new UserInfo(restrictedProfileA.id, restrictedProfileA.name,
209 restrictedProfileA.flags);
210 tempProfile.restrictedProfileParentId = primaryUser.id;
211
212 final UidRange user = UidRange.createForUser(primaryUser.id);
213 final UidRange profile = UidRange.createForUser(tempProfile.id);
214
215 // Set lockdown.
216 assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true));
217 verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
218 new UidRange(user.start, user.start + PKG_UIDS[3] - 1),
219 new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
220 }));
221
222 // Verify restricted user isn't affected at first.
223 vpn.assertUnblocked(profile.start + PKG_UIDS[0]);
224
225 // Add the restricted user.
226 setMockedUsers(primaryUser, tempProfile);
227 vpn.onUserAdded(tempProfile.id);
228 verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
229 new UidRange(profile.start, profile.start + PKG_UIDS[3] - 1),
230 new UidRange(profile.start + PKG_UIDS[3] + 1, profile.stop)
231 }));
232
233 // Remove the restricted user.
234 tempProfile.partial = true;
235 vpn.onUserRemoved(tempProfile.id);
236 verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
237 new UidRange(profile.start, profile.start + PKG_UIDS[3] - 1),
238 new UidRange(profile.start + PKG_UIDS[3] + 1, profile.stop)
239 }));
240 }
241
Robin Lee4d03abc2016-05-09 12:32:27 +0100242 /**
Robin Lee17e61832016-05-09 13:46:28 +0100243 * A subclass of {@link Vpn} with some of the fields pre-mocked.
Robin Lee4d03abc2016-05-09 12:32:27 +0100244 */
Robin Lee17e61832016-05-09 13:46:28 +0100245 private class MockVpn extends Vpn {
246 public MockVpn(@UserIdInt int userId) {
247 super(Looper.myLooper(), mContext, mNetService, userId);
248 }
249
250 public void assertBlocked(int... uids) {
251 for (int uid : uids) {
252 assertTrue("Uid " + uid + " should be blocked", isBlockingUid(uid));
253 }
254 }
255
256 public void assertUnblocked(int... uids) {
257 for (int uid : uids) {
258 assertFalse("Uid " + uid + " should not be blocked", isBlockingUid(uid));
259 }
260 }
Robin Lee4d03abc2016-05-09 12:32:27 +0100261 }
262
263 /**
264 * Populate {@link #mUserManager} with a list of fake users.
265 */
266 private void setMockedUsers(UserInfo... users) {
267 final Map<Integer, UserInfo> userMap = new ArrayMap<>();
268 for (UserInfo user : users) {
269 userMap.put(user.id, user);
270 }
271
Robin Lee17e61832016-05-09 13:46:28 +0100272 /**
273 * @see UserManagerService#getUsers(boolean)
274 */
Robin Lee4d03abc2016-05-09 12:32:27 +0100275 doAnswer(invocation -> {
Robin Lee17e61832016-05-09 13:46:28 +0100276 final boolean excludeDying = (boolean) invocation.getArguments()[0];
277 final ArrayList<UserInfo> result = new ArrayList<>(users.length);
278 for (UserInfo ui : users) {
279 if (!excludeDying || (ui.isEnabled() && !ui.partial)) {
280 result.add(ui);
281 }
282 }
283 return result;
284 }).when(mUserManager).getUsers(anyBoolean());
Robin Lee4d03abc2016-05-09 12:32:27 +0100285
286 doAnswer(invocation -> {
287 final int id = (int) invocation.getArguments()[0];
288 return userMap.get(id);
289 }).when(mUserManager).getUserInfo(anyInt());
290
291 doAnswer(invocation -> {
292 final int id = (int) invocation.getArguments()[0];
293 return (userMap.get(id).flags & UserInfo.FLAG_ADMIN) != 0;
294 }).when(mUserManager).canHaveRestrictedProfile(anyInt());
295 }
296
297 /**
298 * Populate {@link #mPackageManager} with a fake packageName-to-UID mapping.
299 */
300 private void setMockedPackages(final Map<String, Integer> packages) {
301 try {
302 doAnswer(invocation -> {
303 final String appName = (String) invocation.getArguments()[0];
304 final int userId = (int) invocation.getArguments()[1];
305 return UserHandle.getUid(userId, packages.get(appName));
306 }).when(mPackageManager).getPackageUidAsUser(anyString(), anyInt());
307 } catch (Exception e) {
308 }
309 }
310}