Home | History | Annotate | Download | only in connectivity
      1 /*
      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 
     17 package com.android.server.connectivity;
     18 
     19 import static android.content.pm.UserInfo.FLAG_ADMIN;
     20 import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
     21 import static android.content.pm.UserInfo.FLAG_PRIMARY;
     22 import static android.content.pm.UserInfo.FLAG_RESTRICTED;
     23 import static org.mockito.AdditionalMatchers.*;
     24 import static org.mockito.Mockito.*;
     25 
     26 import android.annotation.UserIdInt;
     27 import android.app.AppOpsManager;
     28 import android.app.NotificationManager;
     29 import android.content.Context;
     30 import android.content.pm.ApplicationInfo;
     31 import android.content.pm.PackageManager;
     32 import android.content.pm.ResolveInfo;
     33 import android.content.pm.ServiceInfo;
     34 import android.content.pm.UserInfo;
     35 import android.net.NetworkInfo.DetailedState;
     36 import android.net.UidRange;
     37 import android.net.VpnService;
     38 import android.os.Build.VERSION_CODES;
     39 import android.os.Bundle;
     40 import android.os.INetworkManagementService;
     41 import android.os.Looper;
     42 import android.os.UserHandle;
     43 import android.os.UserManager;
     44 import android.test.AndroidTestCase;
     45 import android.test.suitebuilder.annotation.SmallTest;
     46 import android.util.ArrayMap;
     47 import android.util.ArraySet;
     48 
     49 import com.android.internal.net.VpnConfig;
     50 
     51 import org.mockito.Answers;
     52 import org.mockito.InOrder;
     53 import org.mockito.Mock;
     54 import org.mockito.MockitoAnnotations;
     55 
     56 import java.util.ArrayList;
     57 import java.util.Arrays;
     58 import java.util.Collections;
     59 import java.util.Map;
     60 import java.util.Set;
     61 
     62 /**
     63  * Tests for {@link Vpn}.
     64  *
     65  * Build, install and run with:
     66  *  runtest --path java/com/android/server/connectivity/VpnTest.java
     67  */
     68 public class VpnTest extends AndroidTestCase {
     69     private static final String TAG = "VpnTest";
     70 
     71     // Mock users
     72     static final UserInfo primaryUser = new UserInfo(27, "Primary", FLAG_ADMIN | FLAG_PRIMARY);
     73     static final UserInfo secondaryUser = new UserInfo(15, "Secondary", FLAG_ADMIN);
     74     static final UserInfo restrictedProfileA = new UserInfo(40, "RestrictedA", FLAG_RESTRICTED);
     75     static final UserInfo restrictedProfileB = new UserInfo(42, "RestrictedB", FLAG_RESTRICTED);
     76     static final UserInfo managedProfileA = new UserInfo(45, "ManagedA", FLAG_MANAGED_PROFILE);
     77     static {
     78         restrictedProfileA.restrictedProfileParentId = primaryUser.id;
     79         restrictedProfileB.restrictedProfileParentId = secondaryUser.id;
     80         managedProfileA.profileGroupId = primaryUser.id;
     81     }
     82 
     83     /**
     84      * Names and UIDs for some fake packages. Important points:
     85      *  - UID is ordered increasing.
     86      *  - One pair of packages have consecutive UIDs.
     87      */
     88     static final String[] PKGS = {"com.example", "org.example", "net.example", "web.vpn"};
     89     static final int[] PKG_UIDS = {66, 77, 78, 400};
     90 
     91     // Mock packages
     92     static final Map<String, Integer> mPackages = new ArrayMap<>();
     93     static {
     94         for (int i = 0; i < PKGS.length; i++) {
     95             mPackages.put(PKGS[i], PKG_UIDS[i]);
     96         }
     97     }
     98 
     99     @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Context mContext;
    100     @Mock private UserManager mUserManager;
    101     @Mock private PackageManager mPackageManager;
    102     @Mock private INetworkManagementService mNetService;
    103     @Mock private AppOpsManager mAppOps;
    104     @Mock private NotificationManager mNotificationManager;
    105     @Mock private Vpn.SystemServices mSystemServices;
    106 
    107     @Override
    108     public void setUp() throws Exception {
    109         MockitoAnnotations.initMocks(this);
    110 
    111         when(mContext.getPackageManager()).thenReturn(mPackageManager);
    112         setMockedPackages(mPackages);
    113 
    114         when(mContext.getPackageName()).thenReturn(Vpn.class.getPackage().getName());
    115         when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager);
    116         when(mContext.getSystemService(eq(Context.APP_OPS_SERVICE))).thenReturn(mAppOps);
    117         when(mContext.getSystemService(eq(Context.NOTIFICATION_SERVICE)))
    118                 .thenReturn(mNotificationManager);
    119 
    120         // Used by {@link Notification.Builder}
    121         ApplicationInfo applicationInfo = new ApplicationInfo();
    122         applicationInfo.targetSdkVersion = VERSION_CODES.CUR_DEVELOPMENT;
    123         when(mContext.getApplicationInfo()).thenReturn(applicationInfo);
    124 
    125         doNothing().when(mNetService).registerObserver(any());
    126     }
    127 
    128     @SmallTest
    129     public void testRestrictedProfilesAreAddedToVpn() {
    130         setMockedUsers(primaryUser, secondaryUser, restrictedProfileA, restrictedProfileB);
    131 
    132         final Vpn vpn = createVpn(primaryUser.id);
    133         final Set<UidRange> ranges = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
    134                 null, null);
    135 
    136         assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
    137             UidRange.createForUser(primaryUser.id),
    138             UidRange.createForUser(restrictedProfileA.id)
    139         })), ranges);
    140     }
    141 
    142     @SmallTest
    143     public void testManagedProfilesAreNotAddedToVpn() {
    144         setMockedUsers(primaryUser, managedProfileA);
    145 
    146         final Vpn vpn = createVpn(primaryUser.id);
    147         final Set<UidRange> ranges = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
    148                 null, null);
    149 
    150         assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
    151             UidRange.createForUser(primaryUser.id)
    152         })), ranges);
    153     }
    154 
    155     @SmallTest
    156     public void testAddUserToVpnOnlyAddsOneUser() {
    157         setMockedUsers(primaryUser, restrictedProfileA, managedProfileA);
    158 
    159         final Vpn vpn = createVpn(primaryUser.id);
    160         final Set<UidRange> ranges = new ArraySet<>();
    161         vpn.addUserToRanges(ranges, primaryUser.id, null, null);
    162 
    163         assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
    164             UidRange.createForUser(primaryUser.id)
    165         })), ranges);
    166     }
    167 
    168     @SmallTest
    169     public void testUidWhiteAndBlacklist() throws Exception {
    170         final Vpn vpn = createVpn(primaryUser.id);
    171         final UidRange user = UidRange.createForUser(primaryUser.id);
    172         final String[] packages = {PKGS[0], PKGS[1], PKGS[2]};
    173 
    174         // Whitelist
    175         final Set<UidRange> allow = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
    176                 Arrays.asList(packages), null);
    177         assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
    178             new UidRange(user.start + PKG_UIDS[0], user.start + PKG_UIDS[0]),
    179             new UidRange(user.start + PKG_UIDS[1], user.start + PKG_UIDS[2])
    180         })), allow);
    181 
    182         // Blacklist
    183         final Set<UidRange> disallow = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
    184                 null, Arrays.asList(packages));
    185         assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
    186             new UidRange(user.start, user.start + PKG_UIDS[0] - 1),
    187             new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
    188             /* Empty range between UIDS[1] and UIDS[2], should be excluded, */
    189             new UidRange(user.start + PKG_UIDS[2] + 1, user.stop)
    190         })), disallow);
    191     }
    192 
    193     @SmallTest
    194     public void testLockdownChangingPackage() throws Exception {
    195         final Vpn vpn = createVpn(primaryUser.id);
    196         final UidRange user = UidRange.createForUser(primaryUser.id);
    197 
    198         // Default state.
    199         assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
    200 
    201         // Set always-on without lockdown.
    202         assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false));
    203         assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
    204 
    205         // Set always-on with lockdown.
    206         assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true));
    207         verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
    208             new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
    209             new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
    210         }));
    211         assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
    212         assertUnblocked(vpn, user.start + PKG_UIDS[1]);
    213 
    214         // Switch to another app.
    215         assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true));
    216         verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
    217             new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
    218             new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
    219         }));
    220         verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
    221             new UidRange(user.start, user.start + PKG_UIDS[3] - 1),
    222             new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
    223         }));
    224         assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]);
    225         assertUnblocked(vpn, user.start + PKG_UIDS[3]);
    226     }
    227 
    228     @SmallTest
    229     public void testLockdownAddingAProfile() throws Exception {
    230         final Vpn vpn = createVpn(primaryUser.id);
    231         setMockedUsers(primaryUser);
    232 
    233         // Make a copy of the restricted profile, as we're going to mark it deleted halfway through.
    234         final UserInfo tempProfile = new UserInfo(restrictedProfileA.id, restrictedProfileA.name,
    235                 restrictedProfileA.flags);
    236         tempProfile.restrictedProfileParentId = primaryUser.id;
    237 
    238         final UidRange user = UidRange.createForUser(primaryUser.id);
    239         final UidRange profile = UidRange.createForUser(tempProfile.id);
    240 
    241         // Set lockdown.
    242         assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true));
    243         verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
    244             new UidRange(user.start, user.start + PKG_UIDS[3] - 1),
    245             new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
    246         }));
    247 
    248         // Verify restricted user isn't affected at first.
    249         assertUnblocked(vpn, profile.start + PKG_UIDS[0]);
    250 
    251         // Add the restricted user.
    252         setMockedUsers(primaryUser, tempProfile);
    253         vpn.onUserAdded(tempProfile.id);
    254         verify(mNetService).setAllowOnlyVpnForUids(eq(true), 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         // Remove the restricted user.
    260         tempProfile.partial = true;
    261         vpn.onUserRemoved(tempProfile.id);
    262         verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
    263             new UidRange(profile.start, profile.start + PKG_UIDS[3] - 1),
    264             new UidRange(profile.start + PKG_UIDS[3] + 1, profile.stop)
    265         }));
    266     }
    267 
    268     @SmallTest
    269     public void testLockdownRuleRepeatability() throws Exception {
    270         final Vpn vpn = createVpn(primaryUser.id);
    271 
    272         // Given legacy lockdown is already enabled,
    273         vpn.setLockdown(true);
    274         verify(mNetService, times(1)).setAllowOnlyVpnForUids(
    275                 eq(true), aryEq(new UidRange[] {UidRange.createForUser(primaryUser.id)}));
    276 
    277         // Enabling legacy lockdown twice should do nothing.
    278         vpn.setLockdown(true);
    279         verify(mNetService, times(1)).setAllowOnlyVpnForUids(anyBoolean(), any(UidRange[].class));
    280 
    281         // And disabling should remove the rules exactly once.
    282         vpn.setLockdown(false);
    283         verify(mNetService, times(1)).setAllowOnlyVpnForUids(
    284                 eq(false), aryEq(new UidRange[] {UidRange.createForUser(primaryUser.id)}));
    285 
    286         // Removing the lockdown again should have no effect.
    287         vpn.setLockdown(false);
    288         verify(mNetService, times(2)).setAllowOnlyVpnForUids(anyBoolean(), any(UidRange[].class));
    289     }
    290 
    291     @SmallTest
    292     public void testLockdownRuleReversibility() throws Exception {
    293         final Vpn vpn = createVpn(primaryUser.id);
    294 
    295         final UidRange[] entireUser = {
    296             UidRange.createForUser(primaryUser.id)
    297         };
    298         final UidRange[] exceptPkg0 = {
    299             new UidRange(entireUser[0].start, entireUser[0].start + PKG_UIDS[0] - 1),
    300             new UidRange(entireUser[0].start + PKG_UIDS[0] + 1, entireUser[0].stop)
    301         };
    302 
    303         final InOrder order = inOrder(mNetService);
    304 
    305         // Given lockdown is enabled with no package (legacy VPN),
    306         vpn.setLockdown(true);
    307         order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(entireUser));
    308 
    309         // When a new VPN package is set the rules should change to cover that package.
    310         vpn.prepare(null, PKGS[0]);
    311         order.verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(entireUser));
    312         order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(exceptPkg0));
    313 
    314         // When that VPN package is unset, everything should be undone again in reverse.
    315         vpn.prepare(null, VpnConfig.LEGACY_VPN);
    316         order.verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(exceptPkg0));
    317         order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(entireUser));
    318     }
    319 
    320     @SmallTest
    321     public void testIsAlwaysOnPackageSupported() throws Exception {
    322         final Vpn vpn = createVpn(primaryUser.id);
    323 
    324         ApplicationInfo appInfo = new ApplicationInfo();
    325         when(mPackageManager.getApplicationInfoAsUser(eq(PKGS[0]), anyInt(), eq(primaryUser.id)))
    326                 .thenReturn(appInfo);
    327 
    328         ServiceInfo svcInfo = new ServiceInfo();
    329         ResolveInfo resInfo = new ResolveInfo();
    330         resInfo.serviceInfo = svcInfo;
    331         when(mPackageManager.queryIntentServicesAsUser(any(), eq(PackageManager.GET_META_DATA),
    332                 eq(primaryUser.id)))
    333                 .thenReturn(Collections.singletonList(resInfo));
    334 
    335         // null package name should return false
    336         assertFalse(vpn.isAlwaysOnPackageSupported(null));
    337 
    338         // Pre-N apps are not supported
    339         appInfo.targetSdkVersion = VERSION_CODES.M;
    340         assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0]));
    341 
    342         // N+ apps are supported by default
    343         appInfo.targetSdkVersion = VERSION_CODES.N;
    344         assertTrue(vpn.isAlwaysOnPackageSupported(PKGS[0]));
    345 
    346         // Apps that opt out explicitly are not supported
    347         appInfo.targetSdkVersion = VERSION_CODES.CUR_DEVELOPMENT;
    348         Bundle metaData = new Bundle();
    349         metaData.putBoolean(VpnService.SERVICE_META_DATA_SUPPORTS_ALWAYS_ON, false);
    350         svcInfo.metaData = metaData;
    351         assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0]));
    352     }
    353 
    354     @SmallTest
    355     public void testNotificationShownForAlwaysOnApp() {
    356         final UserHandle userHandle = UserHandle.of(primaryUser.id);
    357         final Vpn vpn = createVpn(primaryUser.id);
    358         setMockedUsers(primaryUser);
    359 
    360         final InOrder order = inOrder(mNotificationManager);
    361 
    362         // Don't show a notification for regular disconnected states.
    363         vpn.updateState(DetailedState.DISCONNECTED, TAG);
    364         order.verify(mNotificationManager, atLeastOnce())
    365                 .cancelAsUser(anyString(), anyInt(), eq(userHandle));
    366 
    367         // Start showing a notification for disconnected once always-on.
    368         vpn.setAlwaysOnPackage(PKGS[0], false);
    369         order.verify(mNotificationManager)
    370                 .notifyAsUser(anyString(), anyInt(), any(), eq(userHandle));
    371 
    372         // Stop showing the notification once connected.
    373         vpn.updateState(DetailedState.CONNECTED, TAG);
    374         order.verify(mNotificationManager).cancelAsUser(anyString(), anyInt(), eq(userHandle));
    375 
    376         // Show the notification if we disconnect again.
    377         vpn.updateState(DetailedState.DISCONNECTED, TAG);
    378         order.verify(mNotificationManager)
    379                 .notifyAsUser(anyString(), anyInt(), any(), eq(userHandle));
    380 
    381         // Notification should be cleared after unsetting always-on package.
    382         vpn.setAlwaysOnPackage(null, false);
    383         order.verify(mNotificationManager).cancelAsUser(anyString(), anyInt(), eq(userHandle));
    384     }
    385 
    386     /**
    387      * Mock some methods of vpn object.
    388      */
    389     private Vpn createVpn(@UserIdInt int userId) {
    390         return new Vpn(Looper.myLooper(), mContext, mNetService, userId, mSystemServices);
    391     }
    392 
    393     private static void assertBlocked(Vpn vpn, int... uids) {
    394         for (int uid : uids) {
    395             assertTrue("Uid " + uid + " should be blocked", vpn.isBlockingUid(uid));
    396         }
    397     }
    398 
    399     private static void assertUnblocked(Vpn vpn, int... uids) {
    400         for (int uid : uids) {
    401             assertFalse("Uid " + uid + " should not be blocked", vpn.isBlockingUid(uid));
    402         }
    403     }
    404 
    405     /**
    406      * Populate {@link #mUserManager} with a list of fake users.
    407      */
    408     private void setMockedUsers(UserInfo... users) {
    409         final Map<Integer, UserInfo> userMap = new ArrayMap<>();
    410         for (UserInfo user : users) {
    411             userMap.put(user.id, user);
    412         }
    413 
    414         /**
    415          * @see UserManagerService#getUsers(boolean)
    416          */
    417         doAnswer(invocation -> {
    418             final boolean excludeDying = (boolean) invocation.getArguments()[0];
    419             final ArrayList<UserInfo> result = new ArrayList<>(users.length);
    420             for (UserInfo ui : users) {
    421                 if (!excludeDying || (ui.isEnabled() && !ui.partial)) {
    422                     result.add(ui);
    423                 }
    424             }
    425             return result;
    426         }).when(mUserManager).getUsers(anyBoolean());
    427 
    428         doAnswer(invocation -> {
    429             final int id = (int) invocation.getArguments()[0];
    430             return userMap.get(id);
    431         }).when(mUserManager).getUserInfo(anyInt());
    432 
    433         doAnswer(invocation -> {
    434             final int id = (int) invocation.getArguments()[0];
    435             return (userMap.get(id).flags & UserInfo.FLAG_ADMIN) != 0;
    436         }).when(mUserManager).canHaveRestrictedProfile(anyInt());
    437     }
    438 
    439     /**
    440      * Populate {@link #mPackageManager} with a fake packageName-to-UID mapping.
    441      */
    442     private void setMockedPackages(final Map<String, Integer> packages) {
    443         try {
    444             doAnswer(invocation -> {
    445                 final String appName = (String) invocation.getArguments()[0];
    446                 final int userId = (int) invocation.getArguments()[1];
    447                 return UserHandle.getUid(userId, packages.get(appName));
    448             }).when(mPackageManager).getPackageUidAsUser(anyString(), anyInt());
    449         } catch (Exception e) {
    450         }
    451     }
    452 }
    453