From 4e35f2596b522fe49efb14ce64a09c44e5f0dcd9 Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Thu, 29 Sep 2022 20:42:19 +0800 Subject: [PATCH 1/2] Apply restricted by admin to the disable button Currently, we apply the same device policy for both the uninstallation and disable button. Also, not double check whether a package is admin (device owner or profile owner). Bug: 236346018 Test: Manual on App Settings page Change-Id: If081fd8ff5422eeac931076a18f73632132a528c --- .../app/appsettings/AppButtonRepository.kt | 13 +++++++++ .../spa/app/appsettings/AppDisableButton.kt | 16 ++++++----- .../app/appsettings/AppSettingsMoreOptions.kt | 3 +- .../spa/app/appsettings/AppUninstallButton.kt | 28 ++----------------- 4 files changed, 25 insertions(+), 35 deletions(-) diff --git a/src/com/android/settings/spa/app/appsettings/AppButtonRepository.kt b/src/com/android/settings/spa/app/appsettings/AppButtonRepository.kt index c5e84ae886d..37801989b1e 100644 --- a/src/com/android/settings/spa/app/appsettings/AppButtonRepository.kt +++ b/src/com/android/settings/spa/app/appsettings/AppButtonRepository.kt @@ -22,9 +22,12 @@ import android.content.Context import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.content.pm.ResolveInfo +import com.android.settingslib.RestrictedLockUtils +import com.android.settingslib.RestrictedLockUtilsInternal import com.android.settingslib.Utils import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager import com.android.settingslib.spaprivileged.model.app.isDisallowControl +import com.android.settingslib.spaprivileged.model.app.userId class AppButtonRepository(private val context: Context) { private val packageManager = context.packageManager @@ -43,6 +46,16 @@ class AppButtonRepository(private val context: Context) { else -> app.isDisallowControl(context) } + /** + * Checks whether uninstall is blocked by admin. + */ + fun isUninstallBlockedByAdmin(app: ApplicationInfo): Boolean = + RestrictedLockUtilsInternal.checkIfUninstallBlocked(context, app.packageName, app.userId) + ?.let { admin -> + RestrictedLockUtils.sendShowAdminSupportDetailsIntent(context, admin) + true + } ?: false + fun getHomePackageInfo(): HomePackages { val homePackages = mutableSetOf() val homeActivities = ArrayList() diff --git a/src/com/android/settings/spa/app/appsettings/AppDisableButton.kt b/src/com/android/settings/spa/app/appsettings/AppDisableButton.kt index cc5a76e2f99..0d48728bfd1 100644 --- a/src/com/android/settings/spa/app/appsettings/AppDisableButton.kt +++ b/src/com/android/settings/spa/app/appsettings/AppDisableButton.kt @@ -16,6 +16,7 @@ package com.android.settings.spa.app.appsettings +import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.ArrowCircleDown @@ -34,7 +35,6 @@ import com.android.settings.overlay.FeatureFactory import com.android.settingslib.spa.widget.button.ActionButton 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 @@ -58,7 +58,7 @@ class AppDisableButton( return when { app.enabled && !app.isDisabledUntilUsed -> { - disableButton(enabled = isDisableButtonEnabled(packageInfo)) + disableButton(app = app, enabled = isDisableButtonEnabled(packageInfo)) } else -> enableButton() @@ -82,9 +82,6 @@ class AppDisableButton( // signed with the system certificate. SettingsLibUtils.isSystemPackage(resources, packageManager, packageInfo) -> false - // If this is a device admin, it can't be disabled. - 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" // will clear data on all users. @@ -99,11 +96,16 @@ class AppDisableButton( } } - private fun disableButton(enabled: Boolean) = ActionButton( + private fun disableButton(app: ApplicationInfo, enabled: Boolean) = ActionButton( text = context.getString(R.string.disable_text), imageVector = Icons.Outlined.HideSource, enabled = enabled, - ) { openConfirmDialog = true } + ) { + // Currently we apply the same device policy for both the uninstallation and disable button. + if (!appButtonRepository.isUninstallBlockedByAdmin(app)) { + openConfirmDialog = true + } + } private fun enableButton() = ActionButton( text = context.getString(R.string.enable_text), diff --git a/src/com/android/settings/spa/app/appsettings/AppSettingsMoreOptions.kt b/src/com/android/settings/spa/app/appsettings/AppSettingsMoreOptions.kt index d17ff33bf39..20415c3b028 100644 --- a/src/com/android/settings/spa/app/appsettings/AppSettingsMoreOptions.kt +++ b/src/com/android/settings/spa/app/appsettings/AppSettingsMoreOptions.kt @@ -30,7 +30,6 @@ 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 @@ -76,7 +75,7 @@ private fun isShowUninstallUpdates(context: Context, app: ApplicationInfo): Bool !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) && + app.userId == 0 && !app.isSystemApp && !app.isInstantApp && isOtherUserHasInstallPackage(context, app) private fun isOtherUserHasInstallPackage(context: Context, app: ApplicationInfo): Boolean = diff --git a/src/com/android/settings/spa/app/appsettings/AppUninstallButton.kt b/src/com/android/settings/spa/app/appsettings/AppUninstallButton.kt index cdb2cd01e8f..e85adc8cad5 100644 --- a/src/com/android/settings/spa/app/appsettings/AppUninstallButton.kt +++ b/src/com/android/settings/spa/app/appsettings/AppUninstallButton.kt @@ -16,32 +16,21 @@ package com.android.settings.spa.app.appsettings -import android.app.settings.SettingsEnums -import android.content.ComponentName -import android.content.Intent import android.content.om.OverlayManager import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo -import android.net.Uri import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Delete import com.android.settings.R -import com.android.settings.Utils -import com.android.settings.applications.specialaccess.deviceadmin.DeviceAdminAdd -import com.android.settingslib.RestrictedLockUtils -import com.android.settingslib.RestrictedLockUtilsInternal import com.android.settingslib.spa.widget.button.ActionButton -import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager import com.android.settingslib.spaprivileged.model.app.hasFlag import com.android.settingslib.spaprivileged.model.app.isActiveAdmin import com.android.settingslib.spaprivileged.model.app.userHandle -import com.android.settingslib.spaprivileged.model.app.userId class AppUninstallButton(private val packageInfoPresenter: PackageInfoPresenter) { private val context = packageInfoPresenter.context private val appButtonRepository = AppButtonRepository(context) private val overlayManager = context.getSystemService(OverlayManager::class.java)!! - private val devicePolicyManager = context.devicePolicyManager fun getActionButton(packageInfo: PackageInfo): ActionButton? { val app = packageInfo.applicationInfo @@ -54,7 +43,7 @@ class AppUninstallButton(private val packageInfoPresenter: PackageInfoPresenter) !app.hasFlag(ApplicationInfo.FLAG_INSTALLED) -> false // Not allow to uninstall DO/PO. - Utils.isProfileOrDeviceOwner(devicePolicyManager, app.packageName, app.userId) -> false + app.isActiveAdmin(context) -> false appButtonRepository.isDisallowControl(app) -> false @@ -99,20 +88,7 @@ class AppUninstallButton(private val packageInfoPresenter: PackageInfoPresenter) ) { onUninstallClicked(app) } private fun onUninstallClicked(app: ApplicationInfo) { - if (app.isActiveAdmin(context)) { - packageInfoPresenter.logAction(SettingsEnums.ACTION_SETTINGS_UNINSTALL_DEVICE_ADMIN) - val intent = Intent(context, DeviceAdminAdd::class.java).apply { - putExtra(DeviceAdminAdd.EXTRA_DEVICE_ADMIN_PACKAGE_NAME, app.packageName) - } - context.startActivityAsUser(intent, app.userHandle) - return - } - RestrictedLockUtilsInternal.checkIfUninstallBlocked( - context, app.packageName, app.userId - )?.let { admin -> - RestrictedLockUtils.sendShowAdminSupportDetailsIntent(context, admin) - return - } + if (appButtonRepository.isUninstallBlockedByAdmin(app)) return packageInfoPresenter.startUninstallActivity() } } From a947e88c9fe57768fd6f74ed4cc934261ca2b3ea Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Sun, 9 Oct 2022 15:42:01 +0800 Subject: [PATCH 2/2] Clean up unused AppPermissionsPreferenceController This one is replaced by AppPermissionPreferenceController. Bug: 234570979 Test: m Settings Change-Id: Id9d16caf33cdea626f79a3cd142dfcde2c54769f --- res/values/strings.xml | 4 - .../AppPermissionsPreferenceController.java | 122 --------------- ...ppPermissionsPreferenceControllerTest.java | 146 ------------------ 3 files changed, 272 deletions(-) delete mode 100644 src/com/android/settings/applications/AppPermissionsPreferenceController.java delete mode 100644 tests/robotests/src/com/android/settings/applications/AppPermissionsPreferenceControllerTest.java diff --git a/res/values/strings.xml b/res/values/strings.xml index b569f27e80a..d2128f6d118 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -8832,10 +8832,6 @@ Permission manager - - Apps using %1$s - - Apps using %1$s, and more Tap to wake diff --git a/src/com/android/settings/applications/AppPermissionsPreferenceController.java b/src/com/android/settings/applications/AppPermissionsPreferenceController.java deleted file mode 100644 index 4458ea4105c..00000000000 --- a/src/com/android/settings/applications/AppPermissionsPreferenceController.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2017 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.applications; - -import android.content.Context; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.icu.text.ListFormatter; -import android.util.ArraySet; - -import androidx.annotation.VisibleForTesting; -import androidx.preference.Preference; - -import com.android.settings.R; -import com.android.settings.core.BasePreferenceController; -import com.android.settingslib.applications.PermissionsSummaryHelper; - -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -public class AppPermissionsPreferenceController extends BasePreferenceController { - - private static final String TAG = "AppPermissionPrefCtrl"; - private static final int NUM_PACKAGE_TO_CHECK = 4; - - @VisibleForTesting - static int NUM_PERMISSIONS_TO_SHOW = 3; - - private final PackageManager mPackageManager; - private final Set mPermissionGroups; - - private final PermissionsSummaryHelper.PermissionsResultCallback mPermissionsCallback = - new PermissionsSummaryHelper.PermissionsResultCallback() { - @Override - public void onPermissionSummaryResult(int standardGrantedPermissionCount, - int requestedPermissionCount, int additionalGrantedPermissionCount, - List grantedGroupLabels) { - updateSummary(grantedGroupLabels); - } - }; - - @VisibleForTesting - int mNumPackageChecked; - - private Preference mPreference; - - public AppPermissionsPreferenceController(Context context, String preferenceKey) { - super(context, preferenceKey); - mPackageManager = context.getPackageManager(); - mPermissionGroups = new ArraySet<>(); - } - - @Override - public int getAvailabilityStatus() { - return AVAILABLE; - } - - @Override - public void updateState(Preference preference) { - mPreference = preference; - mNumPackageChecked = 0; - queryPermissionSummary(); - } - - @VisibleForTesting - void queryPermissionSummary() { - final List installedPackages = - mPackageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS); - // Here we only get the first four apps and check their permissions. - final List packagesWithPermission = installedPackages.stream() - .filter(pInfo -> pInfo.permissions != null) - .limit(NUM_PACKAGE_TO_CHECK) - .collect(Collectors.toList()); - - for (PackageInfo installedPackage : packagesWithPermission) { - PermissionsSummaryHelper.getPermissionSummary(mContext, - installedPackage.packageName, mPermissionsCallback); - } - } - - @VisibleForTesting - void updateSummary(List grantedGroupLabels) { - mPermissionGroups.addAll(grantedGroupLabels); - mNumPackageChecked++; - - if (mNumPackageChecked < NUM_PACKAGE_TO_CHECK) { - return; - } - - final List permissionsToShow = mPermissionGroups.stream() - .limit(NUM_PERMISSIONS_TO_SHOW) - .collect(Collectors.toList()); - final boolean isMoreShowed = mPermissionGroups.size() > NUM_PERMISSIONS_TO_SHOW; - CharSequence summary; - - if (!permissionsToShow.isEmpty()) { - if (isMoreShowed) { - summary = mContext.getString(R.string.app_permissions_summary_more, - ListFormatter.getInstance().format(permissionsToShow).toLowerCase()); - } else { - summary = mContext.getString(R.string.app_permissions_summary, - ListFormatter.getInstance().format(permissionsToShow).toLowerCase()); - } - } else { - summary = mContext.getString( - R.string.runtime_permissions_summary_no_permissions_granted); - } - mPreference.setSummary(summary); - } -} \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/applications/AppPermissionsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/AppPermissionsPreferenceControllerTest.java deleted file mode 100644 index dfa938313b3..00000000000 --- a/tests/robotests/src/com/android/settings/applications/AppPermissionsPreferenceControllerTest.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (C) 2017 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.applications; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; - -import android.content.Context; -import android.content.pm.PackageManager.NameNotFoundException; - -import androidx.preference.Preference; - -import com.android.settings.R; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; - -import java.util.ArrayList; -import java.util.List; - -@RunWith(RobolectricTestRunner.class) -public class AppPermissionsPreferenceControllerTest { - - private Context mContext; - private AppPermissionsPreferenceController mController; - private Preference mPreference; - - @Before - public void setUp() throws NameNotFoundException { - mContext = RuntimeEnvironment.application; - mPreference = spy(new Preference(mContext)); - mController = spy(new AppPermissionsPreferenceController(mContext, "pref_key")); - } - - @Test - public void isAvailable_shouldAlwaysReturnTrue() { - assertThat(mController.isAvailable()).isTrue(); - } - - @Test - public void updateState_shouldResetNumPackageChecked() { - doNothing().when(mController).queryPermissionSummary(); - mController.mNumPackageChecked = 3; - - mController.updateState(mPreference); - - assertThat(mController.mNumPackageChecked).isEqualTo(0); - } - - @Test - public void updateSummary_noGrantedPermission_shouldSetNoPermissionGrantedSummary() { - doNothing().when(mController).queryPermissionSummary(); - mController.updateState(mPreference); - mController.mNumPackageChecked = 3; - - mController.updateSummary(new ArrayList<>()); - - assertThat(mPreference.getSummary()).isEqualTo( - mContext.getString(R.string.runtime_permissions_summary_no_permissions_granted)); - } - - @Test - public void updateSummary_hasOnePermission_shouldSetPermissionAsSummary() { - doNothing().when(mController).queryPermissionSummary(); - mController.updateState(mPreference); - final String permission = "location"; - final ArrayList labels = new ArrayList<>(); - labels.add(permission); - final String summary = "Apps using " + permission; - mController.mNumPackageChecked = 3; - - mController.updateSummary(labels); - - assertThat(mPreference.getSummary()).isEqualTo(summary); - } - - @Test - public void updateSummary_hasThreePermissions_shouldShowThreePermissionAsSummary() { - doNothing().when(mController).queryPermissionSummary(); - mController.updateState(mPreference); - mController.mNumPackageChecked = 3; - final List labels = new ArrayList<>(); - labels.add("Phone"); - labels.add("SMS"); - labels.add("Microphone"); - - mController.updateSummary(labels); - - final String summary = "Apps using microphone, sms, and phone"; - assertThat(mPreference.getSummary()).isEqualTo(summary); - } - - @Test - public void updateSummary_hasFivePermissions_shouldShowThreePermissionsAndMoreAsSummary() { - doNothing().when(mController).queryPermissionSummary(); - mController.updateState(mPreference); - mController.mNumPackageChecked = 3; - final List labels = new ArrayList<>(); - labels.add("Phone"); - labels.add("SMS"); - labels.add("Microphone"); - labels.add("Contacts"); - labels.add("Camera"); - labels.add("Location"); - - mController.updateSummary(labels); - - final String summary = "Apps using microphone, contacts, and sms, and more"; - assertThat(mPreference.getSummary()).isEqualTo(summary); - } - - @Test - public void updateSummary_notReachCallbackCount_shouldNotSetSummary() { - doNothing().when(mController).queryPermissionSummary(); - mController.updateState(mPreference); - final String permission = "location"; - final ArrayList labels = new ArrayList<>(); - labels.add(permission); - - mController.updateSummary(labels); - - verify(mPreference, never()).setSummary(anyString()); - } -}