blob: efe6fec6fc7d25e1d6a390220faf8f0a5d6fbabe [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;
Tony Makde7f7d12016-06-30 11:19:20 +010028import android.app.NotificationManager;
Robin Lee4d03abc2016-05-09 12:32:27 +010029import android.content.Context;
Robin Leeb8c2a2b2017-03-10 16:17:06 +000030import android.content.pm.ApplicationInfo;
Robin Lee4d03abc2016-05-09 12:32:27 +010031import android.content.pm.PackageManager;
32import android.content.pm.UserInfo;
Tony Makde7f7d12016-06-30 11:19:20 +010033import android.net.NetworkInfo.DetailedState;
Robin Lee4d03abc2016-05-09 12:32:27 +010034import android.net.UidRange;
Robin Leeb8c2a2b2017-03-10 16:17:06 +000035import android.os.Build;
Robin Lee4d03abc2016-05-09 12:32:27 +010036import android.os.INetworkManagementService;
37import android.os.Looper;
38import android.os.UserHandle;
39import android.os.UserManager;
40import android.test.AndroidTestCase;
41import android.test.suitebuilder.annotation.SmallTest;
42import android.util.ArrayMap;
43import android.util.ArraySet;
44
45import java.util.ArrayList;
46import java.util.Arrays;
47import java.util.Map;
48import java.util.Set;
49
Robin Leeb8c2a2b2017-03-10 16:17:06 +000050import org.mockito.Answers;
Tony Makde7f7d12016-06-30 11:19:20 +010051import org.mockito.ArgumentCaptor;
52import org.mockito.InOrder;
Robin Lee4d03abc2016-05-09 12:32:27 +010053import org.mockito.Mock;
54import org.mockito.MockitoAnnotations;
55
56/**
57 * Tests for {@link Vpn}.
58 *
59 * Build, install and run with:
60 * runtest --path src/com/android/server/connectivity/VpnTest.java
61 */
62public class VpnTest extends AndroidTestCase {
63 private static final String TAG = "VpnTest";
64
65 // Mock users
66 static final UserInfo primaryUser = new UserInfo(27, "Primary", FLAG_ADMIN | FLAG_PRIMARY);
67 static final UserInfo secondaryUser = new UserInfo(15, "Secondary", FLAG_ADMIN);
68 static final UserInfo restrictedProfileA = new UserInfo(40, "RestrictedA", FLAG_RESTRICTED);
69 static final UserInfo restrictedProfileB = new UserInfo(42, "RestrictedB", FLAG_RESTRICTED);
70 static final UserInfo managedProfileA = new UserInfo(45, "ManagedA", FLAG_MANAGED_PROFILE);
71 static {
72 restrictedProfileA.restrictedProfileParentId = primaryUser.id;
73 restrictedProfileB.restrictedProfileParentId = secondaryUser.id;
74 managedProfileA.profileGroupId = primaryUser.id;
75 }
76
Robin Lee17e61832016-05-09 13:46:28 +010077 /**
78 * Names and UIDs for some fake packages. Important points:
79 * - UID is ordered increasing.
80 * - One pair of packages have consecutive UIDs.
81 */
82 static final String[] PKGS = {"com.example", "org.example", "net.example", "web.vpn"};
83 static final int[] PKG_UIDS = {66, 77, 78, 400};
84
85 // Mock packages
86 static final Map<String, Integer> mPackages = new ArrayMap<>();
87 static {
88 for (int i = 0; i < PKGS.length; i++) {
89 mPackages.put(PKGS[i], PKG_UIDS[i]);
90 }
91 }
92
Robin Leeb8c2a2b2017-03-10 16:17:06 +000093 @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Context mContext;
Robin Lee4d03abc2016-05-09 12:32:27 +010094 @Mock private UserManager mUserManager;
95 @Mock private PackageManager mPackageManager;
96 @Mock private INetworkManagementService mNetService;
Robin Lee17e61832016-05-09 13:46:28 +010097 @Mock private AppOpsManager mAppOps;
Tony Makde7f7d12016-06-30 11:19:20 +010098 @Mock private NotificationManager mNotificationManager;
Robin Leeb8c2a2b2017-03-10 16:17:06 +000099 @Mock private Vpn.SystemServices mSystemServices;
Robin Lee4d03abc2016-05-09 12:32:27 +0100100
101 @Override
102 public void setUp() throws Exception {
103 MockitoAnnotations.initMocks(this);
104 when(mContext.getPackageManager()).thenReturn(mPackageManager);
Robin Lee17e61832016-05-09 13:46:28 +0100105 setMockedPackages(mPackages);
Tony Makde7f7d12016-06-30 11:19:20 +0100106 when(mContext.getPackageName()).thenReturn(Vpn.class.getPackage().getName());
Robin Lee4d03abc2016-05-09 12:32:27 +0100107 when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager);
Robin Lee17e61832016-05-09 13:46:28 +0100108 when(mContext.getSystemService(eq(Context.APP_OPS_SERVICE))).thenReturn(mAppOps);
Tony Makde7f7d12016-06-30 11:19:20 +0100109 when(mContext.getSystemService(eq(Context.NOTIFICATION_SERVICE)))
110 .thenReturn(mNotificationManager);
Robin Leeb8c2a2b2017-03-10 16:17:06 +0000111
112 // Used by {@link Notification.Builder}
113 ApplicationInfo applicationInfo = new ApplicationInfo();
114 applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
115 when(mContext.getApplicationInfo()).thenReturn(applicationInfo);
116
Robin Lee4d03abc2016-05-09 12:32:27 +0100117 doNothing().when(mNetService).registerObserver(any());
118 }
119
120 @SmallTest
121 public void testRestrictedProfilesAreAddedToVpn() {
122 setMockedUsers(primaryUser, secondaryUser, restrictedProfileA, restrictedProfileB);
123
Robin Leeb8c2a2b2017-03-10 16:17:06 +0000124 final Vpn vpn = createVpn(primaryUser.id);
Robin Lee4d03abc2016-05-09 12:32:27 +0100125 final Set<UidRange> ranges = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
126 null, null);
127
128 assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
129 UidRange.createForUser(primaryUser.id),
130 UidRange.createForUser(restrictedProfileA.id)
131 })), ranges);
132 }
133
134 @SmallTest
135 public void testManagedProfilesAreNotAddedToVpn() {
136 setMockedUsers(primaryUser, managedProfileA);
137
Robin Leeb8c2a2b2017-03-10 16:17:06 +0000138 final Vpn vpn = createVpn(primaryUser.id);
Robin Lee4d03abc2016-05-09 12:32:27 +0100139 final Set<UidRange> ranges = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
140 null, null);
141
142 assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
143 UidRange.createForUser(primaryUser.id)
144 })), ranges);
145 }
146
147 @SmallTest
148 public void testAddUserToVpnOnlyAddsOneUser() {
149 setMockedUsers(primaryUser, restrictedProfileA, managedProfileA);
150
Robin Leeb8c2a2b2017-03-10 16:17:06 +0000151 final Vpn vpn = createVpn(primaryUser.id);
Robin Lee4d03abc2016-05-09 12:32:27 +0100152 final Set<UidRange> ranges = new ArraySet<>();
153 vpn.addUserToRanges(ranges, primaryUser.id, null, null);
154
155 assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
156 UidRange.createForUser(primaryUser.id)
157 })), ranges);
158 }
159
160 @SmallTest
161 public void testUidWhiteAndBlacklist() throws Exception {
Robin Leeb8c2a2b2017-03-10 16:17:06 +0000162 final Vpn vpn = createVpn(primaryUser.id);
Robin Lee4d03abc2016-05-09 12:32:27 +0100163 final UidRange user = UidRange.createForUser(primaryUser.id);
Robin Lee17e61832016-05-09 13:46:28 +0100164 final String[] packages = {PKGS[0], PKGS[1], PKGS[2]};
Robin Lee4d03abc2016-05-09 12:32:27 +0100165
166 // Whitelist
167 final Set<UidRange> allow = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
Robin Lee17e61832016-05-09 13:46:28 +0100168 Arrays.asList(packages), null);
Robin Lee4d03abc2016-05-09 12:32:27 +0100169 assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
Robin Lee17e61832016-05-09 13:46:28 +0100170 new UidRange(user.start + PKG_UIDS[0], user.start + PKG_UIDS[0]),
171 new UidRange(user.start + PKG_UIDS[1], user.start + PKG_UIDS[2])
Robin Lee4d03abc2016-05-09 12:32:27 +0100172 })), allow);
173
174 // Blacklist
175 final Set<UidRange> disallow = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
Robin Lee17e61832016-05-09 13:46:28 +0100176 null, Arrays.asList(packages));
Robin Lee4d03abc2016-05-09 12:32:27 +0100177 assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
Robin Lee17e61832016-05-09 13:46:28 +0100178 new UidRange(user.start, user.start + PKG_UIDS[0] - 1),
179 new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
180 /* Empty range between UIDS[1] and UIDS[2], should be excluded, */
181 new UidRange(user.start + PKG_UIDS[2] + 1, user.stop)
Robin Lee4d03abc2016-05-09 12:32:27 +0100182 })), disallow);
183 }
184
Robin Lee17e61832016-05-09 13:46:28 +0100185 @SmallTest
186 public void testLockdownChangingPackage() throws Exception {
Robin Leeb8c2a2b2017-03-10 16:17:06 +0000187 final Vpn vpn = createVpn(primaryUser.id);
Robin Lee17e61832016-05-09 13:46:28 +0100188 final UidRange user = UidRange.createForUser(primaryUser.id);
189
190 // Default state.
Tony Makde7f7d12016-06-30 11:19:20 +0100191 assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
Robin Lee17e61832016-05-09 13:46:28 +0100192
193 // Set always-on without lockdown.
194 assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false));
Tony Makde7f7d12016-06-30 11:19:20 +0100195 assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
Robin Lee17e61832016-05-09 13:46:28 +0100196
197 // Set always-on with lockdown.
198 assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true));
199 verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
200 new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
201 new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
202 }));
Tony Makde7f7d12016-06-30 11:19:20 +0100203 assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
204 assertUnblocked(vpn, user.start + PKG_UIDS[1]);
Robin Lee17e61832016-05-09 13:46:28 +0100205
206 // Switch to another app.
207 assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true));
208 verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
209 new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
210 new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
211 }));
212 verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
213 new UidRange(user.start, user.start + PKG_UIDS[3] - 1),
214 new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
215 }));
Tony Makde7f7d12016-06-30 11:19:20 +0100216 assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]);
217 assertUnblocked(vpn, user.start + PKG_UIDS[3]);
Robin Lee17e61832016-05-09 13:46:28 +0100218 }
219
220 @SmallTest
221 public void testLockdownAddingAProfile() throws Exception {
Robin Leeb8c2a2b2017-03-10 16:17:06 +0000222 final Vpn vpn = createVpn(primaryUser.id);
Robin Lee17e61832016-05-09 13:46:28 +0100223 setMockedUsers(primaryUser);
224
225 // Make a copy of the restricted profile, as we're going to mark it deleted halfway through.
226 final UserInfo tempProfile = new UserInfo(restrictedProfileA.id, restrictedProfileA.name,
227 restrictedProfileA.flags);
228 tempProfile.restrictedProfileParentId = primaryUser.id;
229
230 final UidRange user = UidRange.createForUser(primaryUser.id);
231 final UidRange profile = UidRange.createForUser(tempProfile.id);
232
233 // Set lockdown.
234 assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true));
235 verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
236 new UidRange(user.start, user.start + PKG_UIDS[3] - 1),
237 new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
238 }));
239
240 // Verify restricted user isn't affected at first.
Tony Makde7f7d12016-06-30 11:19:20 +0100241 assertUnblocked(vpn, profile.start + PKG_UIDS[0]);
Robin Lee17e61832016-05-09 13:46:28 +0100242
243 // Add the restricted user.
244 setMockedUsers(primaryUser, tempProfile);
245 vpn.onUserAdded(tempProfile.id);
246 verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
247 new UidRange(profile.start, profile.start + PKG_UIDS[3] - 1),
248 new UidRange(profile.start + PKG_UIDS[3] + 1, profile.stop)
249 }));
250
251 // Remove the restricted user.
252 tempProfile.partial = true;
253 vpn.onUserRemoved(tempProfile.id);
254 verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
255 new UidRange(profile.start, profile.start + PKG_UIDS[3] - 1),
256 new UidRange(profile.start + PKG_UIDS[3] + 1, profile.stop)
257 }));
258 }
259
Tony Makde7f7d12016-06-30 11:19:20 +0100260 @SmallTest
261 public void testNotificationShownForAlwaysOnApp() {
Robin Leeb8c2a2b2017-03-10 16:17:06 +0000262 final UserHandle userHandle = UserHandle.of(primaryUser.id);
263 final Vpn vpn = createVpn(primaryUser.id);
Tony Makde7f7d12016-06-30 11:19:20 +0100264 setMockedUsers(primaryUser);
265
Robin Leeb8c2a2b2017-03-10 16:17:06 +0000266 final InOrder order = inOrder(mNotificationManager);
267
Tony Makde7f7d12016-06-30 11:19:20 +0100268 // Don't show a notification for regular disconnected states.
269 vpn.updateState(DetailedState.DISCONNECTED, TAG);
Robin Leeb8c2a2b2017-03-10 16:17:06 +0000270 order.verify(mNotificationManager, atLeastOnce())
271 .cancelAsUser(anyString(), anyInt(), eq(userHandle));
Tony Makde7f7d12016-06-30 11:19:20 +0100272
273 // Start showing a notification for disconnected once always-on.
274 vpn.setAlwaysOnPackage(PKGS[0], false);
Robin Leeb8c2a2b2017-03-10 16:17:06 +0000275 order.verify(mNotificationManager)
276 .notifyAsUser(anyString(), anyInt(), any(), eq(userHandle));
Tony Makde7f7d12016-06-30 11:19:20 +0100277
278 // Stop showing the notification once connected.
279 vpn.updateState(DetailedState.CONNECTED, TAG);
Robin Leeb8c2a2b2017-03-10 16:17:06 +0000280 order.verify(mNotificationManager).cancelAsUser(anyString(), anyInt(), eq(userHandle));
Tony Makde7f7d12016-06-30 11:19:20 +0100281
282 // Show the notification if we disconnect again.
283 vpn.updateState(DetailedState.DISCONNECTED, TAG);
Robin Leeb8c2a2b2017-03-10 16:17:06 +0000284 order.verify(mNotificationManager)
285 .notifyAsUser(anyString(), anyInt(), any(), eq(userHandle));
Tony Makde7f7d12016-06-30 11:19:20 +0100286
287 // Notification should be cleared after unsetting always-on package.
288 vpn.setAlwaysOnPackage(null, false);
Robin Leeb8c2a2b2017-03-10 16:17:06 +0000289 order.verify(mNotificationManager).cancelAsUser(anyString(), anyInt(), eq(userHandle));
Tony Makde7f7d12016-06-30 11:19:20 +0100290 }
291
Robin Lee4d03abc2016-05-09 12:32:27 +0100292 /**
Tony Makde7f7d12016-06-30 11:19:20 +0100293 * Mock some methods of vpn object.
Robin Lee4d03abc2016-05-09 12:32:27 +0100294 */
Robin Leeb8c2a2b2017-03-10 16:17:06 +0000295 private Vpn createVpn(@UserIdInt int userId) {
296 return new Vpn(Looper.myLooper(), mContext, mNetService, userId, mSystemServices);
Tony Makde7f7d12016-06-30 11:19:20 +0100297 }
Robin Lee17e61832016-05-09 13:46:28 +0100298
Tony Makde7f7d12016-06-30 11:19:20 +0100299 private static void assertBlocked(Vpn vpn, int... uids) {
300 for (int uid : uids) {
301 assertTrue("Uid " + uid + " should be blocked", vpn.isBlockingUid(uid));
302 }
303 }
304
305 private static void assertUnblocked(Vpn vpn, int... uids) {
306 for (int uid : uids) {
307 assertFalse("Uid " + uid + " should not be blocked", vpn.isBlockingUid(uid));
Robin Lee17e61832016-05-09 13:46:28 +0100308 }
Robin Lee4d03abc2016-05-09 12:32:27 +0100309 }
310
311 /**
312 * Populate {@link #mUserManager} with a list of fake users.
313 */
314 private void setMockedUsers(UserInfo... users) {
315 final Map<Integer, UserInfo> userMap = new ArrayMap<>();
316 for (UserInfo user : users) {
317 userMap.put(user.id, user);
318 }
319
Robin Lee17e61832016-05-09 13:46:28 +0100320 /**
321 * @see UserManagerService#getUsers(boolean)
322 */
Robin Lee4d03abc2016-05-09 12:32:27 +0100323 doAnswer(invocation -> {
Robin Lee17e61832016-05-09 13:46:28 +0100324 final boolean excludeDying = (boolean) invocation.getArguments()[0];
325 final ArrayList<UserInfo> result = new ArrayList<>(users.length);
326 for (UserInfo ui : users) {
327 if (!excludeDying || (ui.isEnabled() && !ui.partial)) {
328 result.add(ui);
329 }
330 }
331 return result;
332 }).when(mUserManager).getUsers(anyBoolean());
Robin Lee4d03abc2016-05-09 12:32:27 +0100333
334 doAnswer(invocation -> {
335 final int id = (int) invocation.getArguments()[0];
336 return userMap.get(id);
337 }).when(mUserManager).getUserInfo(anyInt());
338
339 doAnswer(invocation -> {
340 final int id = (int) invocation.getArguments()[0];
341 return (userMap.get(id).flags & UserInfo.FLAG_ADMIN) != 0;
342 }).when(mUserManager).canHaveRestrictedProfile(anyInt());
343 }
344
345 /**
346 * Populate {@link #mPackageManager} with a fake packageName-to-UID mapping.
347 */
348 private void setMockedPackages(final Map<String, Integer> packages) {
349 try {
350 doAnswer(invocation -> {
351 final String appName = (String) invocation.getArguments()[0];
352 final int userId = (int) invocation.getArguments()[1];
353 return UserHandle.getUid(userId, packages.get(appName));
354 }).when(mPackageManager).getPackageUidAsUser(anyString(), anyInt());
355 } catch (Exception e) {
356 }
357 }
358}