diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppOpsController.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppOpsController.kt index c08169e888520..e8788048776ef 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppOpsController.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppOpsController.kt @@ -41,6 +41,7 @@ class AppOpsController( context: Context, private val app: ApplicationInfo, private val op: Int, + private val setModeByUid: Boolean = false, ) : IAppOpsController { private val appOpsManager = context.appOpsManager @@ -49,7 +50,11 @@ class AppOpsController( override fun setAllowed(allowed: Boolean) { val mode = if (allowed) MODE_ALLOWED else MODE_ERRORED - appOpsManager.setMode(op, app.uid, app.packageName, mode) + if (setModeByUid) { + appOpsManager.setUidMode(op, app.uid, mode) + } else { + appOpsManager.setMode(op, app.uid, app.packageName, mode) + } _mode.postValue(mode) } diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt index a3578321a0986..ee21b81fe92ac 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt @@ -49,6 +49,13 @@ abstract class AppOpPermissionListModel( abstract val appOp: Int abstract val permission: String + /** + * Use AppOpsManager#setUidMode() instead of AppOpsManager#setMode() when set allowed. + * + * Security related app-ops should be set with setUidMode() instead of setMode(). + */ + open val setModeByUid = false + /** These not changeable packages will also be hidden from app list. */ private val notChangeablePackages = setOf("android", "com.android.systemui", context.packageName) @@ -61,7 +68,7 @@ abstract class AppOpPermissionListModel( AppOpPermissionRecord( app = app, hasRequestPermission = app.packageName in packageNames, - appOpsController = AppOpsController(context = context, app = app, op = appOp), + appOpsController = createAppOpsController(app), ) } } @@ -69,7 +76,14 @@ abstract class AppOpPermissionListModel( override fun transformItem(app: ApplicationInfo) = AppOpPermissionRecord( app = app, hasRequestPermission = with(packageManagers) { app.hasRequestPermission(permission) }, - appOpsController = AppOpsController(context = context, app = app, op = appOp), + appOpsController = createAppOpsController(app), + ) + + private fun createAppOpsController(app: ApplicationInfo) = AppOpsController( + context = context, + app = app, + op = appOp, + setModeByUid = setModeByUid, ) override fun filter(userIdFlow: Flow, recordListFlow: Flow>) = diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppOpsControllerTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppOpsControllerTest.kt new file mode 100644 index 0000000000000..668bfdfd7e323 --- /dev/null +++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppOpsControllerTest.kt @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.spaprivileged.model.app + +import android.app.AppOpsManager +import android.app.AppOpsManager.MODE_ALLOWED +import android.app.AppOpsManager.MODE_ERRORED +import android.content.Context +import android.content.pm.ApplicationInfo +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settingslib.spaprivileged.framework.common.appOpsManager +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.verify +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() + + @Spy + private val context: Context = ApplicationProvider.getApplicationContext() + + @Mock + private lateinit var appOpsManager: AppOpsManager + + @Before + fun setUp() { + whenever(context.appOpsManager).thenReturn(appOpsManager) + } + + @Test + fun setAllowed_setToTrue() { + val controller = AppOpsController(context = context, app = APP, op = OP) + + controller.setAllowed(true) + + verify(appOpsManager).setMode(OP, APP.uid, APP.packageName, MODE_ALLOWED) + } + + @Test + fun setAllowed_setToFalse() { + val controller = AppOpsController(context = context, app = APP, op = OP) + + controller.setAllowed(false) + + verify(appOpsManager).setMode(OP, APP.uid, APP.packageName, MODE_ERRORED) + } + + @Test + fun setAllowed_setToTrueByUid() { + val controller = + AppOpsController(context = context, app = APP, op = OP, setModeByUid = true) + + controller.setAllowed(true) + + verify(appOpsManager).setUidMode(OP, APP.uid, MODE_ALLOWED) + } + + @Test + fun setAllowed_setToFalseByUid() { + val controller = + AppOpsController(context = context, app = APP, op = OP, setModeByUid = true) + + controller.setAllowed(false) + + verify(appOpsManager).setUidMode(OP, APP.uid, MODE_ERRORED) + } + + @Test + fun getMode() { + whenever( + appOpsManager.checkOpNoThrow(OP, APP.uid, APP.packageName) + ).thenReturn(MODE_ALLOWED) + val controller = AppOpsController(context = context, app = APP, op = OP) + + val mode = controller.getMode() + + assertThat(mode).isEqualTo(MODE_ALLOWED) + } + + private companion object { + const val OP = 1 + val APP = ApplicationInfo().apply { + packageName = "package.name" + uid = 123 + } + } +} \ No newline at end of file diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppListTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppListTest.kt index f1d9abe86804e..dd5db86f72b13 100644 --- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppListTest.kt +++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppListTest.kt @@ -25,6 +25,7 @@ import androidx.lifecycle.liveData import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull +import com.android.settingslib.spaprivileged.framework.common.appOpsManager import com.android.settingslib.spaprivileged.model.app.IAppOpsController import com.android.settingslib.spaprivileged.model.app.IPackageManagers import com.android.settingslib.spaprivileged.test.R @@ -37,6 +38,8 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.Spy import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule import org.mockito.Mockito.`when` as whenever @@ -50,17 +53,20 @@ class AppOpPermissionAppListTest { @get:Rule val composeTestRule = createComposeRule() + @Spy private val context: Context = ApplicationProvider.getApplicationContext() @Mock private lateinit var packageManagers: IPackageManagers + @Mock + private lateinit var appOpsManager: AppOpsManager + private lateinit var listModel: TestAppOpPermissionAppListModel @Before - fun setUp() = runTest { - whenever(packageManagers.getAppOpPermissionPackages(USER_ID, PERMISSION)) - .thenReturn(emptySet()) + fun setUp() { + whenever(context.appOpsManager).thenReturn(appOpsManager) listModel = TestAppOpPermissionAppListModel() } @@ -221,6 +227,16 @@ class AppOpPermissionAppListTest { assertThat(appOpsController.setAllowedCalledWith).isTrue() } + @Test + fun setAllowed_setModeByUid() { + listModel.setModeByUid = true + val record = listModel.transformItem(APP) + + listModel.setAllowed(record = record, newAllowed = true) + + verify(appOpsManager).setUidMode(listModel.appOp, APP.uid, AppOpsManager.MODE_ALLOWED) + } + private fun getIsAllowed(record: AppOpPermissionRecord): Boolean? { lateinit var isAllowedState: State composeTestRule.setContent { @@ -236,6 +252,7 @@ class AppOpPermissionAppListTest { override val footerResId = R.string.test_app_op_permission_footer override val appOp = AppOpsManager.OP_MANAGE_MEDIA override val permission = PERMISSION + override var setModeByUid = false } private companion object {