Loading packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppOpsController.kt +9 −10 Original line number Diff line number Diff line Loading @@ -17,7 +17,6 @@ package com.android.settingslib.spaprivileged.model.app import android.app.AppOpsManager.MODE_ALLOWED import android.app.AppOpsManager.MODE_ERRORED import android.app.AppOpsManager.Mode import android.content.Context import android.content.pm.ApplicationInfo Loading @@ -33,14 +32,14 @@ interface IAppOpsController { fun setAllowed(allowed: Boolean) @Mode fun getMode(): Int @Mode fun getMode(): Int } class AppOpsController( context: Context, private val app: ApplicationInfo, private val op: Int, private val modeForNotAllowed: Int, private val setModeByUid: Boolean = false, ) : IAppOpsController { private val appOpsManager = context.appOpsManager Loading @@ -49,7 +48,7 @@ class AppOpsController( get() = _mode override fun setAllowed(allowed: Boolean) { val mode = if (allowed) MODE_ALLOWED else MODE_ERRORED val mode = if (allowed) MODE_ALLOWED else modeForNotAllowed if (setModeByUid) { appOpsManager.setUidMode(op, app.uid, mode) } else { Loading @@ -58,10 +57,10 @@ class AppOpsController( _mode.postValue(mode) } @Mode override fun getMode(): Int = appOpsManager.checkOpNoThrow(op, app.uid, app.packageName) @Mode override fun getMode(): Int = appOpsManager.checkOpNoThrow(op, app.uid, app.packageName) private val _mode = object : MutableLiveData<Int>() { private val _mode = object : MutableLiveData<Int>() { override fun onActive() { postValue(getMode()) } Loading packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt +76 −26 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.settingslib.spaprivileged.template.app import android.app.AppOpsManager.MODE_ALLOWED import android.app.AppOpsManager.MODE_DEFAULT import android.app.AppOpsManager.MODE_ERRORED import android.content.Context import android.content.pm.ApplicationInfo import androidx.compose.runtime.Composable Loading @@ -25,6 +26,8 @@ import androidx.compose.runtime.State import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.remember import com.android.settingslib.spa.framework.compose.stateOf import com.android.settingslib.spa.framework.util.asyncMapItem import com.android.settingslib.spa.framework.util.filterItem import com.android.settingslib.spaprivileged.model.app.AppOpsController import com.android.settingslib.spaprivileged.model.app.AppRecord Loading @@ -37,6 +40,7 @@ import kotlinx.coroutines.flow.map data class AppOpPermissionRecord( override val app: ApplicationInfo, val hasRequestBroaderPermission: Boolean, val hasRequestPermission: Boolean, var appOpsController: IAppOpsController, ) : AppRecord Loading @@ -49,10 +53,27 @@ abstract class AppOpPermissionListModel( abstract val appOp: Int abstract val permission: String /** * When set, specifies the broader permission who trumps the [permission]. * * When trumped, the [permission] is not changeable and model shows the [permission] as allowed. */ open val broaderPermission: String? = null /** * Indicates whether [permission] has protection level appop flag. * * If true, it uses getAppOpPermissionPackages() to fetch bits to decide whether the permission * is requested. */ open val permissionHasAppopFlag: Boolean = true open val modeForNotAllowed: Int = MODE_ERRORED /** * Use AppOpsManager#setUidMode() instead of AppOpsManager#setMode() when set allowed. * * Security related app-ops should be set with setUidMode() instead of setMode(). * Security or privacy related app-ops should be set with setUidMode() instead of setMode(). */ open val setModeByUid = false Loading @@ -60,31 +81,54 @@ abstract class AppOpPermissionListModel( private val notChangeablePackages = setOf("android", "com.android.systemui", context.packageName) override fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>) = userIdFlow.map { userId -> packageManagers.getAppOpPermissionPackages(userId, permission) }.combine(appListFlow) { packageNames, appList -> appList.map { app -> private fun createAppOpsController(app: ApplicationInfo) = AppOpsController( context = context, app = app, op = appOp, setModeByUid = setModeByUid, modeForNotAllowed = modeForNotAllowed, ) private fun createRecord( app: ApplicationInfo, hasRequestPermission: Boolean ): AppOpPermissionRecord = with(packageManagers) { AppOpPermissionRecord( app = app, hasRequestPermission = app.packageName in packageNames, hasRequestBroaderPermission = broaderPermission?.let { app.hasRequestPermission(it) } ?: false, hasRequestPermission = hasRequestPermission, appOpsController = createAppOpsController(app), ) } } override fun transformItem(app: ApplicationInfo) = AppOpPermissionRecord( override fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>) = if (permissionHasAppopFlag) { userIdFlow .map { userId -> packageManagers.getAppOpPermissionPackages(userId, permission) } .combine(appListFlow) { packageNames, appList -> appList.map { app -> createRecord( app = app, hasRequestPermission = with(packageManagers) { app.hasRequestPermission(permission) }, appOpsController = createAppOpsController(app), hasRequestPermission = app.packageName in packageNames, ) } } } else { appListFlow.asyncMapItem { app -> with(packageManagers) { createRecord(app, app.hasRequestPermission(permission)) } } } private fun createAppOpsController(app: ApplicationInfo) = AppOpsController( context = context, override fun transformItem(app: ApplicationInfo) = with(packageManagers) { createRecord( app = app, op = appOp, setModeByUid = setModeByUid, hasRequestPermission = app.hasRequestPermission(permission), ) } override fun filter(userIdFlow: Flow<Int>, recordListFlow: Flow<List<AppOpPermissionRecord>>) = recordListFlow.filterItem(::isChangeable) Loading @@ -95,15 +139,19 @@ abstract class AppOpPermissionListModel( */ @Composable override fun isAllowed(record: AppOpPermissionRecord): State<Boolean?> { if (record.hasRequestBroaderPermission) { // Broader permission trumps the specific permission. return stateOf(true) } val mode = record.appOpsController.mode.observeAsState() return remember { derivedStateOf { when (mode.value) { null -> null MODE_ALLOWED -> true MODE_DEFAULT -> with(packageManagers) { record.app.hasGrantPermission(permission) } MODE_DEFAULT -> with(packageManagers) { record.app.hasGrantPermission(permission) } else -> false } } Loading @@ -111,7 +159,9 @@ abstract class AppOpPermissionListModel( } override fun isChangeable(record: AppOpPermissionRecord) = record.hasRequestPermission && record.app.packageName !in notChangeablePackages record.hasRequestPermission && !record.hasRequestBroaderPermission && record.app.packageName !in notChangeablePackages override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) { record.appOpsController.setAllowed(newAllowed) Loading packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppOpsControllerTest.kt +47 −20 Original line number Diff line number Diff line Loading @@ -31,21 +31,18 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever import org.mockito.Spy import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule import org.mockito.Mockito.`when` as whenever @RunWith(AndroidJUnit4::class) class AppOpsControllerTest { @get:Rule val mockito: MockitoRule = MockitoJUnit.rule() @get:Rule val mockito: MockitoRule = MockitoJUnit.rule() @Spy private val context: Context = ApplicationProvider.getApplicationContext() @Spy private val context: Context = ApplicationProvider.getApplicationContext() @Mock private lateinit var appOpsManager: AppOpsManager @Mock private lateinit var appOpsManager: AppOpsManager @Before fun setUp() { Loading @@ -54,7 +51,13 @@ class AppOpsControllerTest { @Test fun setAllowed_setToTrue() { val controller = AppOpsController(context = context, app = APP, op = OP) val controller = AppOpsController( context = context, app = APP, op = OP, modeForNotAllowed = MODE_ERRORED ) controller.setAllowed(true) Loading @@ -63,7 +66,13 @@ class AppOpsControllerTest { @Test fun setAllowed_setToFalse() { val controller = AppOpsController(context = context, app = APP, op = OP) val controller = AppOpsController( context = context, app = APP, op = OP, modeForNotAllowed = MODE_ERRORED ) controller.setAllowed(false) Loading @@ -73,7 +82,13 @@ class AppOpsControllerTest { @Test fun setAllowed_setToTrueByUid() { val controller = AppOpsController(context = context, app = APP, op = OP, setModeByUid = true) AppOpsController( context = context, app = APP, op = OP, modeForNotAllowed = MODE_ERRORED, setModeByUid = true ) controller.setAllowed(true) Loading @@ -83,7 +98,13 @@ class AppOpsControllerTest { @Test fun setAllowed_setToFalseByUid() { val controller = AppOpsController(context = context, app = APP, op = OP, setModeByUid = true) AppOpsController( context = context, app = APP, op = OP, modeForNotAllowed = MODE_ERRORED, setModeByUid = true ) controller.setAllowed(false) Loading @@ -92,10 +113,15 @@ class AppOpsControllerTest { @Test fun getMode() { whenever( appOpsManager.checkOpNoThrow(OP, APP.uid, APP.packageName) ).thenReturn(MODE_ALLOWED) val controller = AppOpsController(context = context, app = APP, op = OP) whenever(appOpsManager.checkOpNoThrow(OP, APP.uid, APP.packageName)) .thenReturn(MODE_ALLOWED) val controller = AppOpsController( context = context, app = APP, op = OP, modeForNotAllowed = MODE_ERRORED ) val mode = controller.getMode() Loading @@ -104,7 +130,8 @@ class AppOpsControllerTest { private companion object { const val OP = 1 val APP = ApplicationInfo().apply { val APP = ApplicationInfo().apply { packageName = "package.name" uid = 123 } Loading packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppListTest.kt +138 −76 Original line number Diff line number Diff line Loading @@ -39,28 +39,23 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever import org.mockito.Spy import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule import org.mockito.Mockito.`when` as whenever @OptIn(ExperimentalCoroutinesApi::class) @RunWith(AndroidJUnit4::class) class AppOpPermissionAppListTest { @get:Rule val mockito: MockitoRule = MockitoJUnit.rule() @get:Rule val mockito: MockitoRule = MockitoJUnit.rule() @get:Rule val composeTestRule = createComposeRule() @get:Rule val composeTestRule = createComposeRule() @Spy private val context: Context = ApplicationProvider.getApplicationContext() @Spy private val context: Context = ApplicationProvider.getApplicationContext() @Mock private lateinit var packageManagers: IPackageManagers @Mock private lateinit var packageManagers: IPackageManagers @Mock private lateinit var appOpsManager: AppOpsManager @Mock private lateinit var appOpsManager: AppOpsManager private lateinit var listModel: TestAppOpPermissionAppListModel Loading @@ -79,9 +74,7 @@ class AppOpPermissionAppListTest { @Test fun transformItem_hasRequestPermission() = runTest { with(packageManagers) { whenever(APP.hasRequestPermission(PERMISSION)).thenReturn(true) } with(packageManagers) { whenever(APP.hasRequestPermission(PERMISSION)).thenReturn(true) } val record = listModel.transformItem(APP) Loading @@ -90,22 +83,44 @@ class AppOpPermissionAppListTest { @Test fun transformItem_notRequestPermission() = runTest { with(packageManagers) { whenever(APP.hasRequestPermission(PERMISSION)).thenReturn(false) } val record = listModel.transformItem(APP) assertThat(record.hasRequestPermission).isFalse() } @Test fun transformItem_hasRequestBroaderPermission() = runTest { listModel.broaderPermission = BROADER_PERMISSION with(packageManagers) { whenever(APP.hasRequestPermission(PERMISSION)).thenReturn(false) whenever(APP.hasRequestPermission(BROADER_PERMISSION)).thenReturn(true) } val record = listModel.transformItem(APP) assertThat(record.hasRequestPermission).isFalse() assertThat(record.hasRequestBroaderPermission).isTrue() } @Test fun filter() = runTest { fun transformItem_notRequestBroaderPermission() = runTest { listModel.broaderPermission = BROADER_PERMISSION with(packageManagers) { whenever(APP.hasRequestPermission(PERMISSION)).thenReturn(false) whenever(APP.hasRequestPermission(BROADER_PERMISSION)).thenReturn(false) } val record = listModel.transformItem(APP) assertThat(record.hasRequestPermission).isFalse() } val record = AppOpPermissionRecord( @Test fun filter() = runTest { with(packageManagers) { whenever(APP.hasRequestPermission(PERMISSION)).thenReturn(false) } val record = AppOpPermissionRecord( app = APP, hasRequestBroaderPermission = false, hasRequestPermission = false, appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT), ) Loading @@ -118,8 +133,10 @@ class AppOpPermissionAppListTest { @Test fun isAllowed_allowed() { val record = AppOpPermissionRecord( val record = AppOpPermissionRecord( app = APP, hasRequestBroaderPermission = false, hasRequestPermission = true, appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_ALLOWED), ) Loading @@ -131,11 +148,11 @@ class AppOpPermissionAppListTest { @Test fun isAllowed_defaultAndHasGrantPermission() { with(packageManagers) { whenever(APP.hasGrantPermission(PERMISSION)).thenReturn(true) } val record = AppOpPermissionRecord( with(packageManagers) { whenever(APP.hasGrantPermission(PERMISSION)).thenReturn(true) } val record = AppOpPermissionRecord( app = APP, hasRequestBroaderPermission = false, hasRequestPermission = true, appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT), ) Loading @@ -147,11 +164,11 @@ class AppOpPermissionAppListTest { @Test fun isAllowed_defaultAndNotGrantPermission() { with(packageManagers) { whenever(APP.hasGrantPermission(PERMISSION)).thenReturn(false) } val record = AppOpPermissionRecord( with(packageManagers) { whenever(APP.hasGrantPermission(PERMISSION)).thenReturn(false) } val record = AppOpPermissionRecord( app = APP, hasRequestBroaderPermission = false, hasRequestPermission = true, appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT), ) Loading @@ -161,10 +178,32 @@ class AppOpPermissionAppListTest { assertThat(isAllowed).isFalse() } @Test fun isAllowed_broaderPermissionTrumps() { listModel.broaderPermission = BROADER_PERMISSION with(packageManagers) { whenever(APP.hasGrantPermission(PERMISSION)).thenReturn(false) whenever(APP.hasGrantPermission(BROADER_PERMISSION)).thenReturn(true) } val record = AppOpPermissionRecord( app = APP, hasRequestBroaderPermission = true, hasRequestPermission = false, appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_ERRORED), ) val isAllowed = getIsAllowed(record) assertThat(isAllowed).isTrue() } @Test fun isAllowed_notAllowed() { val record = AppOpPermissionRecord( val record = AppOpPermissionRecord( app = APP, hasRequestBroaderPermission = false, hasRequestPermission = true, appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_ERRORED), ) Loading @@ -176,8 +215,10 @@ class AppOpPermissionAppListTest { @Test fun isChangeable_notRequestPermission() { val record = AppOpPermissionRecord( val record = AppOpPermissionRecord( app = APP, hasRequestBroaderPermission = false, hasRequestPermission = false, appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT), ) Loading @@ -189,8 +230,10 @@ class AppOpPermissionAppListTest { @Test fun isChangeable_notChangeablePackages() { val record = AppOpPermissionRecord( val record = AppOpPermissionRecord( app = NOT_CHANGEABLE_APP, hasRequestBroaderPermission = false, hasRequestPermission = true, appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT), ) Loading @@ -202,8 +245,10 @@ class AppOpPermissionAppListTest { @Test fun isChangeable_hasRequestPermissionAndChangeable() { val record = AppOpPermissionRecord( val record = AppOpPermissionRecord( app = APP, hasRequestBroaderPermission = false, hasRequestPermission = true, appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT), ) Loading @@ -213,11 +258,29 @@ class AppOpPermissionAppListTest { assertThat(isChangeable).isTrue() } @Test fun isChangeable_broaderPermissionTrumps() { listModel.broaderPermission = BROADER_PERMISSION val record = AppOpPermissionRecord( app = APP, hasRequestBroaderPermission = true, hasRequestPermission = true, appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT), ) val isChangeable = listModel.isChangeable(record) assertThat(isChangeable).isFalse() } @Test fun setAllowed() { val appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT) val record = AppOpPermissionRecord( val record = AppOpPermissionRecord( app = APP, hasRequestBroaderPermission = false, hasRequestPermission = true, appOpsController = appOpsController, ) Loading @@ -239,9 +302,7 @@ class AppOpPermissionAppListTest { private fun getIsAllowed(record: AppOpPermissionRecord): Boolean? { lateinit var isAllowedState: State<Boolean?> composeTestRule.setContent { isAllowedState = listModel.isAllowed(record) } composeTestRule.setContent { isAllowedState = listModel.isAllowed(record) } return isAllowedState.value } Loading @@ -250,8 +311,12 @@ class AppOpPermissionAppListTest { override val pageTitleResId = R.string.test_app_op_permission_title override val switchTitleResId = R.string.test_app_op_permission_switch_title override val footerResId = R.string.test_app_op_permission_footer override val appOp = AppOpsManager.OP_MANAGE_MEDIA override val permission = PERMISSION override val permissionHasAppopFlag = true override var broaderPermission: String? = null override var setModeByUid = false } Loading @@ -259,12 +324,9 @@ class AppOpPermissionAppListTest { const val USER_ID = 0 const val PACKAGE_NAME = "package.name" const val PERMISSION = "PERMISSION" val APP = ApplicationInfo().apply { packageName = PACKAGE_NAME } val NOT_CHANGEABLE_APP = ApplicationInfo().apply { packageName = "android" } const val BROADER_PERMISSION = "BROADER_PERMISSION" val APP = ApplicationInfo().apply { packageName = PACKAGE_NAME } val NOT_CHANGEABLE_APP = ApplicationInfo().apply { packageName = "android" } } } Loading Loading
packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppOpsController.kt +9 −10 Original line number Diff line number Diff line Loading @@ -17,7 +17,6 @@ package com.android.settingslib.spaprivileged.model.app import android.app.AppOpsManager.MODE_ALLOWED import android.app.AppOpsManager.MODE_ERRORED import android.app.AppOpsManager.Mode import android.content.Context import android.content.pm.ApplicationInfo Loading @@ -33,14 +32,14 @@ interface IAppOpsController { fun setAllowed(allowed: Boolean) @Mode fun getMode(): Int @Mode fun getMode(): Int } class AppOpsController( context: Context, private val app: ApplicationInfo, private val op: Int, private val modeForNotAllowed: Int, private val setModeByUid: Boolean = false, ) : IAppOpsController { private val appOpsManager = context.appOpsManager Loading @@ -49,7 +48,7 @@ class AppOpsController( get() = _mode override fun setAllowed(allowed: Boolean) { val mode = if (allowed) MODE_ALLOWED else MODE_ERRORED val mode = if (allowed) MODE_ALLOWED else modeForNotAllowed if (setModeByUid) { appOpsManager.setUidMode(op, app.uid, mode) } else { Loading @@ -58,10 +57,10 @@ class AppOpsController( _mode.postValue(mode) } @Mode override fun getMode(): Int = appOpsManager.checkOpNoThrow(op, app.uid, app.packageName) @Mode override fun getMode(): Int = appOpsManager.checkOpNoThrow(op, app.uid, app.packageName) private val _mode = object : MutableLiveData<Int>() { private val _mode = object : MutableLiveData<Int>() { override fun onActive() { postValue(getMode()) } Loading
packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt +76 −26 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.settingslib.spaprivileged.template.app import android.app.AppOpsManager.MODE_ALLOWED import android.app.AppOpsManager.MODE_DEFAULT import android.app.AppOpsManager.MODE_ERRORED import android.content.Context import android.content.pm.ApplicationInfo import androidx.compose.runtime.Composable Loading @@ -25,6 +26,8 @@ import androidx.compose.runtime.State import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.remember import com.android.settingslib.spa.framework.compose.stateOf import com.android.settingslib.spa.framework.util.asyncMapItem import com.android.settingslib.spa.framework.util.filterItem import com.android.settingslib.spaprivileged.model.app.AppOpsController import com.android.settingslib.spaprivileged.model.app.AppRecord Loading @@ -37,6 +40,7 @@ import kotlinx.coroutines.flow.map data class AppOpPermissionRecord( override val app: ApplicationInfo, val hasRequestBroaderPermission: Boolean, val hasRequestPermission: Boolean, var appOpsController: IAppOpsController, ) : AppRecord Loading @@ -49,10 +53,27 @@ abstract class AppOpPermissionListModel( abstract val appOp: Int abstract val permission: String /** * When set, specifies the broader permission who trumps the [permission]. * * When trumped, the [permission] is not changeable and model shows the [permission] as allowed. */ open val broaderPermission: String? = null /** * Indicates whether [permission] has protection level appop flag. * * If true, it uses getAppOpPermissionPackages() to fetch bits to decide whether the permission * is requested. */ open val permissionHasAppopFlag: Boolean = true open val modeForNotAllowed: Int = MODE_ERRORED /** * Use AppOpsManager#setUidMode() instead of AppOpsManager#setMode() when set allowed. * * Security related app-ops should be set with setUidMode() instead of setMode(). * Security or privacy related app-ops should be set with setUidMode() instead of setMode(). */ open val setModeByUid = false Loading @@ -60,31 +81,54 @@ abstract class AppOpPermissionListModel( private val notChangeablePackages = setOf("android", "com.android.systemui", context.packageName) override fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>) = userIdFlow.map { userId -> packageManagers.getAppOpPermissionPackages(userId, permission) }.combine(appListFlow) { packageNames, appList -> appList.map { app -> private fun createAppOpsController(app: ApplicationInfo) = AppOpsController( context = context, app = app, op = appOp, setModeByUid = setModeByUid, modeForNotAllowed = modeForNotAllowed, ) private fun createRecord( app: ApplicationInfo, hasRequestPermission: Boolean ): AppOpPermissionRecord = with(packageManagers) { AppOpPermissionRecord( app = app, hasRequestPermission = app.packageName in packageNames, hasRequestBroaderPermission = broaderPermission?.let { app.hasRequestPermission(it) } ?: false, hasRequestPermission = hasRequestPermission, appOpsController = createAppOpsController(app), ) } } override fun transformItem(app: ApplicationInfo) = AppOpPermissionRecord( override fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>) = if (permissionHasAppopFlag) { userIdFlow .map { userId -> packageManagers.getAppOpPermissionPackages(userId, permission) } .combine(appListFlow) { packageNames, appList -> appList.map { app -> createRecord( app = app, hasRequestPermission = with(packageManagers) { app.hasRequestPermission(permission) }, appOpsController = createAppOpsController(app), hasRequestPermission = app.packageName in packageNames, ) } } } else { appListFlow.asyncMapItem { app -> with(packageManagers) { createRecord(app, app.hasRequestPermission(permission)) } } } private fun createAppOpsController(app: ApplicationInfo) = AppOpsController( context = context, override fun transformItem(app: ApplicationInfo) = with(packageManagers) { createRecord( app = app, op = appOp, setModeByUid = setModeByUid, hasRequestPermission = app.hasRequestPermission(permission), ) } override fun filter(userIdFlow: Flow<Int>, recordListFlow: Flow<List<AppOpPermissionRecord>>) = recordListFlow.filterItem(::isChangeable) Loading @@ -95,15 +139,19 @@ abstract class AppOpPermissionListModel( */ @Composable override fun isAllowed(record: AppOpPermissionRecord): State<Boolean?> { if (record.hasRequestBroaderPermission) { // Broader permission trumps the specific permission. return stateOf(true) } val mode = record.appOpsController.mode.observeAsState() return remember { derivedStateOf { when (mode.value) { null -> null MODE_ALLOWED -> true MODE_DEFAULT -> with(packageManagers) { record.app.hasGrantPermission(permission) } MODE_DEFAULT -> with(packageManagers) { record.app.hasGrantPermission(permission) } else -> false } } Loading @@ -111,7 +159,9 @@ abstract class AppOpPermissionListModel( } override fun isChangeable(record: AppOpPermissionRecord) = record.hasRequestPermission && record.app.packageName !in notChangeablePackages record.hasRequestPermission && !record.hasRequestBroaderPermission && record.app.packageName !in notChangeablePackages override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) { record.appOpsController.setAllowed(newAllowed) Loading
packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppOpsControllerTest.kt +47 −20 Original line number Diff line number Diff line Loading @@ -31,21 +31,18 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever import org.mockito.Spy import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule import org.mockito.Mockito.`when` as whenever @RunWith(AndroidJUnit4::class) class AppOpsControllerTest { @get:Rule val mockito: MockitoRule = MockitoJUnit.rule() @get:Rule val mockito: MockitoRule = MockitoJUnit.rule() @Spy private val context: Context = ApplicationProvider.getApplicationContext() @Spy private val context: Context = ApplicationProvider.getApplicationContext() @Mock private lateinit var appOpsManager: AppOpsManager @Mock private lateinit var appOpsManager: AppOpsManager @Before fun setUp() { Loading @@ -54,7 +51,13 @@ class AppOpsControllerTest { @Test fun setAllowed_setToTrue() { val controller = AppOpsController(context = context, app = APP, op = OP) val controller = AppOpsController( context = context, app = APP, op = OP, modeForNotAllowed = MODE_ERRORED ) controller.setAllowed(true) Loading @@ -63,7 +66,13 @@ class AppOpsControllerTest { @Test fun setAllowed_setToFalse() { val controller = AppOpsController(context = context, app = APP, op = OP) val controller = AppOpsController( context = context, app = APP, op = OP, modeForNotAllowed = MODE_ERRORED ) controller.setAllowed(false) Loading @@ -73,7 +82,13 @@ class AppOpsControllerTest { @Test fun setAllowed_setToTrueByUid() { val controller = AppOpsController(context = context, app = APP, op = OP, setModeByUid = true) AppOpsController( context = context, app = APP, op = OP, modeForNotAllowed = MODE_ERRORED, setModeByUid = true ) controller.setAllowed(true) Loading @@ -83,7 +98,13 @@ class AppOpsControllerTest { @Test fun setAllowed_setToFalseByUid() { val controller = AppOpsController(context = context, app = APP, op = OP, setModeByUid = true) AppOpsController( context = context, app = APP, op = OP, modeForNotAllowed = MODE_ERRORED, setModeByUid = true ) controller.setAllowed(false) Loading @@ -92,10 +113,15 @@ class AppOpsControllerTest { @Test fun getMode() { whenever( appOpsManager.checkOpNoThrow(OP, APP.uid, APP.packageName) ).thenReturn(MODE_ALLOWED) val controller = AppOpsController(context = context, app = APP, op = OP) whenever(appOpsManager.checkOpNoThrow(OP, APP.uid, APP.packageName)) .thenReturn(MODE_ALLOWED) val controller = AppOpsController( context = context, app = APP, op = OP, modeForNotAllowed = MODE_ERRORED ) val mode = controller.getMode() Loading @@ -104,7 +130,8 @@ class AppOpsControllerTest { private companion object { const val OP = 1 val APP = ApplicationInfo().apply { val APP = ApplicationInfo().apply { packageName = "package.name" uid = 123 } Loading
packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppListTest.kt +138 −76 Original line number Diff line number Diff line Loading @@ -39,28 +39,23 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever import org.mockito.Spy import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule import org.mockito.Mockito.`when` as whenever @OptIn(ExperimentalCoroutinesApi::class) @RunWith(AndroidJUnit4::class) class AppOpPermissionAppListTest { @get:Rule val mockito: MockitoRule = MockitoJUnit.rule() @get:Rule val mockito: MockitoRule = MockitoJUnit.rule() @get:Rule val composeTestRule = createComposeRule() @get:Rule val composeTestRule = createComposeRule() @Spy private val context: Context = ApplicationProvider.getApplicationContext() @Spy private val context: Context = ApplicationProvider.getApplicationContext() @Mock private lateinit var packageManagers: IPackageManagers @Mock private lateinit var packageManagers: IPackageManagers @Mock private lateinit var appOpsManager: AppOpsManager @Mock private lateinit var appOpsManager: AppOpsManager private lateinit var listModel: TestAppOpPermissionAppListModel Loading @@ -79,9 +74,7 @@ class AppOpPermissionAppListTest { @Test fun transformItem_hasRequestPermission() = runTest { with(packageManagers) { whenever(APP.hasRequestPermission(PERMISSION)).thenReturn(true) } with(packageManagers) { whenever(APP.hasRequestPermission(PERMISSION)).thenReturn(true) } val record = listModel.transformItem(APP) Loading @@ -90,22 +83,44 @@ class AppOpPermissionAppListTest { @Test fun transformItem_notRequestPermission() = runTest { with(packageManagers) { whenever(APP.hasRequestPermission(PERMISSION)).thenReturn(false) } val record = listModel.transformItem(APP) assertThat(record.hasRequestPermission).isFalse() } @Test fun transformItem_hasRequestBroaderPermission() = runTest { listModel.broaderPermission = BROADER_PERMISSION with(packageManagers) { whenever(APP.hasRequestPermission(PERMISSION)).thenReturn(false) whenever(APP.hasRequestPermission(BROADER_PERMISSION)).thenReturn(true) } val record = listModel.transformItem(APP) assertThat(record.hasRequestPermission).isFalse() assertThat(record.hasRequestBroaderPermission).isTrue() } @Test fun filter() = runTest { fun transformItem_notRequestBroaderPermission() = runTest { listModel.broaderPermission = BROADER_PERMISSION with(packageManagers) { whenever(APP.hasRequestPermission(PERMISSION)).thenReturn(false) whenever(APP.hasRequestPermission(BROADER_PERMISSION)).thenReturn(false) } val record = listModel.transformItem(APP) assertThat(record.hasRequestPermission).isFalse() } val record = AppOpPermissionRecord( @Test fun filter() = runTest { with(packageManagers) { whenever(APP.hasRequestPermission(PERMISSION)).thenReturn(false) } val record = AppOpPermissionRecord( app = APP, hasRequestBroaderPermission = false, hasRequestPermission = false, appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT), ) Loading @@ -118,8 +133,10 @@ class AppOpPermissionAppListTest { @Test fun isAllowed_allowed() { val record = AppOpPermissionRecord( val record = AppOpPermissionRecord( app = APP, hasRequestBroaderPermission = false, hasRequestPermission = true, appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_ALLOWED), ) Loading @@ -131,11 +148,11 @@ class AppOpPermissionAppListTest { @Test fun isAllowed_defaultAndHasGrantPermission() { with(packageManagers) { whenever(APP.hasGrantPermission(PERMISSION)).thenReturn(true) } val record = AppOpPermissionRecord( with(packageManagers) { whenever(APP.hasGrantPermission(PERMISSION)).thenReturn(true) } val record = AppOpPermissionRecord( app = APP, hasRequestBroaderPermission = false, hasRequestPermission = true, appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT), ) Loading @@ -147,11 +164,11 @@ class AppOpPermissionAppListTest { @Test fun isAllowed_defaultAndNotGrantPermission() { with(packageManagers) { whenever(APP.hasGrantPermission(PERMISSION)).thenReturn(false) } val record = AppOpPermissionRecord( with(packageManagers) { whenever(APP.hasGrantPermission(PERMISSION)).thenReturn(false) } val record = AppOpPermissionRecord( app = APP, hasRequestBroaderPermission = false, hasRequestPermission = true, appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT), ) Loading @@ -161,10 +178,32 @@ class AppOpPermissionAppListTest { assertThat(isAllowed).isFalse() } @Test fun isAllowed_broaderPermissionTrumps() { listModel.broaderPermission = BROADER_PERMISSION with(packageManagers) { whenever(APP.hasGrantPermission(PERMISSION)).thenReturn(false) whenever(APP.hasGrantPermission(BROADER_PERMISSION)).thenReturn(true) } val record = AppOpPermissionRecord( app = APP, hasRequestBroaderPermission = true, hasRequestPermission = false, appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_ERRORED), ) val isAllowed = getIsAllowed(record) assertThat(isAllowed).isTrue() } @Test fun isAllowed_notAllowed() { val record = AppOpPermissionRecord( val record = AppOpPermissionRecord( app = APP, hasRequestBroaderPermission = false, hasRequestPermission = true, appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_ERRORED), ) Loading @@ -176,8 +215,10 @@ class AppOpPermissionAppListTest { @Test fun isChangeable_notRequestPermission() { val record = AppOpPermissionRecord( val record = AppOpPermissionRecord( app = APP, hasRequestBroaderPermission = false, hasRequestPermission = false, appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT), ) Loading @@ -189,8 +230,10 @@ class AppOpPermissionAppListTest { @Test fun isChangeable_notChangeablePackages() { val record = AppOpPermissionRecord( val record = AppOpPermissionRecord( app = NOT_CHANGEABLE_APP, hasRequestBroaderPermission = false, hasRequestPermission = true, appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT), ) Loading @@ -202,8 +245,10 @@ class AppOpPermissionAppListTest { @Test fun isChangeable_hasRequestPermissionAndChangeable() { val record = AppOpPermissionRecord( val record = AppOpPermissionRecord( app = APP, hasRequestBroaderPermission = false, hasRequestPermission = true, appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT), ) Loading @@ -213,11 +258,29 @@ class AppOpPermissionAppListTest { assertThat(isChangeable).isTrue() } @Test fun isChangeable_broaderPermissionTrumps() { listModel.broaderPermission = BROADER_PERMISSION val record = AppOpPermissionRecord( app = APP, hasRequestBroaderPermission = true, hasRequestPermission = true, appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT), ) val isChangeable = listModel.isChangeable(record) assertThat(isChangeable).isFalse() } @Test fun setAllowed() { val appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT) val record = AppOpPermissionRecord( val record = AppOpPermissionRecord( app = APP, hasRequestBroaderPermission = false, hasRequestPermission = true, appOpsController = appOpsController, ) Loading @@ -239,9 +302,7 @@ class AppOpPermissionAppListTest { private fun getIsAllowed(record: AppOpPermissionRecord): Boolean? { lateinit var isAllowedState: State<Boolean?> composeTestRule.setContent { isAllowedState = listModel.isAllowed(record) } composeTestRule.setContent { isAllowedState = listModel.isAllowed(record) } return isAllowedState.value } Loading @@ -250,8 +311,12 @@ class AppOpPermissionAppListTest { override val pageTitleResId = R.string.test_app_op_permission_title override val switchTitleResId = R.string.test_app_op_permission_switch_title override val footerResId = R.string.test_app_op_permission_footer override val appOp = AppOpsManager.OP_MANAGE_MEDIA override val permission = PERMISSION override val permissionHasAppopFlag = true override var broaderPermission: String? = null override var setModeByUid = false } Loading @@ -259,12 +324,9 @@ class AppOpPermissionAppListTest { const val USER_ID = 0 const val PACKAGE_NAME = "package.name" const val PERMISSION = "PERMISSION" val APP = ApplicationInfo().apply { packageName = PACKAGE_NAME } val NOT_CHANGEABLE_APP = ApplicationInfo().apply { packageName = "android" } const val BROADER_PERMISSION = "BROADER_PERMISSION" val APP = ApplicationInfo().apply { packageName = PACKAGE_NAME } val NOT_CHANGEABLE_APP = ApplicationInfo().apply { packageName = "android" } } } Loading