Loading packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt +39 −24 Original line number Diff line number Diff line Loading @@ -16,9 +16,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.app.AppOpsManager import android.content.Context import android.content.pm.ApplicationInfo import androidx.compose.runtime.Composable Loading Loading @@ -64,7 +62,7 @@ abstract class AppOpPermissionListModel( */ open val permissionHasAppOpFlag: Boolean = true open val modeForNotAllowed: Int = MODE_ERRORED open val modeForNotAllowed: Int = AppOpsManager.MODE_ERRORED /** * Use AppOpsManager#setUidMode() instead of AppOpsManager#setMode() when set allowed. Loading Loading @@ -130,34 +128,51 @@ abstract class AppOpPermissionListModel( override fun filter(userIdFlow: Flow<Int>, recordListFlow: Flow<List<AppOpPermissionRecord>>) = recordListFlow.filterItem(::isChangeable) @Composable override fun isAllowed(record: AppOpPermissionRecord): () -> Boolean? = isAllowed( record = record, appOpsController = record.appOpsController, permission = permission, packageManagers = packageManagers, ) override fun isChangeable(record: AppOpPermissionRecord) = record.hasRequestPermission && !record.hasRequestBroaderPermission && record.app.packageName !in notChangeablePackages override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) { record.appOpsController.setAllowed(newAllowed) } } /** * Defining the default behavior as permissible as long as the package requested this permission * (This means pre-M gets approval during install time; M apps gets approval during runtime). */ @Composable override fun isAllowed(record: AppOpPermissionRecord): () -> Boolean? { internal fun isAllowed( record: AppOpPermissionRecord, appOpsController: IAppOpsController, permission: String, packageManagers: IPackageManagers = PackageManagers, ): () -> Boolean? { if (record.hasRequestBroaderPermission) { // Broader permission trumps the specific permission. return { true } } val mode = record.appOpsController.mode.observeAsState() val mode = appOpsController.mode.observeAsState() return { when (mode.value) { null -> null MODE_ALLOWED -> true MODE_DEFAULT -> with(packageManagers) { record.app.hasGrantPermission(permission) } else -> false } } AppOpsManager.MODE_ALLOWED -> true AppOpsManager.MODE_DEFAULT -> { with(packageManagers) { record.app.hasGrantPermission(permission) } } override fun isChangeable(record: AppOpPermissionRecord) = record.hasRequestPermission && !record.hasRequestBroaderPermission && record.app.packageName !in notChangeablePackages override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) { record.appOpsController.setAllowed(newAllowed) else -> false } } } packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt +28 −35 Original line number Diff line number Diff line Loading @@ -16,19 +16,16 @@ package com.android.settingslib.spaprivileged.template.app import android.content.Context import android.content.pm.ApplicationInfo import android.os.Bundle import androidx.annotation.VisibleForTesting import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.core.os.bundleOf import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavType import androidx.navigation.navArgument import com.android.settingslib.spa.framework.common.SettingsEntry Loading @@ -49,6 +46,8 @@ import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProvid import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl import com.android.settingslib.spaprivileged.template.preference.RestrictedSwitchPreference import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn internal class TogglePermissionAppInfoPageProvider( private val appListTemplate: TogglePermissionAppListTemplate, Loading Loading @@ -132,7 +131,7 @@ internal fun <T : AppRecord> TogglePermissionAppListModel<T>.TogglePermissionApp @VisibleForTesting @Composable internal fun TogglePermissionAppListModel<out AppRecord>.TogglePermissionAppInfoPage( internal fun <T : AppRecord> TogglePermissionAppListModel<T>.TogglePermissionAppInfoPage( packageName: String, userId: Int, packageManagers: IPackageManagers = PackageManagers, Loading @@ -145,40 +144,34 @@ internal fun TogglePermissionAppListModel<out AppRecord>.TogglePermissionAppInfo footerContent = { AnnotatedText(footerResId) }, packageManagers = packageManagers, ) { val model = createSwitchModel(checkNotNull(applicationInfo)) val app = applicationInfo ?: return@AppInfoPage val record = rememberRecord(app).value ?: return@AppInfoPage val isAllowed = isAllowed(record) val isChangeable by rememberIsChangeable(record) val switchModel = object : SwitchPreferenceModel { override val title = stringResource(switchTitleResId) override val checked = isAllowed override val changeable = { isChangeable } override val onCheckedChange: (Boolean) -> Unit = { setAllowed(record, it) } } val restrictions = Restrictions(userId, switchRestrictionKeys) RestrictedSwitchPreference(model, restrictions, restrictionsProviderFactory) RestrictedSwitchPreference(switchModel, restrictions, restrictionsProviderFactory) } } @Composable private fun <T : AppRecord> TogglePermissionAppListModel<T>.createSwitchModel( app: ApplicationInfo, ): TogglePermissionSwitchModel<T> { val context = LocalContext.current val record = remember(app) { transformItem(app) } val isAllowed = isAllowed(record) return remember(record) { TogglePermissionSwitchModel(context, this, record, isAllowed) } .also { model -> LaunchedEffect(model, Dispatchers.IO) { model.initState() } } } private fun <T : AppRecord> TogglePermissionAppListModel<T>.rememberRecord(app: ApplicationInfo) = remember(app) { flow { emit(transformItem(app)) }.flowOn(Dispatchers.Default) }.collectAsStateWithLifecycle(initialValue = null) private class TogglePermissionSwitchModel<T : AppRecord>( context: Context, private val listModel: TogglePermissionAppListModel<T>, private val record: T, isAllowed: () -> Boolean?, ) : SwitchPreferenceModel { private var appChangeable by mutableStateOf(true) override val title: String = context.getString(listModel.switchTitleResId) override val checked = isAllowed override val changeable = { appChangeable } fun initState() { appChangeable = listModel.isChangeable(record) } override val onCheckedChange: (Boolean) -> Unit = { newChecked -> listModel.setAllowed(record, newChecked) } } @Composable private fun <T : AppRecord> TogglePermissionAppListModel<T>.rememberIsChangeable(record: T) = remember(record) { flow { emit(isChangeable(record)) }.flowOn(Dispatchers.Default) }.collectAsStateWithLifecycle(initialValue = false) packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppListTest.kt +15 −22 Original line number Diff line number Diff line Loading @@ -32,44 +32,37 @@ import com.android.settingslib.spaprivileged.test.R import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Spy import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule import org.mockito.kotlin.any import org.mockito.kotlin.doNothing import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.spy import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) class AppOpPermissionAppListTest { @get:Rule val mockito: MockitoRule = MockitoJUnit.rule() @get:Rule val composeTestRule = createComposeRule() @get:Rule val composeTestRule = createComposeRule() private val packageManagers = mock<IPackageManagers>() @Spy private val context: Context = ApplicationProvider.getApplicationContext() private val appOpsManager = mock<AppOpsManager>() @Mock private lateinit var packageManagers: IPackageManagers @Mock private lateinit var appOpsManager: AppOpsManager @Mock private lateinit var packageManager: PackageManager private lateinit var listModel: TestAppOpPermissionAppListModel private val packageManager = mock<PackageManager> { doNothing().whenever(mock).updatePermissionFlags(any(), any(), any(), any(), any()) } @Before fun setUp() { whenever(context.appOpsManager).thenReturn(appOpsManager) whenever(context.packageManager).thenReturn(packageManager) doNothing().whenever(packageManager) .updatePermissionFlags(any(), any(), any(), any(), any()) listModel = TestAppOpPermissionAppListModel() private val context: Context = spy(ApplicationProvider.getApplicationContext()) { on { appOpsManager } doReturn appOpsManager on { packageManager } doReturn packageManager } private val listModel = TestAppOpPermissionAppListModel() @Test fun transformItem_recordHasCorrectApp() { val record = listModel.transformItem(APP) Loading packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPageTest.kt +8 −18 Original line number Diff line number Diff line Loading @@ -38,44 +38,34 @@ import com.android.settingslib.spaprivileged.tests.testutils.FakeRestrictionsPro import com.android.settingslib.spaprivileged.tests.testutils.TestTogglePermissionAppListModel import com.android.settingslib.spaprivileged.tests.testutils.TestTogglePermissionAppListProvider 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.junit.MockitoJUnit import org.mockito.junit.MockitoRule import org.mockito.kotlin.whenever import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock @RunWith(AndroidJUnit4::class) class TogglePermissionAppInfoPageTest { @get:Rule val composeTestRule = createComposeRule() @get:Rule val mockito: MockitoRule = MockitoJUnit.rule() private val context: Context = ApplicationProvider.getApplicationContext() @Mock private lateinit var packageManagers: IPackageManagers private val packageManagers = mock<IPackageManagers> { on { getPackageInfoAsUser(PACKAGE_NAME, USER_ID) } doReturn PACKAGE_INFO } private val fakeNavControllerWrapper = FakeNavControllerWrapper() private val fakeRestrictionsProvider = FakeRestrictionsProvider() private val fakeRestrictionsProvider = FakeRestrictionsProvider().apply { restrictedMode = NoRestricted } private val appListTemplate = TogglePermissionAppListTemplate(listOf(TestTogglePermissionAppListProvider)) private val appInfoPageProvider = TogglePermissionAppInfoPageProvider(appListTemplate) @Before fun setUp() { fakeRestrictionsProvider.restrictedMode = NoRestricted whenever(packageManagers.getPackageInfoAsUser(PACKAGE_NAME, USER_ID)) .thenReturn(PACKAGE_INFO) } @Test fun buildEntry() { val entryList = appInfoPageProvider.buildEntry(null) Loading Loading
packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt +39 −24 Original line number Diff line number Diff line Loading @@ -16,9 +16,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.app.AppOpsManager import android.content.Context import android.content.pm.ApplicationInfo import androidx.compose.runtime.Composable Loading Loading @@ -64,7 +62,7 @@ abstract class AppOpPermissionListModel( */ open val permissionHasAppOpFlag: Boolean = true open val modeForNotAllowed: Int = MODE_ERRORED open val modeForNotAllowed: Int = AppOpsManager.MODE_ERRORED /** * Use AppOpsManager#setUidMode() instead of AppOpsManager#setMode() when set allowed. Loading Loading @@ -130,34 +128,51 @@ abstract class AppOpPermissionListModel( override fun filter(userIdFlow: Flow<Int>, recordListFlow: Flow<List<AppOpPermissionRecord>>) = recordListFlow.filterItem(::isChangeable) @Composable override fun isAllowed(record: AppOpPermissionRecord): () -> Boolean? = isAllowed( record = record, appOpsController = record.appOpsController, permission = permission, packageManagers = packageManagers, ) override fun isChangeable(record: AppOpPermissionRecord) = record.hasRequestPermission && !record.hasRequestBroaderPermission && record.app.packageName !in notChangeablePackages override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) { record.appOpsController.setAllowed(newAllowed) } } /** * Defining the default behavior as permissible as long as the package requested this permission * (This means pre-M gets approval during install time; M apps gets approval during runtime). */ @Composable override fun isAllowed(record: AppOpPermissionRecord): () -> Boolean? { internal fun isAllowed( record: AppOpPermissionRecord, appOpsController: IAppOpsController, permission: String, packageManagers: IPackageManagers = PackageManagers, ): () -> Boolean? { if (record.hasRequestBroaderPermission) { // Broader permission trumps the specific permission. return { true } } val mode = record.appOpsController.mode.observeAsState() val mode = appOpsController.mode.observeAsState() return { when (mode.value) { null -> null MODE_ALLOWED -> true MODE_DEFAULT -> with(packageManagers) { record.app.hasGrantPermission(permission) } else -> false } } AppOpsManager.MODE_ALLOWED -> true AppOpsManager.MODE_DEFAULT -> { with(packageManagers) { record.app.hasGrantPermission(permission) } } override fun isChangeable(record: AppOpPermissionRecord) = record.hasRequestPermission && !record.hasRequestBroaderPermission && record.app.packageName !in notChangeablePackages override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) { record.appOpsController.setAllowed(newAllowed) else -> false } } }
packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt +28 −35 Original line number Diff line number Diff line Loading @@ -16,19 +16,16 @@ package com.android.settingslib.spaprivileged.template.app import android.content.Context import android.content.pm.ApplicationInfo import android.os.Bundle import androidx.annotation.VisibleForTesting import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.core.os.bundleOf import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavType import androidx.navigation.navArgument import com.android.settingslib.spa.framework.common.SettingsEntry Loading @@ -49,6 +46,8 @@ import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProvid import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl import com.android.settingslib.spaprivileged.template.preference.RestrictedSwitchPreference import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn internal class TogglePermissionAppInfoPageProvider( private val appListTemplate: TogglePermissionAppListTemplate, Loading Loading @@ -132,7 +131,7 @@ internal fun <T : AppRecord> TogglePermissionAppListModel<T>.TogglePermissionApp @VisibleForTesting @Composable internal fun TogglePermissionAppListModel<out AppRecord>.TogglePermissionAppInfoPage( internal fun <T : AppRecord> TogglePermissionAppListModel<T>.TogglePermissionAppInfoPage( packageName: String, userId: Int, packageManagers: IPackageManagers = PackageManagers, Loading @@ -145,40 +144,34 @@ internal fun TogglePermissionAppListModel<out AppRecord>.TogglePermissionAppInfo footerContent = { AnnotatedText(footerResId) }, packageManagers = packageManagers, ) { val model = createSwitchModel(checkNotNull(applicationInfo)) val app = applicationInfo ?: return@AppInfoPage val record = rememberRecord(app).value ?: return@AppInfoPage val isAllowed = isAllowed(record) val isChangeable by rememberIsChangeable(record) val switchModel = object : SwitchPreferenceModel { override val title = stringResource(switchTitleResId) override val checked = isAllowed override val changeable = { isChangeable } override val onCheckedChange: (Boolean) -> Unit = { setAllowed(record, it) } } val restrictions = Restrictions(userId, switchRestrictionKeys) RestrictedSwitchPreference(model, restrictions, restrictionsProviderFactory) RestrictedSwitchPreference(switchModel, restrictions, restrictionsProviderFactory) } } @Composable private fun <T : AppRecord> TogglePermissionAppListModel<T>.createSwitchModel( app: ApplicationInfo, ): TogglePermissionSwitchModel<T> { val context = LocalContext.current val record = remember(app) { transformItem(app) } val isAllowed = isAllowed(record) return remember(record) { TogglePermissionSwitchModel(context, this, record, isAllowed) } .also { model -> LaunchedEffect(model, Dispatchers.IO) { model.initState() } } } private fun <T : AppRecord> TogglePermissionAppListModel<T>.rememberRecord(app: ApplicationInfo) = remember(app) { flow { emit(transformItem(app)) }.flowOn(Dispatchers.Default) }.collectAsStateWithLifecycle(initialValue = null) private class TogglePermissionSwitchModel<T : AppRecord>( context: Context, private val listModel: TogglePermissionAppListModel<T>, private val record: T, isAllowed: () -> Boolean?, ) : SwitchPreferenceModel { private var appChangeable by mutableStateOf(true) override val title: String = context.getString(listModel.switchTitleResId) override val checked = isAllowed override val changeable = { appChangeable } fun initState() { appChangeable = listModel.isChangeable(record) } override val onCheckedChange: (Boolean) -> Unit = { newChecked -> listModel.setAllowed(record, newChecked) } } @Composable private fun <T : AppRecord> TogglePermissionAppListModel<T>.rememberIsChangeable(record: T) = remember(record) { flow { emit(isChangeable(record)) }.flowOn(Dispatchers.Default) }.collectAsStateWithLifecycle(initialValue = false)
packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppListTest.kt +15 −22 Original line number Diff line number Diff line Loading @@ -32,44 +32,37 @@ import com.android.settingslib.spaprivileged.test.R import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Spy import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule import org.mockito.kotlin.any import org.mockito.kotlin.doNothing import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.spy import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) class AppOpPermissionAppListTest { @get:Rule val mockito: MockitoRule = MockitoJUnit.rule() @get:Rule val composeTestRule = createComposeRule() @get:Rule val composeTestRule = createComposeRule() private val packageManagers = mock<IPackageManagers>() @Spy private val context: Context = ApplicationProvider.getApplicationContext() private val appOpsManager = mock<AppOpsManager>() @Mock private lateinit var packageManagers: IPackageManagers @Mock private lateinit var appOpsManager: AppOpsManager @Mock private lateinit var packageManager: PackageManager private lateinit var listModel: TestAppOpPermissionAppListModel private val packageManager = mock<PackageManager> { doNothing().whenever(mock).updatePermissionFlags(any(), any(), any(), any(), any()) } @Before fun setUp() { whenever(context.appOpsManager).thenReturn(appOpsManager) whenever(context.packageManager).thenReturn(packageManager) doNothing().whenever(packageManager) .updatePermissionFlags(any(), any(), any(), any(), any()) listModel = TestAppOpPermissionAppListModel() private val context: Context = spy(ApplicationProvider.getApplicationContext()) { on { appOpsManager } doReturn appOpsManager on { packageManager } doReturn packageManager } private val listModel = TestAppOpPermissionAppListModel() @Test fun transformItem_recordHasCorrectApp() { val record = listModel.transformItem(APP) Loading
packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPageTest.kt +8 −18 Original line number Diff line number Diff line Loading @@ -38,44 +38,34 @@ import com.android.settingslib.spaprivileged.tests.testutils.FakeRestrictionsPro import com.android.settingslib.spaprivileged.tests.testutils.TestTogglePermissionAppListModel import com.android.settingslib.spaprivileged.tests.testutils.TestTogglePermissionAppListProvider 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.junit.MockitoJUnit import org.mockito.junit.MockitoRule import org.mockito.kotlin.whenever import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock @RunWith(AndroidJUnit4::class) class TogglePermissionAppInfoPageTest { @get:Rule val composeTestRule = createComposeRule() @get:Rule val mockito: MockitoRule = MockitoJUnit.rule() private val context: Context = ApplicationProvider.getApplicationContext() @Mock private lateinit var packageManagers: IPackageManagers private val packageManagers = mock<IPackageManagers> { on { getPackageInfoAsUser(PACKAGE_NAME, USER_ID) } doReturn PACKAGE_INFO } private val fakeNavControllerWrapper = FakeNavControllerWrapper() private val fakeRestrictionsProvider = FakeRestrictionsProvider() private val fakeRestrictionsProvider = FakeRestrictionsProvider().apply { restrictedMode = NoRestricted } private val appListTemplate = TogglePermissionAppListTemplate(listOf(TestTogglePermissionAppListProvider)) private val appInfoPageProvider = TogglePermissionAppInfoPageProvider(appListTemplate) @Before fun setUp() { fakeRestrictionsProvider.restrictedMode = NoRestricted whenever(packageManagers.getPackageInfoAsUser(PACKAGE_NAME, USER_ID)) .thenReturn(PACKAGE_INFO) } @Test fun buildEntry() { val entryList = appInfoPageProvider.buildEntry(null) Loading