Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d166cae0 authored by Paul Hu's avatar Paul Hu Committed by Android (Google) Code Review
Browse files

Merge "Register APPS_ALLOWED_ON_RESTRICTED_NETWORKS setting observer" into sc-dev

parents 9a535ee8 e0269622
Loading
Loading
Loading
Loading
+87 −16
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import static android.Manifest.permission.UPDATE_DEVICE_STATS;
import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.net.ConnectivitySettingsManager.APPS_ALLOWED_ON_RESTRICTED_NETWORKS;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.os.Process.INVALID_UID;
import static android.os.Process.SYSTEM_UID;
@@ -39,6 +40,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.database.ContentObserver;
import android.net.ConnectivitySettingsManager;
import android.net.INetd;
import android.net.UidRange;
@@ -49,6 +51,7 @@ import android.os.ServiceSpecificException;
import android.os.SystemConfigManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.system.OsConstants;
import android.util.ArraySet;
import android.util.Log;
@@ -68,7 +71,6 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;


/**
 * A utility class to inform Netd of UID permisisons.
 * Does a mass update at boot and then monitors for app install/remove.
@@ -145,6 +147,22 @@ public class PermissionMonitor {
        public int getDeviceFirstSdkInt() {
            return Build.VERSION.DEVICE_INITIAL_SDK_INT;
        }

        /**
         * Get apps allowed to use restricted networks via ConnectivitySettingsManager.
         */
        public Set<String> getAppsAllowedOnRestrictedNetworks(@NonNull Context context) {
            return ConnectivitySettingsManager.getAppsAllowedOnRestrictedNetworks(context);
        }

        /**
         * Register ContentObserver for given Uri.
         */
        public void registerContentObserver(@NonNull Context context, @NonNull Uri uri,
                boolean notifyForDescendants, @NonNull ContentObserver observer) {
            context.getContentResolver().registerContentObserver(
                    uri, notifyForDescendants, observer);
        }
    }

    public PermissionMonitor(@NonNull final Context context, @NonNull final INetd netd) {
@@ -167,18 +185,30 @@ public class PermissionMonitor {
    public synchronized void startMonitoring() {
        log("Monitoring");

        final Context userAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
        final IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        intentFilter.addDataScheme("package");
        mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */).registerReceiver(
        userAllContext.registerReceiver(
                mIntentReceiver, intentFilter, null /* broadcastPermission */,
                null /* scheduler */);

        // Register APPS_ALLOWED_ON_RESTRICTED_NETWORKS setting observer
        mDeps.registerContentObserver(
                userAllContext,
                Settings.Secure.getUriFor(APPS_ALLOWED_ON_RESTRICTED_NETWORKS),
                false /* notifyForDescendants */,
                new ContentObserver(null) {
                    @Override
                    public void onChange(boolean selfChange) {
                        onSettingChanged();
                    }
                });

        // Read APPS_ALLOWED_ON_RESTRICTED_NETWORKS setting and update
        // mAppsAllowedOnRestrictedNetworks.
        updateAppsAllowedOnRestrictedNetworks(
                ConnectivitySettingsManager.getAppsAllowedOnRestrictedNetworks(mContext));
        updateAppsAllowedOnRestrictedNetworks(mDeps.getAppsAllowedOnRestrictedNetworks(mContext));

        List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS
                | MATCH_ANY_USER);
@@ -435,6 +465,20 @@ public class PermissionMonitor {
        mAllApps.add(UserHandle.getAppId(uid));
    }

    private Boolean highestUidNetworkPermission(int uid) {
        Boolean permission = null;
        final String[] packages = mPackageManager.getPackagesForUid(uid);
        if (!CollectionUtils.isEmpty(packages)) {
            for (String name : packages) {
                permission = highestPermissionForUid(permission, name);
                if (permission == SYSTEM) {
                    break;
                }
            }
        }
        return permission;
    }

    /**
     * Called when a package is removed.
     *
@@ -465,19 +509,14 @@ public class PermissionMonitor {
        }

        Map<Integer, Boolean> apps = new HashMap<>();
        Boolean permission = null;
        String[] packages = mPackageManager.getPackagesForUid(uid);
        if (packages != null && packages.length > 0) {
            for (String name : packages) {
                permission = highestPermissionForUid(permission, name);
        final Boolean permission = highestUidNetworkPermission(uid);
        if (permission == SYSTEM) {
            // An app with this UID still has the SYSTEM permission.
            // Therefore, this UID must already have the SYSTEM permission.
            // Nothing to do.
            return;
        }
            }
        }

        if (permission == mApps.get(uid)) {
            // The permissions of this UID have not changed. Nothing to do.
            return;
@@ -730,6 +769,38 @@ public class PermissionMonitor {
        return mVpnUidRanges.get(iface);
    }

    private synchronized void onSettingChanged() {
        // Step1. Update apps allowed to use restricted networks and compute the set of packages to
        // update.
        final Set<String> packagesToUpdate = new ArraySet<>(mAppsAllowedOnRestrictedNetworks);
        updateAppsAllowedOnRestrictedNetworks(mDeps.getAppsAllowedOnRestrictedNetworks(mContext));
        packagesToUpdate.addAll(mAppsAllowedOnRestrictedNetworks);

        final Map<Integer, Boolean> updatedApps = new HashMap<>();
        final Map<Integer, Boolean> removedApps = new HashMap<>();

        // Step2. For each package to update, find out its new permission.
        for (String app : packagesToUpdate) {
            final PackageInfo info = getPackageInfo(app);
            if (info == null || info.applicationInfo == null) continue;

            final int uid = info.applicationInfo.uid;
            final Boolean permission = highestUidNetworkPermission(uid);

            if (null == permission) {
                removedApps.put(uid, NETWORK); // Doesn't matter which permission is set here.
                mApps.remove(uid);
            } else {
                updatedApps.put(uid, permission);
                mApps.put(uid, permission);
            }
        }

        // Step3. Update or revoke permission for uids with netd.
        update(mUsers, updatedApps, true /* add */);
        update(mUsers, removedApps, false /* add */);
    }

    /** Dump info to dumpsys */
    public void dump(IndentingPrintWriter pw) {
        pw.println("Interface filtering rules:");
+104 −17
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_REQUIRED;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.net.ConnectivitySettingsManager.APPS_ALLOWED_ON_RESTRICTED_NETWORKS;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.os.Process.SYSTEM_UID;

@@ -44,8 +45,10 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.AdditionalMatchers.aryEq;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
@@ -62,6 +65,7 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.net.INetd;
import android.net.UidRange;
import android.net.Uri;
@@ -69,8 +73,6 @@ import android.os.Build;
import android.os.SystemConfigManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
import android.util.ArraySet;
import android.util.SparseIntArray;

@@ -78,9 +80,6 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import com.android.internal.util.test.FakeSettingsProvider;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -127,7 +126,6 @@ public class PermissionMonitorTest {
    @Mock private SystemConfigManager mSystemConfigManager;

    private PermissionMonitor mPermissionMonitor;
    private MockContentResolver mContentResolver;

    @Before
    public void setUp() throws Exception {
@@ -144,11 +142,7 @@ public class PermissionMonitorTest {
        final Context asUserCtx = mock(Context.class, AdditionalAnswers.delegatesTo(mContext));
        doReturn(UserHandle.ALL).when(asUserCtx).getUser();
        when(mContext.createContextAsUser(eq(UserHandle.ALL), anyInt())).thenReturn(asUserCtx);

        FakeSettingsProvider.clearSettingsProvider();
        mContentResolver = new MockContentResolver();
        mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
        when(mContext.getContentResolver()).thenReturn(mContentResolver);
        when(mDeps.getAppsAllowedOnRestrictedNetworks(any())).thenReturn(new ArraySet<>());

        mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService, mDeps));

@@ -156,11 +150,6 @@ public class PermissionMonitorTest {
        mPermissionMonitor.startMonitoring();
    }

    @After
    public void tearDown() throws Exception {
        FakeSettingsProvider.clearSettingsProvider();
    }

    private boolean hasRestrictedNetworkPermission(String partition, int targetSdkVersion, int uid,
            String... permissions) {
        return hasRestrictedNetworkPermission(
@@ -911,4 +900,102 @@ public class PermissionMonitorTest {
        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UNINSTALLED, new int[] { MOCK_UID1 });
    }

    @Test
    public void testAppsAllowedOnRestrictedNetworksChanged() throws Exception {
        final NetdMonitor mNetdMonitor = new NetdMonitor(mNetdService);
        final ArgumentCaptor<ContentObserver> captor =
                ArgumentCaptor.forClass(ContentObserver.class);
        verify(mDeps, times(1)).registerContentObserver(any(),
                argThat(uri -> uri.getEncodedPath().contains(APPS_ALLOWED_ON_RESTRICTED_NETWORKS)),
                anyBoolean(), captor.capture());
        final ContentObserver contentObserver = captor.getValue();

        mPermissionMonitor.onUserAdded(MOCK_USER1);
        // Prepare PackageInfo for MOCK_PACKAGE1
        final PackageInfo packageInfo = buildPackageInfo(
                false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1);
        packageInfo.packageName = MOCK_PACKAGE1;
        when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1), anyInt())).thenReturn(packageInfo);
        when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{MOCK_PACKAGE1});
        // Prepare PackageInfo for MOCK_PACKAGE2
        final PackageInfo packageInfo2 = buildPackageInfo(
                false /* hasSystemPermission */, MOCK_UID2, MOCK_USER1);
        packageInfo2.packageName = MOCK_PACKAGE2;
        when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE2), anyInt())).thenReturn(packageInfo2);
        when(mPackageManager.getPackagesForUid(MOCK_UID2)).thenReturn(new String[]{MOCK_PACKAGE2});

        // MOCK_PACKAGE1 is listed in setting that allow to use restricted networks, MOCK_UID1
        // should have SYSTEM permission.
        when(mDeps.getAppsAllowedOnRestrictedNetworks(any())).thenReturn(
                new ArraySet<>(new String[] { MOCK_PACKAGE1 }));
        contentObserver.onChange(true /* selfChange */);
        mNetdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1});
        mNetdMonitor.expectNoPermission(new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID2});

        // MOCK_PACKAGE2 is listed in setting that allow to use restricted networks, MOCK_UID2
        // should have SYSTEM permission but MOCK_UID1 should revoke permission.
        when(mDeps.getAppsAllowedOnRestrictedNetworks(any())).thenReturn(
                new ArraySet<>(new String[] { MOCK_PACKAGE2 }));
        contentObserver.onChange(true /* selfChange */);
        mNetdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID2});
        mNetdMonitor.expectNoPermission(new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1});

        // No app lists in setting, should revoke permission from all uids.
        when(mDeps.getAppsAllowedOnRestrictedNetworks(any())).thenReturn(new ArraySet<>());
        contentObserver.onChange(true /* selfChange */);
        mNetdMonitor.expectNoPermission(
                new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1, MOCK_UID2});
    }

    @Test
    public void testAppsAllowedOnRestrictedNetworksChangedWithSharedUid() throws Exception {
        final NetdMonitor mNetdMonitor = new NetdMonitor(mNetdService);
        final ArgumentCaptor<ContentObserver> captor =
                ArgumentCaptor.forClass(ContentObserver.class);
        verify(mDeps, times(1)).registerContentObserver(any(),
                argThat(uri -> uri.getEncodedPath().contains(APPS_ALLOWED_ON_RESTRICTED_NETWORKS)),
                anyBoolean(), captor.capture());
        final ContentObserver contentObserver = captor.getValue();

        mPermissionMonitor.onUserAdded(MOCK_USER1);
        // Prepare PackageInfo for MOCK_PACKAGE1 and MOCK_PACKAGE2 with shared uid MOCK_UID1.
        final PackageInfo packageInfo = systemPackageInfoWithPermissions(CHANGE_NETWORK_STATE);
        packageInfo.applicationInfo.uid = MOCK_USER1.getUid(MOCK_UID1);
        packageInfo.packageName = MOCK_PACKAGE1;
        final PackageInfo packageInfo2 = buildPackageInfo(
                false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1);
        packageInfo2.packageName = MOCK_PACKAGE2;
        when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1), anyInt())).thenReturn(packageInfo);
        when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE2), anyInt())).thenReturn(packageInfo2);
        when(mPackageManager.getPackagesForUid(MOCK_UID1))
                .thenReturn(new String[]{MOCK_PACKAGE1, MOCK_PACKAGE2});

        // MOCK_PACKAGE1 have CHANGE_NETWORK_STATE, MOCK_UID1 should have NETWORK permission.
        addPackageForUsers(new UserHandle[]{MOCK_USER1}, MOCK_PACKAGE1, MOCK_UID1);
        mNetdMonitor.expectPermission(NETWORK, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1});

        // MOCK_PACKAGE2 is listed in setting that allow to use restricted networks, MOCK_UID1
        // should upgrade to SYSTEM permission.
        when(mDeps.getAppsAllowedOnRestrictedNetworks(any())).thenReturn(
                new ArraySet<>(new String[] { MOCK_PACKAGE2 }));
        contentObserver.onChange(true /* selfChange */);
        mNetdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1});

        // MOCK_PACKAGE1 is listed in setting that allow to use restricted networks, MOCK_UID1
        // should still have SYSTEM permission.
        when(mDeps.getAppsAllowedOnRestrictedNetworks(any())).thenReturn(
                new ArraySet<>(new String[] { MOCK_PACKAGE1 }));
        contentObserver.onChange(true /* selfChange */);
        mNetdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1});

        // No app lists in setting, MOCK_UID1 should downgrade to NETWORK permission.
        when(mDeps.getAppsAllowedOnRestrictedNetworks(any())).thenReturn(new ArraySet<>());
        contentObserver.onChange(true /* selfChange */);
        mNetdMonitor.expectPermission(NETWORK, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1});

        // MOCK_PACKAGE1 removed, should revoke permission from MOCK_UID1.
        when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{MOCK_PACKAGE2});
        removePackageForUsers(new UserHandle[]{MOCK_USER1}, MOCK_PACKAGE1, MOCK_UID1);
        mNetdMonitor.expectNoPermission(new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1});
    }
}
 No newline at end of file