Add restriction to AppInfoSettingsMoreOptions

The DISALLOW_APPS_CONTROL restriction.

Bug: 259492166
Test: Unit test
Test: Manually with Settings
Change-Id: Id92e36194201412a0d5cad71410c08508a3b8aaa
This commit is contained in:
Chaohui Wang
2022-11-28 02:19:29 +08:00
parent bf26483af5
commit 3b05ba6da8
2 changed files with 233 additions and 28 deletions

View File

@@ -18,8 +18,10 @@ package com.android.settings.spa.app.appinfo
import android.content.Context
import android.content.pm.ApplicationInfo
import android.os.UserManager
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.State
import androidx.compose.runtime.produceState
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import com.android.settings.R
@@ -27,48 +29,90 @@ import com.android.settings.Utils
import com.android.settingslib.spa.widget.scaffold.MoreOptionsAction
import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager
import com.android.settingslib.spaprivileged.framework.common.userManager
import com.android.settingslib.spaprivileged.model.app.IPackageManagers
import com.android.settingslib.spaprivileged.model.app.PackageManagers
import com.android.settingslib.spaprivileged.model.app.isDisallowControl
import com.android.settingslib.spaprivileged.model.app.userId
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
import com.android.settingslib.spaprivileged.template.scaffold.RestrictedMenuItem
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable
fun AppInfoSettingsMoreOptions(packageInfoPresenter: PackageInfoPresenter, app: ApplicationInfo) {
val context = LocalContext.current
// We don't allow uninstalling update for DO/PO if it's a system app, because it will clear data
// on all users. We also don't allow uninstalling for all users if it's DO/PO for any user.
val isProfileOrDeviceOwner = remember(app) {
Utils.isProfileOrDeviceOwner(
context.userManager, context.devicePolicyManager, app.packageName
)
fun AppInfoSettingsMoreOptions(
packageInfoPresenter: PackageInfoPresenter,
app: ApplicationInfo,
packageManagers: IPackageManagers = PackageManagers,
) {
val state = app.produceState(packageManagers).value ?: return
when {
// We don't allow uninstalling update for DO/PO if it's a system app, because it will clear
// data on all users. We also don't allow uninstalling for all users if it's DO/PO for any
// user.
state.isProfileOrDeviceOwner -> return
!state.shownUninstallUpdates && !state.shownUninstallForAllUsers -> return
}
if (isProfileOrDeviceOwner) return
val shownUninstallUpdates = remember(app) { isShowUninstallUpdates(context, app) }
val shownUninstallForAllUsers = remember(app) { isShowUninstallForAllUsers(context, app) }
if (!shownUninstallUpdates && !shownUninstallForAllUsers) return
MoreOptionsAction {
if (shownUninstallUpdates) {
MenuItem(text = stringResource(R.string.app_factory_reset)) {
val restrictions =
Restrictions(userId = app.userId, keys = listOf(UserManager.DISALLOW_APPS_CONTROL))
if (state.shownUninstallUpdates) {
RestrictedMenuItem(
text = stringResource(R.string.app_factory_reset),
restrictions = restrictions,
) {
packageInfoPresenter.startUninstallActivity(forAllUsers = false)
}
}
if (shownUninstallForAllUsers) {
MenuItem(text = stringResource(R.string.uninstall_all_users_text)) {
if (state.shownUninstallForAllUsers) {
RestrictedMenuItem(
text = stringResource(R.string.uninstall_all_users_text),
restrictions = restrictions,
) {
packageInfoPresenter.startUninstallActivity(forAllUsers = true)
}
}
}
}
private fun isShowUninstallUpdates(context: Context, app: ApplicationInfo): Boolean =
app.isUpdatedSystemApp && context.userManager.isUserAdmin(app.userId) &&
!app.isDisallowControl(context) &&
private data class AppInfoSettingsMoreOptionsState(
val isProfileOrDeviceOwner: Boolean,
val shownUninstallUpdates: Boolean,
val shownUninstallForAllUsers: Boolean,
)
@Composable
private fun ApplicationInfo.produceState(
packageManagers: IPackageManagers,
): State<AppInfoSettingsMoreOptionsState?> {
val context = LocalContext.current
return produceState<AppInfoSettingsMoreOptionsState?>(initialValue = null, this) {
withContext(Dispatchers.IO) {
value = AppInfoSettingsMoreOptionsState(
isProfileOrDeviceOwner = Utils.isProfileOrDeviceOwner(
context.userManager, context.devicePolicyManager, packageName
),
shownUninstallUpdates = isShowUninstallUpdates(context),
shownUninstallForAllUsers = isShowUninstallForAllUsers(
userManager = context.userManager,
packageManagers = packageManagers,
),
)
}
}
}
private fun ApplicationInfo.isShowUninstallUpdates(context: Context): Boolean =
isUpdatedSystemApp && context.userManager.isUserAdmin(userId) &&
!context.resources.getBoolean(R.bool.config_disable_uninstall_update)
private fun isShowUninstallForAllUsers(context: Context, app: ApplicationInfo): Boolean =
app.userId == 0 && !app.isSystemApp && !app.isInstantApp &&
isOtherUserHasInstallPackage(context, app)
private fun ApplicationInfo.isShowUninstallForAllUsers(
userManager: UserManager,
packageManagers: IPackageManagers,
): Boolean = userId == 0 && !isSystemApp && !isInstantApp &&
isOtherUserHasInstallPackage(userManager, packageManagers)
private fun isOtherUserHasInstallPackage(context: Context, app: ApplicationInfo): Boolean =
context.userManager.aliveUsers
.filter { it.id != app.userId }
.any { PackageManagers.isPackageInstalledAsUser(app.packageName, it.id) }
private fun ApplicationInfo.isOtherUserHasInstallPackage(
userManager: UserManager,
packageManagers: IPackageManagers,
): Boolean = userManager.aliveUsers
.filter { it.id != userId }
.any { packageManagers.isPackageInstalledAsUser(packageName, it.id) }