Loading services/supervision/java/com/android/server/supervision/SupervisionService.java +25 −4 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.supervision; import static android.Manifest.permission.BYPASS_ROLE_QUALIFICATION; import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.Manifest.permission.MANAGE_ROLE_HOLDERS; import static android.Manifest.permission.MANAGE_USERS; Loading Loading @@ -53,6 +54,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.IInterface; import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; Loading Loading @@ -138,7 +140,7 @@ public class SupervisionService extends ISupervisionManager.Stub { @Override public void setSupervisionEnabledForUser(@UserIdInt int userId, boolean enabled) { // TODO(b/395630828): Ensure that this method can only be called by the system. enforceCallerCanSetSupervisionEnabled(); if (UserHandle.getUserId(Binder.getCallingUid()) != userId) { enforcePermission(INTERACT_ACROSS_USERS); } Loading Loading @@ -577,21 +579,40 @@ public class SupervisionService extends ISupervisionManager.Stub { /** Enforces that the caller has the given permission. */ private void enforcePermission(String permission) { checkCallAuthorization( mInjector.context.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED); checkCallAuthorization(hasCallingPermission(permission)); } /** Enforces that the caller has at least one of the given permission. */ private void enforceAnyPermission(String... permissions) { boolean authorized = false; for (String permission : permissions) { if (mInjector.context.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) { if (hasCallingPermission(permission)) { authorized = true; break; } } checkCallAuthorization(authorized); } /** * Enforces that the caller can set supervision enabled state. * * This is restricted to the callers with the root, shell, or system uid or callers with the * BYPASS_ROLE_QUALIFICATION permission. This permission is only granted to the * SYSTEM_SHELL role holder. */ private void enforceCallerCanSetSupervisionEnabled() { checkCallAuthorization(isCallerSystem() || hasCallingPermission(BYPASS_ROLE_QUALIFICATION)); } private boolean hasCallingPermission(String permission) { return mInjector.context.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED; } private boolean isCallerSystem() { return UserHandle.isSameApp(Binder.getCallingUid(), Process.SYSTEM_UID); } /** Provides local services in a lazy manner. */ static class Injector { public Context context; Loading services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt +55 −19 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.supervision import android.Manifest.permission.BYPASS_ROLE_QUALIFICATION import android.app.Activity import android.app.KeyguardManager import android.app.admin.DevicePolicyManager Loading @@ -32,6 +33,8 @@ import android.content.ContextWrapper import android.content.Intent import android.content.IntentFilter import android.content.pm.PackageManager import android.content.pm.PackageManager.PERMISSION_DENIED import android.content.pm.PackageManager.PERMISSION_GRANTED import android.content.pm.PackageManagerInternal import android.content.pm.UserInfo import android.content.pm.UserInfo.FLAG_FOR_TESTING Loading Loading @@ -61,6 +64,7 @@ import com.android.server.supervision.SupervisionService.ACTION_CONFIRM_SUPERVIS import com.android.server.supervision.SupervisionService.RoleManagerWrapper import com.google.common.truth.Truth.assertThat import java.nio.file.Files import kotlin.test.assertFailsWith import org.junit.Before import org.junit.Ignore import org.junit.Rule Loading Loading @@ -95,14 +99,16 @@ class SupervisionServiceTest { @Mock private lateinit var mockUserManagerInternal: UserManagerInternal @Mock private lateinit var mockRoleManager: SupervisionService.RoleManagerWrapper private lateinit var context: Context private lateinit var context: SupervisionContextWrapper private lateinit var lifecycle: SupervisionService.Lifecycle private lateinit var service: SupervisionService @Before fun setUp() { context = InstrumentationRegistry.getInstrumentation().context context = SupervisionContextWrapper(context, mockKeyguardManager, mockPackageManager, context = SupervisionContextWrapper( InstrumentationRegistry.getInstrumentation().context, mockKeyguardManager, mockPackageManager, mockRoleManager) LocalServices.removeServiceForTest(DevicePolicyManagerInternal::class.java) Loading @@ -118,6 +124,11 @@ class SupervisionServiceTest { SupervisionSettings.getInstance() .changeDirForTesting(Files.createTempDirectory("tempSupervisionFolder").toFile()) // Simulate that this test has the BYPASS_ROLE_QUALIFICATION permission. This is needed to // bypass the system uid check in setSupervisionEnabled. This is the permission the // supervision CTS tests use to enable supervision. context.permissions[BYPASS_ROLE_QUALIFICATION] = PERMISSION_GRANTED service = SupervisionService(context) lifecycle = SupervisionService.Lifecycle(context, service) lifecycle.registerProfileOwnerListener() Loading Loading @@ -279,12 +290,20 @@ class SupervisionServiceTest { assertThat(getSecureSetting(SEARCH_CONTENT_FILTERS_ENABLED)).isEqualTo(-1) } @Test fun setSupervisionEnabledForUser_noPermission_throwsException() { context.permissions[BYPASS_ROLE_QUALIFICATION] = PERMISSION_DENIED assertFailsWith<SecurityException> { service.setSupervisionEnabledForUser(USER_ID, false) } } @Test @RequiresFlagsEnabled(Flags.FLAG_ENABLE_REMOVE_POLICIES_ON_SUPERVISION_DISABLE) fun setSupervisionEnabledForUser_removesPoliciesWhenDisabling() { for ((role, packageName) in supervisionRoleHolders) { whenever(mockRoleManager.getRoleHoldersAsUser(eq(role), any())) .thenReturn(listOf(packageName)); .thenReturn(listOf(packageName)) } service.setSupervisionEnabledForUser(USER_ID, false) Loading Loading @@ -511,7 +530,7 @@ class SupervisionServiceTest { private fun setSupervisionEnabledForUser( expectedUserId: Int, enabled: Boolean, listeners: Map<Int, Pair<ISupervisionListener, IBinder>>, listeners: Map<Int, Pair<ISupervisionListener, IBinder>> ) { service.setSupervisionEnabledForUser(expectedUserId, enabled) listeners.forEach { userId, (listener, binder) -> Loading @@ -533,7 +552,7 @@ class SupervisionServiceTest { assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse() for ((role, packageName) in supervisionRoleHolders) { whenever(mockRoleManager.getRoleHoldersAsUser(eq(role), any())) .thenReturn(listOf(packageName)); .thenReturn(listOf(packageName)) } service.setSupervisionEnabledForUser(USER_ID, false) Loading @@ -549,10 +568,15 @@ class SupervisionServiceTest { @RequiresFlagsEnabled(Flags.FLAG_ENABLE_REMOVE_POLICIES_ON_SUPERVISION_DISABLE) fun clearPackageSuspensions_noSupervisionPackages_doesNothing() { whenever(mockRoleManager.getRoleHoldersAsUser( any(), any())).thenReturn(listOf()) any(), any() )).thenReturn(listOf()) service.setSupervisionEnabledForUser(USER_ID, false) verify(mockPackageManagerInternal, never()).unsuspendForSuspendingPackage(any(), any(), any()) verify( mockPackageManagerInternal, never() ).unsuspendForSuspendingPackage(any(), any(), any()) } private val systemSupervisionPackage: String Loading @@ -566,7 +590,7 @@ class SupervisionServiceTest { .let(ComponentName::unflattenFromString) private fun simulateUserStarting(userId: Int, preCreated: Boolean = false) { val userInfo = UserInfo(userId, /* name= */ "tempUser", /* flags= */ 0) val userInfo = UserInfo(userId, "tempUser", 0) userInfo.preCreated = preCreated lifecycle.onUserStarting(TargetUser(userInfo)) } Loading Loading @@ -630,6 +654,7 @@ private class SupervisionContextWrapper( val roleManagerWrapper: RoleManagerWrapper, ) : ContextWrapper(context) { val interceptors = mutableListOf<Pair<BroadcastReceiver, IntentFilter>>() val permissions = mutableMapOf<String, Int>() override fun getSystemService(name: String): Any? { var ret = when (name) { Loading @@ -637,7 +662,7 @@ private class SupervisionContextWrapper( Context.ROLE_SERVICE -> roleManagerWrapper else -> super.getSystemService(name) } return ret; return ret } override fun getPackageManager() = pkgManager Loading @@ -660,14 +685,21 @@ private class SupervisionContextWrapper( val pendingResult = BroadcastReceiver.PendingResult( Activity.RESULT_OK, /* resultData= */ "", /* resultExtras= */ null, /* type= */ 0, /* ordered= */ true, /* sticky= */ false, /* token= */ null, /* resultData= */ "", /* resultExtras= */ null, /* type= */ 0, /* ordered= */ true, /* sticky= */ false, /* token= */ null, user.identifier, /* flags= */ 0, /* flags= */ 0, ) for ((receiver, filter) in interceptors) { if (filter.match(contentResolver, intent, false, "") > 0) { Loading @@ -676,4 +708,8 @@ private class SupervisionContextWrapper( } } } override fun checkCallingOrSelfPermission(permission: String): Int { return permissions[permission] ?: super.checkCallingOrSelfPermission(permission) } } Loading
services/supervision/java/com/android/server/supervision/SupervisionService.java +25 −4 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.supervision; import static android.Manifest.permission.BYPASS_ROLE_QUALIFICATION; import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.Manifest.permission.MANAGE_ROLE_HOLDERS; import static android.Manifest.permission.MANAGE_USERS; Loading Loading @@ -53,6 +54,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.IInterface; import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; Loading Loading @@ -138,7 +140,7 @@ public class SupervisionService extends ISupervisionManager.Stub { @Override public void setSupervisionEnabledForUser(@UserIdInt int userId, boolean enabled) { // TODO(b/395630828): Ensure that this method can only be called by the system. enforceCallerCanSetSupervisionEnabled(); if (UserHandle.getUserId(Binder.getCallingUid()) != userId) { enforcePermission(INTERACT_ACROSS_USERS); } Loading Loading @@ -577,21 +579,40 @@ public class SupervisionService extends ISupervisionManager.Stub { /** Enforces that the caller has the given permission. */ private void enforcePermission(String permission) { checkCallAuthorization( mInjector.context.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED); checkCallAuthorization(hasCallingPermission(permission)); } /** Enforces that the caller has at least one of the given permission. */ private void enforceAnyPermission(String... permissions) { boolean authorized = false; for (String permission : permissions) { if (mInjector.context.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) { if (hasCallingPermission(permission)) { authorized = true; break; } } checkCallAuthorization(authorized); } /** * Enforces that the caller can set supervision enabled state. * * This is restricted to the callers with the root, shell, or system uid or callers with the * BYPASS_ROLE_QUALIFICATION permission. This permission is only granted to the * SYSTEM_SHELL role holder. */ private void enforceCallerCanSetSupervisionEnabled() { checkCallAuthorization(isCallerSystem() || hasCallingPermission(BYPASS_ROLE_QUALIFICATION)); } private boolean hasCallingPermission(String permission) { return mInjector.context.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED; } private boolean isCallerSystem() { return UserHandle.isSameApp(Binder.getCallingUid(), Process.SYSTEM_UID); } /** Provides local services in a lazy manner. */ static class Injector { public Context context; Loading
services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt +55 −19 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.supervision import android.Manifest.permission.BYPASS_ROLE_QUALIFICATION import android.app.Activity import android.app.KeyguardManager import android.app.admin.DevicePolicyManager Loading @@ -32,6 +33,8 @@ import android.content.ContextWrapper import android.content.Intent import android.content.IntentFilter import android.content.pm.PackageManager import android.content.pm.PackageManager.PERMISSION_DENIED import android.content.pm.PackageManager.PERMISSION_GRANTED import android.content.pm.PackageManagerInternal import android.content.pm.UserInfo import android.content.pm.UserInfo.FLAG_FOR_TESTING Loading Loading @@ -61,6 +64,7 @@ import com.android.server.supervision.SupervisionService.ACTION_CONFIRM_SUPERVIS import com.android.server.supervision.SupervisionService.RoleManagerWrapper import com.google.common.truth.Truth.assertThat import java.nio.file.Files import kotlin.test.assertFailsWith import org.junit.Before import org.junit.Ignore import org.junit.Rule Loading Loading @@ -95,14 +99,16 @@ class SupervisionServiceTest { @Mock private lateinit var mockUserManagerInternal: UserManagerInternal @Mock private lateinit var mockRoleManager: SupervisionService.RoleManagerWrapper private lateinit var context: Context private lateinit var context: SupervisionContextWrapper private lateinit var lifecycle: SupervisionService.Lifecycle private lateinit var service: SupervisionService @Before fun setUp() { context = InstrumentationRegistry.getInstrumentation().context context = SupervisionContextWrapper(context, mockKeyguardManager, mockPackageManager, context = SupervisionContextWrapper( InstrumentationRegistry.getInstrumentation().context, mockKeyguardManager, mockPackageManager, mockRoleManager) LocalServices.removeServiceForTest(DevicePolicyManagerInternal::class.java) Loading @@ -118,6 +124,11 @@ class SupervisionServiceTest { SupervisionSettings.getInstance() .changeDirForTesting(Files.createTempDirectory("tempSupervisionFolder").toFile()) // Simulate that this test has the BYPASS_ROLE_QUALIFICATION permission. This is needed to // bypass the system uid check in setSupervisionEnabled. This is the permission the // supervision CTS tests use to enable supervision. context.permissions[BYPASS_ROLE_QUALIFICATION] = PERMISSION_GRANTED service = SupervisionService(context) lifecycle = SupervisionService.Lifecycle(context, service) lifecycle.registerProfileOwnerListener() Loading Loading @@ -279,12 +290,20 @@ class SupervisionServiceTest { assertThat(getSecureSetting(SEARCH_CONTENT_FILTERS_ENABLED)).isEqualTo(-1) } @Test fun setSupervisionEnabledForUser_noPermission_throwsException() { context.permissions[BYPASS_ROLE_QUALIFICATION] = PERMISSION_DENIED assertFailsWith<SecurityException> { service.setSupervisionEnabledForUser(USER_ID, false) } } @Test @RequiresFlagsEnabled(Flags.FLAG_ENABLE_REMOVE_POLICIES_ON_SUPERVISION_DISABLE) fun setSupervisionEnabledForUser_removesPoliciesWhenDisabling() { for ((role, packageName) in supervisionRoleHolders) { whenever(mockRoleManager.getRoleHoldersAsUser(eq(role), any())) .thenReturn(listOf(packageName)); .thenReturn(listOf(packageName)) } service.setSupervisionEnabledForUser(USER_ID, false) Loading Loading @@ -511,7 +530,7 @@ class SupervisionServiceTest { private fun setSupervisionEnabledForUser( expectedUserId: Int, enabled: Boolean, listeners: Map<Int, Pair<ISupervisionListener, IBinder>>, listeners: Map<Int, Pair<ISupervisionListener, IBinder>> ) { service.setSupervisionEnabledForUser(expectedUserId, enabled) listeners.forEach { userId, (listener, binder) -> Loading @@ -533,7 +552,7 @@ class SupervisionServiceTest { assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse() for ((role, packageName) in supervisionRoleHolders) { whenever(mockRoleManager.getRoleHoldersAsUser(eq(role), any())) .thenReturn(listOf(packageName)); .thenReturn(listOf(packageName)) } service.setSupervisionEnabledForUser(USER_ID, false) Loading @@ -549,10 +568,15 @@ class SupervisionServiceTest { @RequiresFlagsEnabled(Flags.FLAG_ENABLE_REMOVE_POLICIES_ON_SUPERVISION_DISABLE) fun clearPackageSuspensions_noSupervisionPackages_doesNothing() { whenever(mockRoleManager.getRoleHoldersAsUser( any(), any())).thenReturn(listOf()) any(), any() )).thenReturn(listOf()) service.setSupervisionEnabledForUser(USER_ID, false) verify(mockPackageManagerInternal, never()).unsuspendForSuspendingPackage(any(), any(), any()) verify( mockPackageManagerInternal, never() ).unsuspendForSuspendingPackage(any(), any(), any()) } private val systemSupervisionPackage: String Loading @@ -566,7 +590,7 @@ class SupervisionServiceTest { .let(ComponentName::unflattenFromString) private fun simulateUserStarting(userId: Int, preCreated: Boolean = false) { val userInfo = UserInfo(userId, /* name= */ "tempUser", /* flags= */ 0) val userInfo = UserInfo(userId, "tempUser", 0) userInfo.preCreated = preCreated lifecycle.onUserStarting(TargetUser(userInfo)) } Loading Loading @@ -630,6 +654,7 @@ private class SupervisionContextWrapper( val roleManagerWrapper: RoleManagerWrapper, ) : ContextWrapper(context) { val interceptors = mutableListOf<Pair<BroadcastReceiver, IntentFilter>>() val permissions = mutableMapOf<String, Int>() override fun getSystemService(name: String): Any? { var ret = when (name) { Loading @@ -637,7 +662,7 @@ private class SupervisionContextWrapper( Context.ROLE_SERVICE -> roleManagerWrapper else -> super.getSystemService(name) } return ret; return ret } override fun getPackageManager() = pkgManager Loading @@ -660,14 +685,21 @@ private class SupervisionContextWrapper( val pendingResult = BroadcastReceiver.PendingResult( Activity.RESULT_OK, /* resultData= */ "", /* resultExtras= */ null, /* type= */ 0, /* ordered= */ true, /* sticky= */ false, /* token= */ null, /* resultData= */ "", /* resultExtras= */ null, /* type= */ 0, /* ordered= */ true, /* sticky= */ false, /* token= */ null, user.identifier, /* flags= */ 0, /* flags= */ 0, ) for ((receiver, filter) in interceptors) { if (filter.match(contentResolver, intent, false, "") > 0) { Loading @@ -676,4 +708,8 @@ private class SupervisionContextWrapper( } } } override fun checkCallingOrSelfPermission(permission: String): Int { return permissions[permission] ?: super.checkCallingOrSelfPermission(permission) } }