Loading packages/Connectivity/service/src/com/android/server/connectivity/PermissionMonitor.java +87 −16 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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. Loading Loading @@ -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) { Loading @@ -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); Loading Loading @@ -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. * Loading Loading @@ -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; Loading Loading @@ -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:"); Loading packages/Connectivity/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java +104 −17 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -127,7 +126,6 @@ public class PermissionMonitorTest { @Mock private SystemConfigManager mSystemConfigManager; private PermissionMonitor mPermissionMonitor; private MockContentResolver mContentResolver; @Before public void setUp() throws Exception { Loading @@ -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)); Loading @@ -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( Loading Loading @@ -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 Loading
packages/Connectivity/service/src/com/android/server/connectivity/PermissionMonitor.java +87 −16 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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. Loading Loading @@ -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) { Loading @@ -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); Loading Loading @@ -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. * Loading Loading @@ -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; Loading Loading @@ -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:"); Loading
packages/Connectivity/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java +104 −17 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -127,7 +126,6 @@ public class PermissionMonitorTest { @Mock private SystemConfigManager mSystemConfigManager; private PermissionMonitor mPermissionMonitor; private MockContentResolver mContentResolver; @Before public void setUp() throws Exception { Loading @@ -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)); Loading @@ -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( Loading Loading @@ -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