diff --git a/src/com/android/settings/ActionDisabledByAppOpsDialog.java b/src/com/android/settings/ActionDisabledByAppOpsDialog.java index 15b8503a4c8..be3558eef86 100644 --- a/src/com/android/settings/ActionDisabledByAppOpsDialog.java +++ b/src/com/android/settings/ActionDisabledByAppOpsDialog.java @@ -21,7 +21,6 @@ import android.app.AppOpsManager; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; -import android.util.Log; public class ActionDisabledByAppOpsDialog extends Activity implements DialogInterface.OnDismissListener { diff --git a/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragment.java b/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragment.java index 0dbf05ea5da..4e9cd92d6c0 100644 --- a/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragment.java +++ b/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragment.java @@ -43,6 +43,7 @@ import com.android.settings.R; import com.android.settings.core.InstrumentedFragment; import com.android.settings.core.SubSettingLauncher; import com.android.settings.overlay.FeatureFactory; +import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.accessibility.AccessibilityUtils; import java.util.List; @@ -164,16 +165,9 @@ public class AccessibilityDetailsSettingsFragment extends InstrumentedFragment { if (permittedServices != null && !permittedServices.contains(packageName)) { return false; } - try { - final int mode = mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, - uid, packageName); - final boolean ecmEnabled = getContext().getResources().getBoolean( - com.android.internal.R.bool.config_enhancedConfirmationModeEnabled); - return !ecmEnabled || mode == AppOpsManager.MODE_ALLOWED; - } catch (Exception e) { - // Fallback in case if app ops is not available in testing. - return true; - } + + return !RestrictedLockUtilsInternal.isEnhancedConfirmationRestricted(getContext(), + packageName, AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE); } private AccessibilityServiceInfo getAccessibilityServiceInfo(ComponentName componentName) { diff --git a/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java b/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java index 6f21fb87a30..03bf142f9a2 100644 --- a/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java +++ b/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java @@ -235,10 +235,11 @@ public class RestrictedPreferenceHelper { boolean serviceAllowed = permittedServices == null || permittedServices.contains( preference.getPackageName()); - if (android.security.Flags.extendEcmToAllSettings()) { + if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled() + && android.security.Flags.extendEcmToAllSettings()) { preference.checkEcmRestrictionAndSetDisabled( AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE, - preference.getPackageName(), preference.getUid()); + preference.getPackageName()); if (preference.isDisabledByEcm()) { serviceAllowed = false; } @@ -257,40 +258,39 @@ public class RestrictedPreferenceHelper { preference.setEnabled(false); } } - return; - } - - boolean appOpsAllowed; - if (serviceAllowed) { - try { - final int mode = mAppOps.noteOpNoThrow( - AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, - preference.getUid(), preference.getPackageName()); - final boolean ecmEnabled = mContext.getResources().getBoolean( - com.android.internal.R.bool.config_enhancedConfirmationModeEnabled); - appOpsAllowed = !ecmEnabled || mode == AppOpsManager.MODE_ALLOWED; - serviceAllowed = appOpsAllowed; - } catch (Exception e) { - // Allow service in case if app ops is not available in testing. - appOpsAllowed = true; - } } else { - appOpsAllowed = false; - } - if (serviceAllowed || serviceEnabled) { - preference.setEnabled(true); - } else { - // Disable accessibility service that are not permitted. - final RestrictedLockUtils.EnforcedAdmin admin = - RestrictedLockUtilsInternal.checkIfAccessibilityServiceDisallowed( - mContext, preference.getPackageName(), UserHandle.myUserId()); - - if (admin != null) { - preference.setDisabledByAdmin(admin); - } else if (!appOpsAllowed) { - preference.setDisabledByAppOps(true); + boolean appOpsAllowed; + if (serviceAllowed) { + try { + final int mode = mAppOps.noteOpNoThrow( + AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, + preference.getUid(), preference.getPackageName()); + final boolean ecmEnabled = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_enhancedConfirmationModeEnabled); + appOpsAllowed = !ecmEnabled || mode == AppOpsManager.MODE_ALLOWED; + serviceAllowed = appOpsAllowed; + } catch (Exception e) { + // Allow service in case if app ops is not available in testing. + appOpsAllowed = true; + } } else { - preference.setEnabled(false); + appOpsAllowed = false; + } + if (serviceAllowed || serviceEnabled) { + preference.setEnabled(true); + } else { + // Disable accessibility service that are not permitted. + final RestrictedLockUtils.EnforcedAdmin admin = + RestrictedLockUtilsInternal.checkIfAccessibilityServiceDisallowed( + mContext, preference.getPackageName(), UserHandle.myUserId()); + + if (admin != null) { + preference.setDisabledByAdmin(admin); + } else if (!appOpsAllowed) { + preference.setDisabledByAppOps(true); + } else { + preference.setEnabled(false); + } } } } diff --git a/src/com/android/settings/applications/UsageAccessDetails.java b/src/com/android/settings/applications/UsageAccessDetails.java index 6976149f4d7..e111942f751 100644 --- a/src/com/android/settings/applications/UsageAccessDetails.java +++ b/src/com/android/settings/applications/UsageAccessDetails.java @@ -174,7 +174,7 @@ public class UsageAccessDetails extends AppInfoWithHeader implements OnPreferenc if (shouldEnable && !hasAccess) { mSwitchPref.checkEcmRestrictionAndSetDisabled(AppOpsManager.OPSTR_GET_USAGE_STATS, - mPackageName, mPackageInfo.applicationInfo.uid); + mPackageName); shouldEnable = !mSwitchPref.isDisabledByEcm(); } diff --git a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java index 82d55f3d9f8..90d733e6fe1 100644 --- a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java +++ b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java @@ -24,6 +24,7 @@ import android.app.Activity; import android.app.AppOpsManager; import android.app.KeyguardManager; import android.app.admin.DevicePolicyManager; +import android.app.ecm.EnhancedConfirmationManager; import android.app.settings.SettingsEnums; import android.content.BroadcastReceiver; import android.content.Context; @@ -490,12 +491,23 @@ public class AppInfoDashboardFragment extends DashboardFragment return true; case ACCESS_RESTRICTED_SETTINGS: showLockScreen(getContext(), () -> { - final AppOpsManager appOpsManager = getContext().getSystemService( - AppOpsManager.class); - appOpsManager.setMode(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, - getUid(), - getPackageName(), - AppOpsManager.MODE_ALLOWED); + if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled() + && android.security.Flags.extendEcmToAllSettings()) { + EnhancedConfirmationManager manager = getContext().getSystemService( + EnhancedConfirmationManager.class); + try { + manager.clearRestriction(getPackageName()); + } catch (NameNotFoundException e) { + Log.e(TAG, "Exception when retrieving package:" + getPackageName(), e); + } + } else { + final AppOpsManager appOpsManager = getContext().getSystemService( + AppOpsManager.class); + appOpsManager.setMode(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, + getUid(), + getPackageName(), + AppOpsManager.MODE_ALLOWED); + } getActivity().invalidateOptionsMenu(); final String toastString = getContext().getString( R.string.toast_allows_restricted_settings_successfully, @@ -527,14 +539,25 @@ public class AppInfoDashboardFragment extends DashboardFragment } private boolean shouldShowAccessRestrictedSettings() { - try { - final int mode = getSystemService(AppOpsManager.class).noteOpNoThrow( - AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, getUid(), - getPackageName()); - return mode == AppOpsManager.MODE_IGNORED; - } catch (Exception e) { - // Fallback in case if app ops is not available in testing. - return false; + if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled() + && android.security.Flags.extendEcmToAllSettings()) { + try { + return getSystemService(EnhancedConfirmationManager.class) + .isClearRestrictionAllowed(getPackageName()); + } catch (NameNotFoundException e) { + Log.e(TAG, "Exception when retrieving package:" + getPackageName(), e); + return false; + } + } else { + try { + final int mode = getSystemService(AppOpsManager.class).noteOpNoThrow( + AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, getUid(), + getPackageName()); + return mode == AppOpsManager.MODE_IGNORED; + } catch (Exception e) { + // Fallback in case if app ops is not available in testing. + return false; + } } } diff --git a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceController.java b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceController.java index 949577b8c14..640f21dec0e 100644 --- a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceController.java +++ b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceController.java @@ -222,7 +222,7 @@ public class DeviceAdminListPreferenceController extends BasePreferenceControlle pref.setOnPreferenceChangeListener((preference, newValue) -> false); pref.setSingleLineTitle(true); pref.checkEcmRestrictionAndSetDisabled(Manifest.permission.BIND_DEVICE_ADMIN, - item.getPackageName(), item.getUid()); + item.getPackageName()); } /** diff --git a/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java b/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java index 00f06258ee0..70a31b8e8f0 100644 --- a/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java +++ b/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java @@ -24,6 +24,7 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.AsyncTask; +import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; @@ -42,7 +43,7 @@ public class ApprovalPreferenceController extends BasePreferenceController { private NotificationManager mNm; private PackageManager mPm; // The appOp representing this preference - private String mAppOpStr; + private String mSettingIdentifier; public ApprovalPreferenceController(Context context, String key) { super(context, key); @@ -76,8 +77,9 @@ public class ApprovalPreferenceController extends BasePreferenceController { /** * Set the associated appOp for the Setting */ - public ApprovalPreferenceController setAppOpStr(String appOpStr) { - mAppOpStr = appOpStr; + @NonNull + public ApprovalPreferenceController setSettingIdentifier(@NonNull String settingIdentifier) { + mSettingIdentifier = settingIdentifier; return this; } @@ -118,14 +120,15 @@ public class ApprovalPreferenceController extends BasePreferenceController { } }); - if (android.security.Flags.extendEcmToAllSettings()) { + if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled() + && android.security.Flags.extendEcmToAllSettings()) { if (!isAllowedCn && !isEnabled) { preference.setEnabled(false); } else if (isEnabled) { preference.setEnabled(true); } else { - preference.checkEcmRestrictionAndSetDisabled(mAppOpStr, - mCn.getPackageName(), mPkgInfo.applicationInfo.uid); + preference.checkEcmRestrictionAndSetDisabled(mSettingIdentifier, + mCn.getPackageName()); } } else { preference.updateState( diff --git a/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java b/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java index 89767ddb011..e885d5c3d17 100644 --- a/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java +++ b/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java @@ -103,7 +103,7 @@ public class NotificationAccessDetails extends DashboardFragment { .setCn(mComponentName) .setNm(context.getSystemService(NotificationManager.class)) .setPm(mPm) - .setAppOpStr(AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS) + .setSettingIdentifier(AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS) .setParent(this); use(HeaderPreferenceController.class) .setFragment(this) diff --git a/src/com/android/settings/applications/specialaccess/premiumsms/PremiumSmsAccess.java b/src/com/android/settings/applications/specialaccess/premiumsms/PremiumSmsAccess.java index 4c9f813fc5e..2a5f1dbcf04 100644 --- a/src/com/android/settings/applications/specialaccess/premiumsms/PremiumSmsAccess.java +++ b/src/com/android/settings/applications/specialaccess/premiumsms/PremiumSmsAccess.java @@ -25,7 +25,6 @@ import android.view.View; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; -import androidx.preference.DropDownPreference; import androidx.preference.Preference; import androidx.preference.Preference.OnPreferenceChangeListener; import androidx.preference.PreferenceScreen; @@ -38,6 +37,7 @@ import com.android.settings.applications.AppStateSmsPremBridge.SmsState; import com.android.settings.overlay.FeatureFactory; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.widget.EmptyTextSettings; +import com.android.settingslib.RestrictedDropDownPreference; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.ApplicationsState.AppEntry; import com.android.settingslib.applications.ApplicationsState.Callbacks; @@ -52,6 +52,8 @@ import java.util.ArrayList; public class PremiumSmsAccess extends EmptyTextSettings implements Callback, Callbacks, OnPreferenceChangeListener { + private static final String ECM_RESTRICTION_KEY = "android:premium_sms_access"; + private ApplicationsState mApplicationsState; private AppStateSmsPremBridge mSmsBackend; private Session mSession; @@ -205,7 +207,7 @@ public class PremiumSmsAccess extends EmptyTextSettings } - private class PremiumSmsPreference extends DropDownPreference { + private class PremiumSmsPreference extends RestrictedDropDownPreference { private final AppEntry mAppEntry; public PremiumSmsPreference(AppEntry appEntry, Context context) { @@ -224,6 +226,7 @@ public class PremiumSmsAccess extends EmptyTextSettings }); setValue(String.valueOf(getCurrentValue())); setSummary("%s"); + this.checkEcmRestrictionAndSetDisabled(ECM_RESTRICTION_KEY, appEntry.info.packageName); } private int getCurrentValue() { diff --git a/src/com/android/settings/datausage/UnrestrictedDataAccessPreference.java b/src/com/android/settings/datausage/UnrestrictedDataAccessPreference.java index 7a7eb8cc189..b3e66a9ce06 100644 --- a/src/com/android/settings/datausage/UnrestrictedDataAccessPreference.java +++ b/src/com/android/settings/datausage/UnrestrictedDataAccessPreference.java @@ -20,7 +20,9 @@ import android.graphics.drawable.Drawable; import android.os.UserHandle; import android.view.View; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import androidx.preference.PreferenceViewHolder; import com.android.settings.R; @@ -36,6 +38,7 @@ import com.android.settingslib.widget.AppSwitchPreference; public class UnrestrictedDataAccessPreference extends AppSwitchPreference implements DataSaverBackend.Listener { + private static final String ECM_SETTING_IDENTIFIER = "android:unrestricted_data_access"; private final ApplicationsState mApplicationsState; private final AppEntry mEntry; @@ -58,6 +61,7 @@ public class UnrestrictedDataAccessPreference extends AppSwitchPreference implem mParentFragment = parentFragment; setDisabledByAdmin(checkIfMeteredDataUsageUserControlDisabled( context, entry.info.packageName, UserHandle.getUserId(entry.info.uid))); + mHelper.checkEcmRestrictionAndSetDisabled(ECM_SETTING_IDENTIFIER, entry.info.packageName); updateState(); setKey(generateKey(mEntry)); @@ -166,10 +170,24 @@ public class UnrestrictedDataAccessPreference extends AppSwitchPreference implem return mHelper.isDisabledByAdmin(); } + @VisibleForTesting + boolean isDisabledByEcm() { + return mHelper.isDisabledByEcm(); + } + public void setDisabledByAdmin(EnforcedAdmin admin) { mHelper.setDisabledByAdmin(admin); } + /** + * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this + * package. Marks the preference as disabled if so. + * @param packageName the package to check the restriction for + */ + public void checkEcmRestrictionAndSetDisabled(@NonNull String packageName) { + mHelper.checkEcmRestrictionAndSetDisabled(ECM_SETTING_IDENTIFIER, packageName); + } + // Sets UI state based on allowlist/denylist status. public void updateState() { setTitle(mEntry.label); @@ -179,7 +197,8 @@ public class UnrestrictedDataAccessPreference extends AppSwitchPreference implem setSummary(com.android.settingslib.widget.restricted.R.string.disabled_by_admin); } else if (mDataUsageState.isDataSaverDenylisted) { setSummary(R.string.restrict_background_blocklisted); - } else { + // If disabled by ECM, the summary is set directly by the switch. + } else if (!isDisabledByEcm()) { setSummary(""); } } diff --git a/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceController.java b/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceController.java index fd2fcda71e1..0fb30a8e8ac 100644 --- a/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceController.java +++ b/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceController.java @@ -151,6 +151,7 @@ public class UnrestrictedDataAccessPreferenceController extends BasePreferenceCo } else { preference.setDisabledByAdmin(checkIfMeteredDataUsageUserControlDisabled(mContext, entry.info.packageName, UserHandle.getUserId(entry.info.uid))); + preference.checkEcmRestrictionAndSetDisabled(entry.info.packageName); preference.updateState(); } preference.setOrder(i); diff --git a/src/com/android/settings/notification/zen/ZenAccessSettings.java b/src/com/android/settings/notification/zen/ZenAccessSettings.java index 418a5719b8a..f765d6d2e4d 100644 --- a/src/com/android/settings/notification/zen/ZenAccessSettings.java +++ b/src/com/android/settings/notification/zen/ZenAccessSettings.java @@ -39,8 +39,8 @@ import com.android.settings.applications.specialaccess.zenaccess.ZenAccessDetail import com.android.settings.applications.specialaccess.zenaccess.ZenAccessSettingObserverMixin; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.widget.EmptyTextSettings; +import com.android.settings.widget.RestrictedAppPreference; import com.android.settingslib.search.SearchIndexable; -import com.android.settingslib.widget.AppPreference; import java.util.ArrayList; import java.util.Collections; @@ -122,7 +122,7 @@ public class ZenAccessSettings extends EmptyTextSettings implements for (ApplicationInfo app : apps) { final String pkg = app.packageName; final CharSequence label = app.loadLabel(mPkgMan); - final AppPreference pref = new AppPreference(getPrefContext()); + final RestrictedAppPreference pref = new RestrictedAppPreference(getPrefContext()); pref.setKey(pkg); pref.setIcon(app.loadIcon(mPkgMan)); pref.setTitle(label); @@ -133,6 +133,8 @@ public class ZenAccessSettings extends EmptyTextSettings implements } else { // Not auto approved, update summary according to notification backend. pref.setSummary(getPreferenceSummary(pkg)); + pref.checkEcmRestrictionAndSetDisabled( + android.Manifest.permission.MANAGE_NOTIFICATIONS, app.packageName); } pref.setOnPreferenceClickListener(preference -> { AppInfoBase.startAppInfoFragment( diff --git a/src/com/android/settings/spa/app/appinfo/AppInfoSettingsMoreOptions.kt b/src/com/android/settings/spa/app/appinfo/AppInfoSettingsMoreOptions.kt index 7f7d8c544b4..1ed595909e0 100644 --- a/src/com/android/settings/spa/app/appinfo/AppInfoSettingsMoreOptions.kt +++ b/src/com/android/settings/spa/app/appinfo/AppInfoSettingsMoreOptions.kt @@ -17,6 +17,7 @@ package com.android.settings.spa.app.appinfo import android.app.AppOpsManager +import android.app.ecm.EnhancedConfirmationManager import android.content.Context import android.content.pm.ApplicationInfo import android.os.UserManager @@ -90,12 +91,18 @@ fun AppInfoSettingsMoreOptions( private fun ApplicationInfo.allowRestrictedSettings(context: Context, onSuccess: () -> Unit) { AppInfoDashboardFragment.showLockScreen(context) { - context.appOpsManager.setMode( - AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, - uid, - packageName, - AppOpsManager.MODE_ALLOWED, - ) + if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled() + && android.security.Flags.extendEcmToAllSettings()) { + val manager = context.getSystemService(EnhancedConfirmationManager::class.java)!! + manager.clearRestriction(packageName) + } else { + context.appOpsManager.setMode( + AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, + uid, + packageName, + AppOpsManager.MODE_ALLOWED, + ) + } onSuccess() val toastString = context.getString( R.string.toast_allows_restricted_settings_successfully, @@ -137,7 +144,7 @@ private suspend fun ApplicationInfo.getMoreOptionsState( ) } val shouldShowAccessRestrictedSettingsDeferred = async { - shouldShowAccessRestrictedSettings(context.appOpsManager) + shouldShowAccessRestrictedSettings(context) } val isProfileOrDeviceOwner = Utils.isProfileOrDeviceOwner(context.userManager, context.devicePolicyManager, packageName) @@ -169,7 +176,14 @@ private fun ApplicationInfo.isOtherUserHasInstallPackage( .filter { it.id != userId } .any { packageManagers.isPackageInstalledAsUser(packageName, it.id) } -private fun ApplicationInfo.shouldShowAccessRestrictedSettings(appOpsManager: AppOpsManager) = - appOpsManager.noteOpNoThrow( - AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, uid, packageName, null, null - ) == AppOpsManager.MODE_IGNORED +private fun ApplicationInfo.shouldShowAccessRestrictedSettings(context: Context): Boolean { + return if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled() + && android.security.Flags.extendEcmToAllSettings()) { + val manager = context.getSystemService(EnhancedConfirmationManager::class.java)!! + manager.isClearRestrictionAllowed(packageName) + } else { + context.appOpsManager.noteOpNoThrow( + AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, uid, packageName, null, null + ) == AppOpsManager.MODE_IGNORED + } +} diff --git a/src/com/android/settings/utils/ManagedServiceSettings.java b/src/com/android/settings/utils/ManagedServiceSettings.java index d5f00408fd5..9f5fbc51f0d 100644 --- a/src/com/android/settings/utils/ManagedServiceSettings.java +++ b/src/com/android/settings/utils/ManagedServiceSettings.java @@ -37,14 +37,14 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; import androidx.preference.PreferenceScreen; -import androidx.preference.TwoStatePreference; import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.widget.EmptyTextSettings; +import com.android.settingslib.RestrictedSwitchPreference; import com.android.settingslib.applications.ServiceListing; -import com.android.settingslib.widget.AppSwitchPreference; +import com.android.settingslib.widget.TwoTargetPreference; import java.util.List; @@ -121,10 +121,12 @@ public abstract class ManagedServiceSettings extends EmptyTextSettings { } final CharSequence finalTitle = title; final String summary = service.loadLabel(mPm).toString(); - final TwoStatePreference pref = new AppSwitchPreference(getPrefContext()); + final RestrictedSwitchPreference pref = + new RestrictedSwitchPreference(getPrefContext()); pref.setPersistent(false); pref.setIcon(mIconDrawableFactory.getBadgedIcon(service, service.applicationInfo, UserHandle.getUserId(service.applicationInfo.uid))); + pref.setIconSize(TwoTargetPreference.ICON_SIZE_MEDIUM); if (title != null && !title.equals(summary)) { pref.setTitle(title); pref.setSummary(summary); @@ -150,6 +152,9 @@ public abstract class ManagedServiceSettings extends EmptyTextSettings { } }); pref.setKey(cn.flattenToString()); + if (!pref.isChecked()) { + pref.checkEcmRestrictionAndSetDisabled(mConfig.permission, service.packageName); + } screen.addPreference(pref); } highlightPreferenceIfNeeded(); diff --git a/src/com/android/settings/widget/RestrictedAppPreference.java b/src/com/android/settings/widget/RestrictedAppPreference.java index f93b935d5b0..c76a5de4535 100644 --- a/src/com/android/settings/widget/RestrictedAppPreference.java +++ b/src/com/android/settings/widget/RestrictedAppPreference.java @@ -21,6 +21,7 @@ import android.os.UserHandle; import android.text.TextUtils; import android.util.AttributeSet; +import androidx.annotation.NonNull; import androidx.preference.PreferenceManager; import androidx.preference.PreferenceViewHolder; @@ -72,10 +73,18 @@ public class RestrictedAppPreference extends AppPreference { @Override public void setEnabled(boolean enabled) { - if (isDisabledByAdmin() && enabled) { - return; + boolean changed = false; + if (enabled && isDisabledByAdmin()) { + mHelper.setDisabledByAdmin(null); + changed = true; + } + if (enabled && isDisabledByEcm()) { + mHelper.setDisabledByEcm(null); + changed = true; + } + if (!changed) { + super.setEnabled(enabled); } - super.setEnabled(enabled); } public void setDisabledByAdmin(RestrictedLockUtils.EnforcedAdmin admin) { @@ -88,6 +97,10 @@ public class RestrictedAppPreference extends AppPreference { return mHelper.isDisabledByAdmin(); } + public boolean isDisabledByEcm() { + return mHelper.isDisabledByEcm(); + } + public void useAdminDisabledSummary(boolean useSummary) { mHelper.useAdminDisabledSummary(useSummary); } @@ -112,4 +125,15 @@ public class RestrictedAppPreference extends AppPreference { public void checkRestrictionAndSetDisabled(String userRestriction, int userId) { mHelper.checkRestrictionAndSetDisabled(userRestriction, userId); } + + /** + * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this + * package. Marks the preference as disabled if so. + * @param settingIdentifier The key identifying the setting + * @param packageName the package to check the settingIdentifier for + */ + public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier, + @NonNull String packageName) { + mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName); + } } diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragmentTest.java index c105d088bb4..d88a83eba51 100644 --- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragmentTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragmentTest.java @@ -38,6 +38,7 @@ import androidx.test.core.app.ApplicationProvider; import com.android.settings.SettingsActivity; import com.android.settings.testutils.shadow.ShadowDevicePolicyManager; +import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal; import com.google.common.collect.ImmutableList; @@ -57,7 +58,10 @@ import java.util.ArrayList; import java.util.List; /** Tests for {@link AccessibilityDetailsSettingsFragment}. */ -@Config(shadows = ShadowDevicePolicyManager.class) +@Config(shadows = { + ShadowDevicePolicyManager.class, + ShadowRestrictedLockUtilsInternal.class +}) @RunWith(RobolectricTestRunner.class) public class AccessibilityDetailsSettingsFragmentTest { private static final String PACKAGE_NAME = "com.foo.bar"; diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java index db6f43bf2f9..05e56ca74a4 100644 --- a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java @@ -52,6 +52,7 @@ import com.android.settings.testutils.XmlTestUtils; import com.android.settings.testutils.shadow.ShadowApplicationPackageManager; import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; import com.android.settings.testutils.shadow.ShadowBluetoothUtils; +import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal; import com.android.settings.testutils.shadow.ShadowUserManager; import com.android.settingslib.RestrictedPreference; import com.android.settingslib.bluetooth.LocalBluetoothManager; @@ -87,6 +88,7 @@ import java.util.List; ShadowUserManager.class, ShadowColorDisplayManager.class, ShadowApplicationPackageManager.class, + ShadowRestrictedLockUtilsInternal.class, }) public class AccessibilitySettingsTest { private static final String PACKAGE_NAME = "com.android.test"; diff --git a/tests/robotests/src/com/android/settings/accessibility/RestrictedPreferenceHelperTest.java b/tests/robotests/src/com/android/settings/accessibility/RestrictedPreferenceHelperTest.java index 99a78cfc408..bc9c1d83fb1 100644 --- a/tests/robotests/src/com/android/settings/accessibility/RestrictedPreferenceHelperTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/RestrictedPreferenceHelperTest.java @@ -31,9 +31,14 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.platform.test.flag.junit.SetFlagsRule; import androidx.test.core.app.ApplicationProvider; +import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal; import com.android.settingslib.RestrictedPreference; import org.junit.Rule; @@ -45,6 +50,7 @@ import org.mockito.Spy; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; @@ -53,6 +59,9 @@ import java.util.List; /** Test for {@link RestrictedPreferenceHelper}. */ @RunWith(RobolectricTestRunner.class) +@Config(shadows = { + ShadowRestrictedLockUtilsInternal.class +}) public class RestrictedPreferenceHelperTest { private static final String PACKAGE_NAME = "com.android.test"; @@ -72,6 +81,11 @@ public class RestrictedPreferenceHelperTest { private AccessibilityShortcutInfo mShortcutInfo; private final RestrictedPreferenceHelper mHelper = new RestrictedPreferenceHelper(mContext); + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + @Test public void createAccessibilityServicePreferenceList_hasOneInfo_containsSameKey() { final String key = COMPONENT_NAME.flattenToString(); @@ -85,6 +99,37 @@ public class RestrictedPreferenceHelperTest { assertThat(preference.getKey()).isEqualTo(key); } + @Test + @RequiresFlagsEnabled(value = {android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS, + android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED}) + public void createAccessibilityServicePreferenceList_ecmRestricted_prefIsEcmRestricted() { + ShadowRestrictedLockUtilsInternal.setEcmRestrictedPkgs( + mServiceInfo.getResolveInfo().serviceInfo.packageName); + final List infoList = new ArrayList<>( + singletonList(mServiceInfo)); + + final List preferenceList = + mHelper.createAccessibilityServicePreferenceList(infoList); + final RestrictedPreference preference = preferenceList.get(0); + + assertThat(preference.isDisabledByEcm()).isTrue(); + } + + @Test + @RequiresFlagsEnabled(value = {android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS, + android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED}) + public void createAccessibilityServicePreferenceList_ecmNotRestricted_prefIsNotEcmRestricted() { + ShadowRestrictedLockUtilsInternal.setEcmRestrictedPkgs(); + final List infoList = new ArrayList<>( + singletonList(mServiceInfo)); + + final List preferenceList = + mHelper.createAccessibilityServicePreferenceList(infoList); + final RestrictedPreference preference = preferenceList.get(0); + + assertThat(preference.isDisabledByEcm()).isFalse(); + } + @Test public void createAccessibilityActivityPreferenceList_hasOneInfo_containsSameKey() { final String key = COMPONENT_NAME.flattenToString(); diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/premiumsms/PremiumSmsAccessTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/premiumsms/PremiumSmsAccessTest.java index e91c0fa571f..46b19102e2b 100644 --- a/tests/robotests/src/com/android/settings/applications/specialaccess/premiumsms/PremiumSmsAccessTest.java +++ b/tests/robotests/src/com/android/settings/applications/specialaccess/premiumsms/PremiumSmsAccessTest.java @@ -16,13 +16,35 @@ package com.android.settings.applications.specialaccess.premiumsms; +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.app.settings.SettingsEnums; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.os.Process; import android.telephony.SmsManager; +import android.view.LayoutInflater; +import android.view.View; + +import androidx.preference.PreferenceManager; +import androidx.preference.PreferenceScreen; +import androidx.preference.PreferenceViewHolder; +import androidx.preference.R; import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.datausage.AppStateDataUsageBridge; import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal; +import com.android.settingslib.RestrictedDropDownPreference; +import com.android.settingslib.applications.ApplicationsState; import org.junit.Before; import org.junit.Test; @@ -30,19 +52,28 @@ import org.junit.runner.RunWith; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.util.ReflectionHelpers; + +import java.util.ArrayList; @RunWith(RobolectricTestRunner.class) +@Config(shadows = { + ShadowRestrictedLockUtilsInternal.class +}) public class PremiumSmsAccessTest { private FakeFeatureFactory mFeatureFactory; private PremiumSmsAccess mFragment; + private Context mContext; @Before public void setUp() { MockitoAnnotations.initMocks(this); mFeatureFactory = FakeFeatureFactory.setupForTest(); mFragment = new PremiumSmsAccess(); - mFragment.onAttach(RuntimeEnvironment.application); + mContext = RuntimeEnvironment.application; + mFragment.onAttach(mContext); } @Test @@ -74,4 +105,89 @@ public class PremiumSmsAccessTest { "app", SmsManager.PREMIUM_SMS_CONSENT_ALWAYS_ALLOW); } + + @Test + public void onRebuildComplete_ecmRestricted_shouldBeDisabled() { + mFragment = spy(mFragment); + mContext = spy(mContext); + LayoutInflater inflater = LayoutInflater.from(mContext); + View view = inflater.inflate(R.layout.preference_dropdown, null); + PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(view); + + PreferenceManager preferenceManager = new PreferenceManager(mContext); + PreferenceScreen preferenceScreen = spy(preferenceManager.createPreferenceScreen(mContext)); + doReturn(preferenceManager).when(mFragment).getPreferenceManager(); + doReturn(preferenceScreen).when(mFragment).getPreferenceScreen(); + final String testPkg = "com.example.disabled"; + doNothing().when(mContext).startActivity(any()); + ShadowRestrictedLockUtilsInternal.setEcmRestrictedPkgs(testPkg); + + doAnswer((invocation) -> { + final RestrictedDropDownPreference preference = invocation.getArgument(0); + // Verify preference is disabled by ecm and the summary is changed accordingly. + assertThat(preference.isDisabledByEcm()).isTrue(); + assertThat(preference.getSummary().toString()).isEqualTo( + mContext.getString( + com.android.settingslib.R.string.disabled_by_app_ops_text)); + preference.onBindViewHolder(holder); + preference.performClick(); + // Verify that when the preference is clicked, ecm details intent is launched + verify(mContext).startActivity(any()); + + return null; + }).when(preferenceScreen).addPreference(any(RestrictedDropDownPreference.class)); + + mFragment.onRebuildComplete(createAppEntries(testPkg)); + verify(preferenceScreen).addPreference(any(RestrictedDropDownPreference.class)); + } + + @Test + public void onRebuildComplete_ecmNotRestricted_notDisabled() { + mFragment = spy(mFragment); + mContext = spy(mContext); + LayoutInflater inflater = LayoutInflater.from(mContext); + View view = inflater.inflate(R.layout.preference_dropdown, null); + PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(view); + + PreferenceManager preferenceManager = new PreferenceManager(mContext); + PreferenceScreen preferenceScreen = spy(preferenceManager.createPreferenceScreen(mContext)); + doReturn(preferenceManager).when(mFragment).getPreferenceManager(); + doReturn(preferenceScreen).when(mFragment).getPreferenceScreen(); + final String testPkg = "com.example.enabled"; + ShadowRestrictedLockUtilsInternal.setEcmRestrictedPkgs(); + + + doAnswer((invocation) -> { + final RestrictedDropDownPreference preference = invocation.getArgument(0); + assertThat(preference.isDisabledByEcm()).isFalse(); + assertThat(preference.getSummary().toString()).isEqualTo(""); + preference.onBindViewHolder(holder); + preference.performClick(); + // Verify that when the preference is clicked, ecm details intent is not launched + verify(mContext, never()).startActivity(any()); + + return null; + }).when(preferenceScreen).addPreference(any(RestrictedDropDownPreference.class)); + + mFragment.onRebuildComplete(createAppEntries(testPkg)); + verify(preferenceScreen).addPreference(any(RestrictedDropDownPreference.class)); + } + + private ArrayList createAppEntries(String... packageNames) { + final ArrayList appEntries = new ArrayList<>(); + for (int i = 0; i < packageNames.length; ++i) { + final ApplicationInfo info = new ApplicationInfo(); + info.packageName = packageNames[i]; + info.uid = Process.FIRST_APPLICATION_UID + i; + info.sourceDir = info.packageName; + final ApplicationsState.AppEntry appEntry = + spy(new ApplicationsState.AppEntry(mContext, info, i)); + appEntry.extraInfo = new AppStateDataUsageBridge + .DataUsageState(false, false); + doNothing().when(appEntry).ensureLabel(any(Context.class)); + ReflectionHelpers.setField(appEntry, "info", info); + appEntries.add(appEntry); + } + return appEntries; + } } diff --git a/tests/robotests/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceControllerTest.java index 8aa2e5aaabf..ce47052a271 100644 --- a/tests/robotests/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceControllerTest.java @@ -25,6 +25,7 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -163,6 +164,81 @@ public class UnrestrictedDataAccessPreferenceControllerTest { mController.onRebuildComplete(createAppEntries(testPkg1, testPkg2)); } + @Test + public void onRebuildComplete_ecmRestricted_shouldBeDisabled() { + mFragment = spy(new UnrestrictedDataAccess()); + mContext = spy(mContext); + doNothing().when(mFragment).setLoading(anyBoolean(), anyBoolean()); + mController.setParentFragment(mFragment); + mPreferenceManager = new PreferenceManager(mContext); + mPreferenceScreen = spy(mPreferenceManager.createPreferenceScreen(mContext)); + doReturn(mPreferenceManager).when(mFragment).getPreferenceManager(); + doReturn(mPreferenceScreen).when(mFragment).getPreferenceScreen(); + doReturn(0).when(mPreferenceScreen).getPreferenceCount(); + final DataSaverBackend dataSaverBackend = mock(DataSaverBackend.class); + ReflectionHelpers.setField(mController, "mDataSaverBackend", dataSaverBackend); + ReflectionHelpers.setField(mController, "mScreen", mPreferenceScreen); + + final String testPkg = "com.example.disabled"; + doNothing().when(mContext).startActivity(any()); + ShadowRestrictedLockUtilsInternal.setEcmRestrictedPkgs(testPkg); + + doAnswer((invocation) -> { + final UnrestrictedDataAccessPreference preference = invocation.getArgument(0); + // Verify preference is disabled by ecm and the summary is changed accordingly. + assertThat(preference.isDisabledByEcm()).isTrue(); + assertThat(preference.getSummary().toString()).isEqualTo( + mContext.getString( + com.android.settingslib.R.string.disabled_by_app_ops_text)); + assertThat(preference.isChecked()).isFalse(); + preference.performClick(); + // Verify that when the preference is clicked, ecm details intent is launched + assertThat(preference.isChecked()).isFalse(); + verify(mContext).startActivity(any()); + + return null; + }).when(mPreferenceScreen).addPreference(any(UnrestrictedDataAccessPreference.class)); + + mController.onRebuildComplete(createAppEntries(testPkg)); + verify(mPreferenceScreen).addPreference(any(UnrestrictedDataAccessPreference.class)); + } + + @Test + public void onRebuildComplete_ecmNotRestricted_notDisabled() { + mFragment = spy(new UnrestrictedDataAccess()); + mContext = spy(mContext); + doNothing().when(mFragment).setLoading(anyBoolean(), anyBoolean()); + mController.setParentFragment(mFragment); + mPreferenceManager = new PreferenceManager(mContext); + mPreferenceScreen = spy(mPreferenceManager.createPreferenceScreen(mContext)); + doReturn(mPreferenceManager).when(mFragment).getPreferenceManager(); + doReturn(mPreferenceScreen).when(mFragment).getPreferenceScreen(); + doReturn(0).when(mPreferenceScreen).getPreferenceCount(); + final DataSaverBackend dataSaverBackend = mock(DataSaverBackend.class); + ReflectionHelpers.setField(mController, "mDataSaverBackend", dataSaverBackend); + ReflectionHelpers.setField(mController, "mScreen", mPreferenceScreen); + + final String testPkg = "com.example.enabled"; + doNothing().when(mContext).startActivity(any()); + ShadowRestrictedLockUtilsInternal.setEcmRestrictedPkgs(); + + doAnswer((invocation) -> { + final UnrestrictedDataAccessPreference preference = invocation.getArgument(0); + assertThat(preference.isDisabledByEcm()).isFalse(); + assertThat(preference.getSummary()).isEqualTo(""); + assertThat(preference.isChecked()).isFalse(); + preference.performClick(); + // Verify that when the preference is clicked, ecm details intent is not launched + assertThat(preference.isChecked()).isTrue(); + verify(mContext, never()).startActivity(any()); + + return null; + }).when(mPreferenceScreen).addPreference(any(UnrestrictedDataAccessPreference.class)); + + mController.onRebuildComplete(createAppEntries(testPkg)); + verify(mPreferenceScreen).addPreference(any(UnrestrictedDataAccessPreference.class)); + } + private ArrayList createAppEntries(String... packageNames) { final ArrayList appEntries = new ArrayList<>(); for (int i = 0; i < packageNames.length; ++i) { diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRestrictedLockUtilsInternal.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRestrictedLockUtilsInternal.java index 5989d49c7fd..e03134b0fee 100644 --- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRestrictedLockUtilsInternal.java +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRestrictedLockUtilsInternal.java @@ -15,9 +15,11 @@ */ package com.android.settings.testutils.shadow; +import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.admin.DevicePolicyManager; import android.content.Context; +import android.content.Intent; import com.android.internal.util.ArrayUtils; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; @@ -38,6 +40,8 @@ public class ShadowRestrictedLockUtilsInternal { private static DevicePolicyManager sDevicePolicyManager; private static String[] sDisabledTypes; private static int sKeyguardDisabledFeatures; + private static String[] sEcmRestrictedPkgs; + private static boolean sAccessibilityServiceRestricted; @Resetter public static void reset() { @@ -47,6 +51,8 @@ public class ShadowRestrictedLockUtilsInternal { sDisabledTypes = new String[0]; sMaximumTimeToLockIsSet = false; sMteOverridden = false; + sEcmRestrictedPkgs = new String[0]; + sAccessibilityServiceRestricted = false; } @Implementation @@ -108,10 +114,35 @@ public class ShadowRestrictedLockUtilsInternal { return sMteOverridden ? new EnforcedAdmin() : null; } + public static EnforcedAdmin checkIfAccessibilityServiceDisallowed(Context context, + String packageName, int userId) { + return sAccessibilityServiceRestricted ? new EnforcedAdmin() : null; + } + + @Implementation + public static Intent checkIfRequiresEnhancedConfirmation(@NonNull Context context, + @NonNull String settingIdentifier, @NonNull String packageName) { + if (ArrayUtils.contains(sEcmRestrictedPkgs, packageName)) { + return new Intent(); + } + + return null; + } + + @Implementation + public static boolean isEnhancedConfirmationRestricted(@NonNull Context context, + @NonNull String settingIdentifier, @NonNull String packageName) { + return false; + } + public static void setRestricted(boolean restricted) { sIsRestricted = restricted; } + public static void setEcmRestrictedPkgs(String... pkgs) { + sEcmRestrictedPkgs = pkgs; + } + public static void setRestrictedPkgs(String... pkgs) { sRestrictedPkgs = pkgs; } diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppInfoSettingsMoreOptionsTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppInfoSettingsMoreOptionsTest.kt index 71035163057..9e7ec9bcd42 100644 --- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppInfoSettingsMoreOptionsTest.kt +++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppInfoSettingsMoreOptionsTest.kt @@ -19,11 +19,16 @@ package com.android.settings.spa.app.appinfo import android.app.AppOpsManager import android.app.KeyguardManager import android.app.admin.DevicePolicyManager +import android.app.ecm.EnhancedConfirmationManager import android.content.Context import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.content.pm.UserInfo import android.os.UserManager +import android.platform.test.annotations.RequiresFlagsDisabled +import android.platform.test.annotations.RequiresFlagsEnabled +import android.platform.test.flag.junit.CheckFlagsRule +import android.platform.test.flag.junit.DeviceFlagsValueProvider import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.test.assertIsNotDisplayed @@ -61,6 +66,9 @@ class AppInfoSettingsMoreOptionsTest { @get:Rule val composeTestRule = createComposeRule() + @get:Rule + val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + private lateinit var mockSession: MockitoSession @Spy @@ -81,6 +89,9 @@ class AppInfoSettingsMoreOptionsTest { @Mock private lateinit var appOpsManager: AppOpsManager + @Mock + private lateinit var enhancedConfirmationManager: EnhancedConfirmationManager + @Mock private lateinit var keyguardManager: KeyguardManager @@ -103,6 +114,8 @@ class AppInfoSettingsMoreOptionsTest { whenever(context.devicePolicyManager).thenReturn(devicePolicyManager) whenever(context.appOpsManager).thenReturn(appOpsManager) whenever(context.getSystemService(KeyguardManager::class.java)).thenReturn(keyguardManager) + whenever(context.getSystemService(EnhancedConfirmationManager::class.java)) + .thenReturn(enhancedConfirmationManager) whenever(keyguardManager.isKeyguardSecure).thenReturn(false) whenever(Utils.isProfileOrDeviceOwner(userManager, devicePolicyManager, PACKAGE_NAME)) .thenReturn(false) @@ -158,7 +171,9 @@ class AppInfoSettingsMoreOptionsTest { } @Test - fun shouldShowAccessRestrictedSettings() { + @RequiresFlagsDisabled(android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS, + android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED) + fun shouldShowAccessRestrictedSettings_appOp() { whenever( appOpsManager.noteOpNoThrow( AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, UID, PACKAGE_NAME, null, null @@ -186,6 +201,31 @@ class AppInfoSettingsMoreOptionsTest { ) } + @Test + @RequiresFlagsEnabled(android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS, + android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED) + fun shouldShowAccessRestrictedSettings() { + whenever( + enhancedConfirmationManager.isClearRestrictionAllowed(PACKAGE_NAME) + ).thenReturn(true) + val app = ApplicationInfo().apply { + packageName = PACKAGE_NAME + uid = UID + } + + setContent(app) + composeTestRule.onRoot().performClick() + + composeTestRule.waitUntilExists( + hasText(context.getString(R.string.app_restricted_settings_lockscreen_title)) + ) + composeTestRule + .onNodeWithText(context + .getString(R.string.app_restricted_settings_lockscreen_title)) + .performClick() + verify(enhancedConfirmationManager).clearRestriction(PACKAGE_NAME) + } + private fun setContent(app: ApplicationInfo) { composeTestRule.setContent { CompositionLocalProvider(LocalContext provides context) { diff --git a/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java b/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java index fa5af6d4f9e..eb236854450 100644 --- a/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java +++ b/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java @@ -36,6 +36,9 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.platform.test.annotations.EnableFlags; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.platform.test.flag.junit.SetFlagsRule; import androidx.test.core.app.ApplicationProvider; @@ -45,6 +48,7 @@ import com.android.internal.logging.nano.MetricsProto; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settingslib.RestrictedSwitchPreference; +import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -58,6 +62,8 @@ public class ApprovalPreferenceControllerTest { @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule( SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT); + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); private Context mContext; private FakeFeatureFactory mFeatureFactory; @@ -122,6 +128,7 @@ public class ApprovalPreferenceControllerTest { } @Test + @RequiresFlagsDisabled(android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS) public void updateState_checked() { when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn( AppOpsManager.MODE_ALLOWED); @@ -136,19 +143,26 @@ public class ApprovalPreferenceControllerTest { } @Test + @RequiresFlagsDisabled(android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS) public void restrictedSettings_appOpsDisabled() { - when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn( - AppOpsManager.MODE_ERRORED); + Assert.assertFalse(android.security.Flags.extendEcmToAllSettings()); + when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())) + .thenReturn(AppOpsManager.MODE_ERRORED); + doReturn(mAppOpsManager).when(mContext).getSystemService(Context.APP_OPS_SERVICE); when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(false); RestrictedSwitchPreference pref = new RestrictedSwitchPreference( mContext); pref.setAppOps(mAppOpsManager); + mController.setSettingIdentifier(AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS); mController.updateState(pref); + + verify(mAppOpsManager).noteOpNoThrow(anyInt(), anyInt(), anyString()); assertThat(pref.isEnabled()).isFalse(); } @Test + @RequiresFlagsDisabled(android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS) public void restrictedSettings_serviceAlreadyEnabled() { when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn( AppOpsManager.MODE_ERRORED);