diff --git a/service/src/com/android/server/connectivity/PermissionMonitor.java b/service/src/com/android/server/connectivity/PermissionMonitor.java index 5886b1ad9a..b19348fa41 100644 --- a/service/src/com/android/server/connectivity/PermissionMonitor.java +++ b/service/src/com/android/server/connectivity/PermissionMonitor.java @@ -39,6 +39,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.net.ConnectivitySettingsManager; import android.net.INetd; import android.net.UidRange; import android.net.Uri; @@ -49,6 +50,7 @@ import android.os.SystemConfigManager; import android.os.UserHandle; import android.os.UserManager; import android.system.OsConstants; +import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; import android.util.SparseIntArray; @@ -105,6 +107,14 @@ public class PermissionMonitor { @GuardedBy("this") private final Set mAllApps = new HashSet<>(); + // A set of apps which are allowed to use restricted networks. These apps can't hold the + // CONNECTIVITY_USE_RESTRICTED_NETWORKS permission because they can't be signature|privileged + // apps. However, these apps should still be able to use restricted networks under certain + // conditions (e.g. government app using emergency services). So grant netd system permission + // to uids whose package name is listed in APPS_ALLOWED_ON_RESTRICTED_NETWORKS setting. + @GuardedBy("this") + private final Set mAppsAllowedOnRestrictedNetworks = new ArraySet<>(); + private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -165,6 +175,11 @@ public class PermissionMonitor { mIntentReceiver, intentFilter, null /* broadcastPermission */, null /* scheduler */); + // Read APPS_ALLOWED_ON_RESTRICTED_NETWORKS setting and update + // mAppsAllowedOnRestrictedNetworks. + updateAppsAllowedOnRestrictedNetworks( + ConnectivitySettingsManager.getAppsAllowedOnRestrictedNetworks(mContext)); + List apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS | MATCH_ANY_USER); if (apps == null) { @@ -219,11 +234,33 @@ public class PermissionMonitor { sendPackagePermissionsToNetd(netdPermsUids); } + @VisibleForTesting + void updateAppsAllowedOnRestrictedNetworks(final Set apps) { + mAppsAllowedOnRestrictedNetworks.clear(); + mAppsAllowedOnRestrictedNetworks.addAll(apps); + } + @VisibleForTesting static boolean isVendorApp(@NonNull ApplicationInfo appInfo) { return appInfo.isVendor() || appInfo.isOem() || appInfo.isProduct(); } + @VisibleForTesting + boolean isCarryoverPackage(final ApplicationInfo appInfo) { + if (appInfo == null) return false; + return (appInfo.targetSdkVersion < VERSION_Q && isVendorApp(appInfo)) + // Backward compatibility for b/114245686, on devices that launched before Q daemons + // and apps running as the system UID are exempted from this check. + || (appInfo.uid == SYSTEM_UID && mDeps.getDeviceFirstSdkInt() < VERSION_Q); + } + + @VisibleForTesting + boolean isAppAllowedOnRestrictedNetworks(@NonNull final PackageInfo app) { + // Check whether package name is in allowed on restricted networks app list. If so, this app + // can have netd system permission. + return mAppsAllowedOnRestrictedNetworks.contains(app.packageName); + } + @VisibleForTesting boolean hasPermission(@NonNull final PackageInfo app, @NonNull final String permission) { if (app.requestedPermissions == null || app.requestedPermissionsFlags == null) { @@ -241,22 +278,10 @@ public class PermissionMonitor { @VisibleForTesting boolean hasRestrictedNetworkPermission(@NonNull final PackageInfo app) { - // TODO : remove this check in the future(b/31479477). All apps should just - // request the appropriate permission for their use case since android Q. - if (app.applicationInfo != null) { - // Backward compatibility for b/114245686, on devices that launched before Q daemons - // and apps running as the system UID are exempted from this check. - if (app.applicationInfo.uid == SYSTEM_UID && mDeps.getDeviceFirstSdkInt() < VERSION_Q) { - return true; - } - - if (app.applicationInfo.targetSdkVersion < VERSION_Q - && isVendorApp(app.applicationInfo)) { - return true; - } - } - - return hasPermission(app, PERMISSION_MAINLINE_NETWORK_STACK) + // TODO : remove carryover package check in the future(b/31479477). All apps should just + // request the appropriate permission for their use case since android Q. + return isCarryoverPackage(app.applicationInfo) || isAppAllowedOnRestrictedNetworks(app) + || hasPermission(app, PERMISSION_MAINLINE_NETWORK_STACK) || hasPermission(app, NETWORK_STACK) || hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS); } diff --git a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java index 02a58080fe..c797a7b0ed 100644 --- a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java +++ b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java @@ -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.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; import static android.os.Process.SYSTEM_UID; import static com.android.server.connectivity.PermissionMonitor.NETWORK; @@ -68,12 +69,18 @@ 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; 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; @@ -120,6 +127,7 @@ public class PermissionMonitorTest { @Mock private SystemConfigManager mSystemConfigManager; private PermissionMonitor mPermissionMonitor; + private MockContentResolver mContentResolver; @Before public void setUp() throws Exception { @@ -137,16 +145,33 @@ public class PermissionMonitorTest { 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); + mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService, mDeps)); when(mPackageManager.getInstalledPackages(anyInt())).thenReturn(/* empty app list */ null); mPermissionMonitor.startMonitoring(); } + @After + public void tearDown() throws Exception { + FakeSettingsProvider.clearSettingsProvider(); + } + private boolean hasRestrictedNetworkPermission(String partition, int targetSdkVersion, int uid, String... permissions) { + return hasRestrictedNetworkPermission( + partition, targetSdkVersion, "" /* packageName */, uid, permissions); + } + + private boolean hasRestrictedNetworkPermission(String partition, int targetSdkVersion, + String packageName, int uid, String... permissions) { final PackageInfo packageInfo = packageInfoWithPermissions(REQUESTED_PERMISSION_GRANTED, permissions, partition); + packageInfo.packageName = packageName; packageInfo.applicationInfo.targetSdkVersion = targetSdkVersion; packageInfo.applicationInfo.uid = uid; return mPermissionMonitor.hasRestrictedNetworkPermission(packageInfo); @@ -280,6 +305,8 @@ public class PermissionMonitorTest { PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); assertFalse(hasRestrictedNetworkPermission( PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE)); + assertTrue(hasRestrictedNetworkPermission( + PARTITION_SYSTEM, VERSION_P, MOCK_UID1, PERMISSION_MAINLINE_NETWORK_STACK)); assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1)); assertFalse(hasRestrictedNetworkPermission( @@ -324,6 +351,90 @@ public class PermissionMonitorTest { PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CHANGE_NETWORK_STATE)); } + @Test + public void testHasRestrictedNetworkPermissionAppAllowedOnRestrictedNetworks() { + mPermissionMonitor.updateAppsAllowedOnRestrictedNetworks( + new ArraySet<>(new String[] { MOCK_PACKAGE1 })); + assertTrue(hasRestrictedNetworkPermission( + PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID1)); + assertTrue(hasRestrictedNetworkPermission( + PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID1, CHANGE_NETWORK_STATE)); + assertTrue(hasRestrictedNetworkPermission( + PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID1, CONNECTIVITY_INTERNAL)); + + assertFalse(hasRestrictedNetworkPermission( + PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID1)); + assertFalse(hasRestrictedNetworkPermission( + PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID1, CHANGE_NETWORK_STATE)); + assertFalse(hasRestrictedNetworkPermission( + PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID1, CONNECTIVITY_INTERNAL)); + + } + + private boolean wouldBeCarryoverPackage(String partition, int targetSdkVersion, int uid) { + final PackageInfo packageInfo = packageInfoWithPermissions( + REQUESTED_PERMISSION_GRANTED, new String[] {}, partition); + packageInfo.applicationInfo.targetSdkVersion = targetSdkVersion; + packageInfo.applicationInfo.uid = uid; + return mPermissionMonitor.isCarryoverPackage(packageInfo.applicationInfo); + } + + @Test + public void testIsCarryoverPackage() { + doReturn(VERSION_P).when(mDeps).getDeviceFirstSdkInt(); + assertTrue(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID)); + assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, SYSTEM_UID)); + assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, MOCK_UID1)); + assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, MOCK_UID1)); + assertTrue(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID)); + assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, SYSTEM_UID)); + assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1)); + assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, MOCK_UID1)); + + doReturn(VERSION_Q).when(mDeps).getDeviceFirstSdkInt(); + assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID)); + assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, SYSTEM_UID)); + assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, MOCK_UID1)); + assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, MOCK_UID1)); + assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID)); + assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, SYSTEM_UID)); + assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1)); + assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, MOCK_UID1)); + + assertFalse(wouldBeCarryoverPackage(PARTITION_OEM, VERSION_Q, SYSTEM_UID)); + assertFalse(wouldBeCarryoverPackage(PARTITION_PRODUCT, VERSION_Q, SYSTEM_UID)); + assertFalse(wouldBeCarryoverPackage(PARTITION_OEM, VERSION_Q, MOCK_UID1)); + assertFalse(wouldBeCarryoverPackage(PARTITION_PRODUCT, VERSION_Q, MOCK_UID1)); + } + + private boolean wouldBeAppAllowedOnRestrictedNetworks(String packageName) { + final PackageInfo packageInfo = new PackageInfo(); + packageInfo.packageName = packageName; + return mPermissionMonitor.isAppAllowedOnRestrictedNetworks(packageInfo); + } + + @Test + public void testIsAppAllowedOnRestrictedNetworks() { + mPermissionMonitor.updateAppsAllowedOnRestrictedNetworks(new ArraySet<>()); + assertFalse(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE1)); + assertFalse(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE2)); + + mPermissionMonitor.updateAppsAllowedOnRestrictedNetworks( + new ArraySet<>(new String[] { MOCK_PACKAGE1 })); + assertTrue(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE1)); + assertFalse(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE2)); + + mPermissionMonitor.updateAppsAllowedOnRestrictedNetworks( + new ArraySet<>(new String[] { MOCK_PACKAGE2 })); + assertFalse(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE1)); + assertTrue(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE2)); + + mPermissionMonitor.updateAppsAllowedOnRestrictedNetworks( + new ArraySet<>(new String[] { "com.android.test" })); + assertFalse(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE1)); + assertFalse(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE2)); + } + private void assertBackgroundPermission(boolean hasPermission, String name, int uid, String... permissions) throws Exception { when(mPackageManager.getPackageInfo(eq(name), anyInt()))