Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7b61876a authored by Chaohui Wang's avatar Chaohui Wang Committed by Android (Google) Code Review
Browse files

Merge "Add uninstall updates & uninstall for all users"

parents 6dbf883b 9b65b158
Loading
Loading
Loading
Loading
+12 −18
Original line number Diff line number Diff line
@@ -17,19 +17,18 @@
package com.android.settings.spa.app.appsettings

import android.app.ActivityManager
import android.app.admin.DevicePolicyManager
import android.content.ComponentName
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.os.UserManager
import com.android.settingslib.RestrictedLockUtilsInternal
import com.android.settingslib.Utils
import com.android.settingslib.spaprivileged.model.app.userId
import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager
import com.android.settingslib.spaprivileged.model.app.isDisallowControl

class AppButtonRepository(private val context: Context) {
    private val packageManager = context.packageManager
    private val devicePolicyManager = context.getSystemService(DevicePolicyManager::class.java)!!
    private val devicePolicyManager = context.devicePolicyManager

    /**
     * Checks whether the given application is disallowed from modifying.
@@ -41,20 +40,10 @@ class AppButtonRepository(private val context: Context) {
        // If the uninstallation intent is already queued, disable the button.
        devicePolicyManager.isUninstallInQueue(app.packageName) -> true

        RestrictedLockUtilsInternal.hasBaseUserRestriction(
            context, UserManager.DISALLOW_APPS_CONTROL, app.userId
        ) -> true

        else -> false
        else -> app.isDisallowControl(context)
    }

    /**
     * Checks whether the given application is an active admin.
     */
    fun isActiveAdmin(app: ApplicationInfo): Boolean =
        devicePolicyManager.packageHasActiveAdmins(app.packageName, app.userId)

    fun getHomePackageInfo(): AppUninstallButton.HomePackages {
    fun getHomePackageInfo(): HomePackages {
        val homePackages = mutableSetOf<String>()
        val homeActivities = ArrayList<ResolveInfo>()
        val currentDefaultHome = packageManager.getHomeActivities(homeActivities)
@@ -66,7 +55,7 @@ class AppButtonRepository(private val context: Context) {
                homePackages.add(metaPackageName)
            }
        }
        return AppUninstallButton.HomePackages(homePackages, currentDefaultHome)
        return HomePackages(homePackages, currentDefaultHome)
    }

    private fun signaturesMatch(packageName1: String, packageName2: String): Boolean = try {
@@ -75,4 +64,9 @@ class AppButtonRepository(private val context: Context) {
        // e.g. named alternate package not found during lookup; this is an expected case sometimes
        false
    }

    data class HomePackages(
        val homePackages: Set<String>,
        val currentDefaultHome: ComponentName?,
    )
}
+7 −8
Original line number Diff line number Diff line
@@ -16,10 +16,7 @@

package com.android.settings.spa.app.appsettings

import android.app.admin.DevicePolicyManager
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.os.UserManager
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ArrowCircleDown
import androidx.compose.material.icons.outlined.HideSource
@@ -34,10 +31,12 @@ import androidx.compose.ui.res.stringResource
import com.android.settings.R
import com.android.settings.Utils
import com.android.settings.overlay.FeatureFactory
import com.android.settingslib.Utils as SettingsLibUtils
import com.android.settingslib.spa.widget.button.ActionButton
import com.android.settingslib.spaprivileged.model.app.hasFlag
import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager
import com.android.settingslib.spaprivileged.framework.common.userManager
import com.android.settingslib.spaprivileged.model.app.isActiveAdmin
import com.android.settingslib.spaprivileged.model.app.isDisabledUntilUsed
import com.android.settingslib.Utils as SettingsLibUtils

class AppDisableButton(
    private val packageInfoPresenter: PackageInfoPresenter,
@@ -46,8 +45,8 @@ class AppDisableButton(
    private val appButtonRepository = AppButtonRepository(context)
    private val resources = context.resources
    private val packageManager = context.packageManager
    private val userManager = context.getSystemService(UserManager::class.java)!!
    private val devicePolicyManager = context.getSystemService(DevicePolicyManager::class.java)!!
    private val userManager = context.userManager
    private val devicePolicyManager = context.devicePolicyManager
    private val applicationFeatureProvider =
        FeatureFactory.getFactory(context).getApplicationFeatureProvider(context)

@@ -84,7 +83,7 @@ class AppDisableButton(
            SettingsLibUtils.isSystemPackage(resources, packageManager, packageInfo) -> false

            // If this is a device admin, it can't be disabled.
            appButtonRepository.isActiveAdmin(app) -> false
            app.isActiveAdmin(context) -> false

            // We don't allow disabling DO/PO on *any* users if it's a system app, because
            // "disabling" is actually "downgrade to the system version + disable", and "downgrade"
+2 −1
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
import com.android.settingslib.RestrictedLockUtilsInternal
import com.android.settingslib.spa.widget.button.ActionButton
import com.android.settingslib.spaprivileged.model.app.hasFlag
import com.android.settingslib.spaprivileged.model.app.isActiveAdmin
import com.android.settingslib.spaprivileged.model.app.userId

class AppForceStopButton(
@@ -61,7 +62,7 @@ class AppForceStopButton(
     */
    private fun isForceStopButtonEnable(app: ApplicationInfo): Boolean = when {
        // User can't force stop device admin.
        appButtonRepository.isActiveAdmin(app) -> false
        app.isActiveAdmin(context) -> false

        appButtonRepository.isDisallowControl(app) -> false

+8 −3
Original line number Diff line number Diff line
@@ -60,7 +60,7 @@ object AppSettingsProvider : SettingsPageProvider {
            PackageInfoPresenter(context, packageName, userId, coroutineScope)
        }
        AppSettings(packageInfoPresenter)
        packageInfoPresenter.PageCloser()
        packageInfoPresenter.PackageRemoveDetector()
    }

    @Composable
@@ -77,7 +77,13 @@ object AppSettingsProvider : SettingsPageProvider {
@Composable
private fun AppSettings(packageInfoPresenter: PackageInfoPresenter) {
    val packageInfo = packageInfoPresenter.flow.collectAsState().value ?: return
    RegularScaffold(title = stringResource(R.string.application_info_label)) {
    val app = packageInfo.applicationInfo
    RegularScaffold(
        title = stringResource(R.string.application_info_label),
        actions = {
            AppSettingsMoreOptions(packageInfoPresenter, app)
        }
    ) {
        val appInfoProvider = remember { AppInfoProvider(packageInfo) }

        appInfoProvider.AppInfo()
@@ -85,7 +91,6 @@ private fun AppSettings(packageInfoPresenter: PackageInfoPresenter) {
        AppButtons(packageInfoPresenter)

        Category(title = stringResource(R.string.advanced_apps)) {
            val app = packageInfo.applicationInfo
            DisplayOverOtherAppsAppListProvider.InfoPageEntryItem(app)
            ModifySystemSettingsAppListProvider.InfoPageEntryItem(app)
            PictureInPictureListProvider.InfoPageEntryItem(app)
+85 −0
Original line number Diff line number Diff line
/*
 * 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.settings.spa.app.appsettings

import android.content.Context
import android.content.pm.ApplicationInfo
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import com.android.settings.R
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.PackageManagers
import com.android.settingslib.spaprivileged.model.app.isActiveAdmin
import com.android.settingslib.spaprivileged.model.app.isDisallowControl
import com.android.settingslib.spaprivileged.model.app.userId

@Composable
fun AppSettingsMoreOptions(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
        )
    }
    if (isProfileOrDeviceOwner) return
    val shownUninstallUpdates = remember(app) { isShowUninstallUpdates(context, app) }
    val shownUninstallForAllUsers = remember(app) { isShowUninstallForAllUsers(context, app) }
    if (!shownUninstallUpdates && !shownUninstallForAllUsers) return
    MoreOptionsAction { onDismissRequest ->
        if (shownUninstallUpdates) {
            DropdownMenuItem(
                text = { Text(stringResource(R.string.app_factory_reset)) },
                onClick = {
                    onDismissRequest()
                    packageInfoPresenter.startUninstallActivity(forAllUsers = false)
                },
            )
        }
        if (shownUninstallForAllUsers) {
            DropdownMenuItem(
                text = { Text(stringResource(R.string.uninstall_all_users_text)) },
                onClick = {
                    onDismissRequest()
                    packageInfoPresenter.startUninstallActivity(forAllUsers = true)
                },
            )
        }
    }
}

private fun isShowUninstallUpdates(context: Context, app: ApplicationInfo): Boolean =
    app.isUpdatedSystemApp && context.userManager.isUserAdmin(app.userId) &&
        !app.isDisallowControl(context) &&
        !context.resources.getBoolean(R.bool.config_disable_uninstall_update)

private fun isShowUninstallForAllUsers(context: Context, app: ApplicationInfo): Boolean =
    app.userId == 0 && !app.isSystemApp && !app.isInstantApp && !app.isActiveAdmin(context) &&
        isOtherUserHasInstallPackage(context, app)

private fun isOtherUserHasInstallPackage(context: Context, app: ApplicationInfo): Boolean =
    context.userManager.aliveUsers
        .filter { it.id != app.userId }
        .any { PackageManagers.isPackageInstalledAsUser(app.packageName, it.id) }
Loading