Merge changes from topics "ecm-manager", "ecm-tests" into main

* changes:
  Replace ECM AppOps call with service
  Wired remaining SpecialAppAccess for ECM.
This commit is contained in:
Hani Kazmi
2024-02-21 19:29:04 +00:00
committed by Android (Google) Code Review
23 changed files with 510 additions and 95 deletions

View File

@@ -21,7 +21,6 @@ import android.app.AppOpsManager;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log;
public class ActionDisabledByAppOpsDialog extends Activity public class ActionDisabledByAppOpsDialog extends Activity
implements DialogInterface.OnDismissListener { implements DialogInterface.OnDismissListener {

View File

@@ -43,6 +43,7 @@ import com.android.settings.R;
import com.android.settings.core.InstrumentedFragment; import com.android.settings.core.InstrumentedFragment;
import com.android.settings.core.SubSettingLauncher; import com.android.settings.core.SubSettingLauncher;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.accessibility.AccessibilityUtils; import com.android.settingslib.accessibility.AccessibilityUtils;
import java.util.List; import java.util.List;
@@ -164,16 +165,9 @@ public class AccessibilityDetailsSettingsFragment extends InstrumentedFragment {
if (permittedServices != null && !permittedServices.contains(packageName)) { if (permittedServices != null && !permittedServices.contains(packageName)) {
return false; return false;
} }
try {
final int mode = mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, return !RestrictedLockUtilsInternal.isEnhancedConfirmationRestricted(getContext(),
uid, packageName); packageName, AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE);
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;
}
} }
private AccessibilityServiceInfo getAccessibilityServiceInfo(ComponentName componentName) { private AccessibilityServiceInfo getAccessibilityServiceInfo(ComponentName componentName) {

View File

@@ -235,10 +235,11 @@ public class RestrictedPreferenceHelper {
boolean serviceAllowed = permittedServices == null || permittedServices.contains( boolean serviceAllowed = permittedServices == null || permittedServices.contains(
preference.getPackageName()); preference.getPackageName());
if (android.security.Flags.extendEcmToAllSettings()) { if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
&& android.security.Flags.extendEcmToAllSettings()) {
preference.checkEcmRestrictionAndSetDisabled( preference.checkEcmRestrictionAndSetDisabled(
AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE, AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE,
preference.getPackageName(), preference.getUid()); preference.getPackageName());
if (preference.isDisabledByEcm()) { if (preference.isDisabledByEcm()) {
serviceAllowed = false; serviceAllowed = false;
} }
@@ -257,9 +258,7 @@ public class RestrictedPreferenceHelper {
preference.setEnabled(false); preference.setEnabled(false);
} }
} }
return; } else {
}
boolean appOpsAllowed; boolean appOpsAllowed;
if (serviceAllowed) { if (serviceAllowed) {
try { try {
@@ -294,6 +293,7 @@ public class RestrictedPreferenceHelper {
} }
} }
} }
}
/** Puts the basic extras into {@link RestrictedPreference}'s getExtras(). */ /** Puts the basic extras into {@link RestrictedPreference}'s getExtras(). */
private void putBasicExtras(RestrictedPreference preference, String prefKey, private void putBasicExtras(RestrictedPreference preference, String prefKey,

View File

@@ -174,7 +174,7 @@ public class UsageAccessDetails extends AppInfoWithHeader implements OnPreferenc
if (shouldEnable && !hasAccess) { if (shouldEnable && !hasAccess) {
mSwitchPref.checkEcmRestrictionAndSetDisabled(AppOpsManager.OPSTR_GET_USAGE_STATS, mSwitchPref.checkEcmRestrictionAndSetDisabled(AppOpsManager.OPSTR_GET_USAGE_STATS,
mPackageName, mPackageInfo.applicationInfo.uid); mPackageName);
shouldEnable = !mSwitchPref.isDisabledByEcm(); shouldEnable = !mSwitchPref.isDisabledByEcm();
} }

View File

@@ -24,6 +24,7 @@ import android.app.Activity;
import android.app.AppOpsManager; import android.app.AppOpsManager;
import android.app.KeyguardManager; import android.app.KeyguardManager;
import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManager;
import android.app.ecm.EnhancedConfirmationManager;
import android.app.settings.SettingsEnums; import android.app.settings.SettingsEnums;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
@@ -490,12 +491,23 @@ public class AppInfoDashboardFragment extends DashboardFragment
return true; return true;
case ACCESS_RESTRICTED_SETTINGS: case ACCESS_RESTRICTED_SETTINGS:
showLockScreen(getContext(), () -> { showLockScreen(getContext(), () -> {
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( final AppOpsManager appOpsManager = getContext().getSystemService(
AppOpsManager.class); AppOpsManager.class);
appOpsManager.setMode(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, appOpsManager.setMode(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
getUid(), getUid(),
getPackageName(), getPackageName(),
AppOpsManager.MODE_ALLOWED); AppOpsManager.MODE_ALLOWED);
}
getActivity().invalidateOptionsMenu(); getActivity().invalidateOptionsMenu();
final String toastString = getContext().getString( final String toastString = getContext().getString(
R.string.toast_allows_restricted_settings_successfully, R.string.toast_allows_restricted_settings_successfully,
@@ -527,6 +539,16 @@ public class AppInfoDashboardFragment extends DashboardFragment
} }
private boolean shouldShowAccessRestrictedSettings() { private boolean shouldShowAccessRestrictedSettings() {
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 { try {
final int mode = getSystemService(AppOpsManager.class).noteOpNoThrow( final int mode = getSystemService(AppOpsManager.class).noteOpNoThrow(
AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, getUid(), AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, getUid(),
@@ -537,6 +559,7 @@ public class AppInfoDashboardFragment extends DashboardFragment
return false; return false;
} }
} }
}
@VisibleForTesting @VisibleForTesting
boolean shouldShowUninstallForAll(AppEntry appEntry) { boolean shouldShowUninstallForAll(AppEntry appEntry) {

View File

@@ -222,7 +222,7 @@ public class DeviceAdminListPreferenceController extends BasePreferenceControlle
pref.setOnPreferenceChangeListener((preference, newValue) -> false); pref.setOnPreferenceChangeListener((preference, newValue) -> false);
pref.setSingleLineTitle(true); pref.setSingleLineTitle(true);
pref.checkEcmRestrictionAndSetDisabled(Manifest.permission.BIND_DEVICE_ADMIN, pref.checkEcmRestrictionAndSetDisabled(Manifest.permission.BIND_DEVICE_ADMIN,
item.getPackageName(), item.getUid()); item.getPackageName());
} }
/** /**

View File

@@ -24,6 +24,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.AsyncTask; import android.os.AsyncTask;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceFragmentCompat;
@@ -42,7 +43,7 @@ public class ApprovalPreferenceController extends BasePreferenceController {
private NotificationManager mNm; private NotificationManager mNm;
private PackageManager mPm; private PackageManager mPm;
// The appOp representing this preference // The appOp representing this preference
private String mAppOpStr; private String mSettingIdentifier;
public ApprovalPreferenceController(Context context, String key) { public ApprovalPreferenceController(Context context, String key) {
super(context, key); super(context, key);
@@ -76,8 +77,9 @@ public class ApprovalPreferenceController extends BasePreferenceController {
/** /**
* Set the associated appOp for the Setting * Set the associated appOp for the Setting
*/ */
public ApprovalPreferenceController setAppOpStr(String appOpStr) { @NonNull
mAppOpStr = appOpStr; public ApprovalPreferenceController setSettingIdentifier(@NonNull String settingIdentifier) {
mSettingIdentifier = settingIdentifier;
return this; 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) { if (!isAllowedCn && !isEnabled) {
preference.setEnabled(false); preference.setEnabled(false);
} else if (isEnabled) { } else if (isEnabled) {
preference.setEnabled(true); preference.setEnabled(true);
} else { } else {
preference.checkEcmRestrictionAndSetDisabled(mAppOpStr, preference.checkEcmRestrictionAndSetDisabled(mSettingIdentifier,
mCn.getPackageName(), mPkgInfo.applicationInfo.uid); mCn.getPackageName());
} }
} else { } else {
preference.updateState( preference.updateState(

View File

@@ -103,7 +103,7 @@ public class NotificationAccessDetails extends DashboardFragment {
.setCn(mComponentName) .setCn(mComponentName)
.setNm(context.getSystemService(NotificationManager.class)) .setNm(context.getSystemService(NotificationManager.class))
.setPm(mPm) .setPm(mPm)
.setAppOpStr(AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS) .setSettingIdentifier(AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS)
.setParent(this); .setParent(this);
use(HeaderPreferenceController.class) use(HeaderPreferenceController.class)
.setFragment(this) .setFragment(this)

View File

@@ -25,7 +25,6 @@ import android.view.View;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.preference.DropDownPreference;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.Preference.OnPreferenceChangeListener; import androidx.preference.Preference.OnPreferenceChangeListener;
import androidx.preference.PreferenceScreen; 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.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.widget.EmptyTextSettings; import com.android.settings.widget.EmptyTextSettings;
import com.android.settingslib.RestrictedDropDownPreference;
import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry; import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.applications.ApplicationsState.Callbacks; import com.android.settingslib.applications.ApplicationsState.Callbacks;
@@ -52,6 +52,8 @@ import java.util.ArrayList;
public class PremiumSmsAccess extends EmptyTextSettings public class PremiumSmsAccess extends EmptyTextSettings
implements Callback, Callbacks, OnPreferenceChangeListener { implements Callback, Callbacks, OnPreferenceChangeListener {
private static final String ECM_RESTRICTION_KEY = "android:premium_sms_access";
private ApplicationsState mApplicationsState; private ApplicationsState mApplicationsState;
private AppStateSmsPremBridge mSmsBackend; private AppStateSmsPremBridge mSmsBackend;
private Session mSession; 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; private final AppEntry mAppEntry;
public PremiumSmsPreference(AppEntry appEntry, Context context) { public PremiumSmsPreference(AppEntry appEntry, Context context) {
@@ -224,6 +226,7 @@ public class PremiumSmsAccess extends EmptyTextSettings
}); });
setValue(String.valueOf(getCurrentValue())); setValue(String.valueOf(getCurrentValue()));
setSummary("%s"); setSummary("%s");
this.checkEcmRestrictionAndSetDisabled(ECM_RESTRICTION_KEY, appEntry.info.packageName);
} }
private int getCurrentValue() { private int getCurrentValue() {

View File

@@ -20,7 +20,9 @@ import android.graphics.drawable.Drawable;
import android.os.UserHandle; import android.os.UserHandle;
import android.view.View; import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceViewHolder; import androidx.preference.PreferenceViewHolder;
import com.android.settings.R; import com.android.settings.R;
@@ -36,6 +38,7 @@ import com.android.settingslib.widget.AppSwitchPreference;
public class UnrestrictedDataAccessPreference extends AppSwitchPreference implements public class UnrestrictedDataAccessPreference extends AppSwitchPreference implements
DataSaverBackend.Listener { DataSaverBackend.Listener {
private static final String ECM_SETTING_IDENTIFIER = "android:unrestricted_data_access";
private final ApplicationsState mApplicationsState; private final ApplicationsState mApplicationsState;
private final AppEntry mEntry; private final AppEntry mEntry;
@@ -58,6 +61,7 @@ public class UnrestrictedDataAccessPreference extends AppSwitchPreference implem
mParentFragment = parentFragment; mParentFragment = parentFragment;
setDisabledByAdmin(checkIfMeteredDataUsageUserControlDisabled( setDisabledByAdmin(checkIfMeteredDataUsageUserControlDisabled(
context, entry.info.packageName, UserHandle.getUserId(entry.info.uid))); context, entry.info.packageName, UserHandle.getUserId(entry.info.uid)));
mHelper.checkEcmRestrictionAndSetDisabled(ECM_SETTING_IDENTIFIER, entry.info.packageName);
updateState(); updateState();
setKey(generateKey(mEntry)); setKey(generateKey(mEntry));
@@ -166,10 +170,24 @@ public class UnrestrictedDataAccessPreference extends AppSwitchPreference implem
return mHelper.isDisabledByAdmin(); return mHelper.isDisabledByAdmin();
} }
@VisibleForTesting
boolean isDisabledByEcm() {
return mHelper.isDisabledByEcm();
}
public void setDisabledByAdmin(EnforcedAdmin admin) { public void setDisabledByAdmin(EnforcedAdmin admin) {
mHelper.setDisabledByAdmin(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. // Sets UI state based on allowlist/denylist status.
public void updateState() { public void updateState() {
setTitle(mEntry.label); setTitle(mEntry.label);
@@ -179,7 +197,8 @@ public class UnrestrictedDataAccessPreference extends AppSwitchPreference implem
setSummary(com.android.settingslib.widget.restricted.R.string.disabled_by_admin); setSummary(com.android.settingslib.widget.restricted.R.string.disabled_by_admin);
} else if (mDataUsageState.isDataSaverDenylisted) { } else if (mDataUsageState.isDataSaverDenylisted) {
setSummary(R.string.restrict_background_blocklisted); setSummary(R.string.restrict_background_blocklisted);
} else { // If disabled by ECM, the summary is set directly by the switch.
} else if (!isDisabledByEcm()) {
setSummary(""); setSummary("");
} }
} }

View File

@@ -151,6 +151,7 @@ public class UnrestrictedDataAccessPreferenceController extends BasePreferenceCo
} else { } else {
preference.setDisabledByAdmin(checkIfMeteredDataUsageUserControlDisabled(mContext, preference.setDisabledByAdmin(checkIfMeteredDataUsageUserControlDisabled(mContext,
entry.info.packageName, UserHandle.getUserId(entry.info.uid))); entry.info.packageName, UserHandle.getUserId(entry.info.uid)));
preference.checkEcmRestrictionAndSetDisabled(entry.info.packageName);
preference.updateState(); preference.updateState();
} }
preference.setOrder(i); preference.setOrder(i);

View File

@@ -39,8 +39,8 @@ import com.android.settings.applications.specialaccess.zenaccess.ZenAccessDetail
import com.android.settings.applications.specialaccess.zenaccess.ZenAccessSettingObserverMixin; import com.android.settings.applications.specialaccess.zenaccess.ZenAccessSettingObserverMixin;
import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.widget.EmptyTextSettings; import com.android.settings.widget.EmptyTextSettings;
import com.android.settings.widget.RestrictedAppPreference;
import com.android.settingslib.search.SearchIndexable; import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.widget.AppPreference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@@ -122,7 +122,7 @@ public class ZenAccessSettings extends EmptyTextSettings implements
for (ApplicationInfo app : apps) { for (ApplicationInfo app : apps) {
final String pkg = app.packageName; final String pkg = app.packageName;
final CharSequence label = app.loadLabel(mPkgMan); final CharSequence label = app.loadLabel(mPkgMan);
final AppPreference pref = new AppPreference(getPrefContext()); final RestrictedAppPreference pref = new RestrictedAppPreference(getPrefContext());
pref.setKey(pkg); pref.setKey(pkg);
pref.setIcon(app.loadIcon(mPkgMan)); pref.setIcon(app.loadIcon(mPkgMan));
pref.setTitle(label); pref.setTitle(label);
@@ -133,6 +133,8 @@ public class ZenAccessSettings extends EmptyTextSettings implements
} else { } else {
// Not auto approved, update summary according to notification backend. // Not auto approved, update summary according to notification backend.
pref.setSummary(getPreferenceSummary(pkg)); pref.setSummary(getPreferenceSummary(pkg));
pref.checkEcmRestrictionAndSetDisabled(
android.Manifest.permission.MANAGE_NOTIFICATIONS, app.packageName);
} }
pref.setOnPreferenceClickListener(preference -> { pref.setOnPreferenceClickListener(preference -> {
AppInfoBase.startAppInfoFragment( AppInfoBase.startAppInfoFragment(

View File

@@ -17,6 +17,7 @@
package com.android.settings.spa.app.appinfo package com.android.settings.spa.app.appinfo
import android.app.AppOpsManager import android.app.AppOpsManager
import android.app.ecm.EnhancedConfirmationManager
import android.content.Context import android.content.Context
import android.content.pm.ApplicationInfo import android.content.pm.ApplicationInfo
import android.os.UserManager import android.os.UserManager
@@ -90,12 +91,18 @@ fun AppInfoSettingsMoreOptions(
private fun ApplicationInfo.allowRestrictedSettings(context: Context, onSuccess: () -> Unit) { private fun ApplicationInfo.allowRestrictedSettings(context: Context, onSuccess: () -> Unit) {
AppInfoDashboardFragment.showLockScreen(context) { AppInfoDashboardFragment.showLockScreen(context) {
if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
&& android.security.Flags.extendEcmToAllSettings()) {
val manager = context.getSystemService(EnhancedConfirmationManager::class.java)!!
manager.clearRestriction(packageName)
} else {
context.appOpsManager.setMode( context.appOpsManager.setMode(
AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
uid, uid,
packageName, packageName,
AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED,
) )
}
onSuccess() onSuccess()
val toastString = context.getString( val toastString = context.getString(
R.string.toast_allows_restricted_settings_successfully, R.string.toast_allows_restricted_settings_successfully,
@@ -137,7 +144,7 @@ private suspend fun ApplicationInfo.getMoreOptionsState(
) )
} }
val shouldShowAccessRestrictedSettingsDeferred = async { val shouldShowAccessRestrictedSettingsDeferred = async {
shouldShowAccessRestrictedSettings(context.appOpsManager) shouldShowAccessRestrictedSettings(context)
} }
val isProfileOrDeviceOwner = val isProfileOrDeviceOwner =
Utils.isProfileOrDeviceOwner(context.userManager, context.devicePolicyManager, packageName) Utils.isProfileOrDeviceOwner(context.userManager, context.devicePolicyManager, packageName)
@@ -169,7 +176,14 @@ private fun ApplicationInfo.isOtherUserHasInstallPackage(
.filter { it.id != userId } .filter { it.id != userId }
.any { packageManagers.isPackageInstalledAsUser(packageName, it.id) } .any { packageManagers.isPackageInstalledAsUser(packageName, it.id) }
private fun ApplicationInfo.shouldShowAccessRestrictedSettings(appOpsManager: AppOpsManager) = private fun ApplicationInfo.shouldShowAccessRestrictedSettings(context: Context): Boolean {
appOpsManager.noteOpNoThrow( 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.OP_ACCESS_RESTRICTED_SETTINGS, uid, packageName, null, null
) == AppOpsManager.MODE_IGNORED ) == AppOpsManager.MODE_IGNORED
}
}

View File

@@ -37,14 +37,14 @@ import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
import androidx.preference.TwoStatePreference;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.Utils; import com.android.settings.Utils;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.widget.EmptyTextSettings; import com.android.settings.widget.EmptyTextSettings;
import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.applications.ServiceListing; import com.android.settingslib.applications.ServiceListing;
import com.android.settingslib.widget.AppSwitchPreference; import com.android.settingslib.widget.TwoTargetPreference;
import java.util.List; import java.util.List;
@@ -121,10 +121,12 @@ public abstract class ManagedServiceSettings extends EmptyTextSettings {
} }
final CharSequence finalTitle = title; final CharSequence finalTitle = title;
final String summary = service.loadLabel(mPm).toString(); final String summary = service.loadLabel(mPm).toString();
final TwoStatePreference pref = new AppSwitchPreference(getPrefContext()); final RestrictedSwitchPreference pref =
new RestrictedSwitchPreference(getPrefContext());
pref.setPersistent(false); pref.setPersistent(false);
pref.setIcon(mIconDrawableFactory.getBadgedIcon(service, service.applicationInfo, pref.setIcon(mIconDrawableFactory.getBadgedIcon(service, service.applicationInfo,
UserHandle.getUserId(service.applicationInfo.uid))); UserHandle.getUserId(service.applicationInfo.uid)));
pref.setIconSize(TwoTargetPreference.ICON_SIZE_MEDIUM);
if (title != null && !title.equals(summary)) { if (title != null && !title.equals(summary)) {
pref.setTitle(title); pref.setTitle(title);
pref.setSummary(summary); pref.setSummary(summary);
@@ -150,6 +152,9 @@ public abstract class ManagedServiceSettings extends EmptyTextSettings {
} }
}); });
pref.setKey(cn.flattenToString()); pref.setKey(cn.flattenToString());
if (!pref.isChecked()) {
pref.checkEcmRestrictionAndSetDisabled(mConfig.permission, service.packageName);
}
screen.addPreference(pref); screen.addPreference(pref);
} }
highlightPreferenceIfNeeded(); highlightPreferenceIfNeeded();

View File

@@ -21,6 +21,7 @@ import android.os.UserHandle;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.AttributeSet; import android.util.AttributeSet;
import androidx.annotation.NonNull;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceViewHolder; import androidx.preference.PreferenceViewHolder;
@@ -72,11 +73,19 @@ public class RestrictedAppPreference extends AppPreference {
@Override @Override
public void setEnabled(boolean enabled) { public void setEnabled(boolean enabled) {
if (isDisabledByAdmin() && enabled) { boolean changed = false;
return; 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) { public void setDisabledByAdmin(RestrictedLockUtils.EnforcedAdmin admin) {
if (mHelper.setDisabledByAdmin(admin)) { if (mHelper.setDisabledByAdmin(admin)) {
@@ -88,6 +97,10 @@ public class RestrictedAppPreference extends AppPreference {
return mHelper.isDisabledByAdmin(); return mHelper.isDisabledByAdmin();
} }
public boolean isDisabledByEcm() {
return mHelper.isDisabledByEcm();
}
public void useAdminDisabledSummary(boolean useSummary) { public void useAdminDisabledSummary(boolean useSummary) {
mHelper.useAdminDisabledSummary(useSummary); mHelper.useAdminDisabledSummary(useSummary);
} }
@@ -112,4 +125,15 @@ public class RestrictedAppPreference extends AppPreference {
public void checkRestrictionAndSetDisabled(String userRestriction, int userId) { public void checkRestrictionAndSetDisabled(String userRestriction, int userId) {
mHelper.checkRestrictionAndSetDisabled(userRestriction, 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);
}
} }

View File

@@ -38,6 +38,7 @@ import androidx.test.core.app.ApplicationProvider;
import com.android.settings.SettingsActivity; import com.android.settings.SettingsActivity;
import com.android.settings.testutils.shadow.ShadowDevicePolicyManager; import com.android.settings.testutils.shadow.ShadowDevicePolicyManager;
import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
@@ -57,7 +58,10 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
/** Tests for {@link AccessibilityDetailsSettingsFragment}. */ /** Tests for {@link AccessibilityDetailsSettingsFragment}. */
@Config(shadows = ShadowDevicePolicyManager.class) @Config(shadows = {
ShadowDevicePolicyManager.class,
ShadowRestrictedLockUtilsInternal.class
})
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public class AccessibilityDetailsSettingsFragmentTest { public class AccessibilityDetailsSettingsFragmentTest {
private static final String PACKAGE_NAME = "com.foo.bar"; private static final String PACKAGE_NAME = "com.foo.bar";

View File

@@ -52,6 +52,7 @@ import com.android.settings.testutils.XmlTestUtils;
import com.android.settings.testutils.shadow.ShadowApplicationPackageManager; import com.android.settings.testutils.shadow.ShadowApplicationPackageManager;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils; import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal;
import com.android.settings.testutils.shadow.ShadowUserManager; import com.android.settings.testutils.shadow.ShadowUserManager;
import com.android.settingslib.RestrictedPreference; import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -87,6 +88,7 @@ import java.util.List;
ShadowUserManager.class, ShadowUserManager.class,
ShadowColorDisplayManager.class, ShadowColorDisplayManager.class,
ShadowApplicationPackageManager.class, ShadowApplicationPackageManager.class,
ShadowRestrictedLockUtilsInternal.class,
}) })
public class AccessibilitySettingsTest { public class AccessibilitySettingsTest {
private static final String PACKAGE_NAME = "com.android.test"; private static final String PACKAGE_NAME = "com.android.test";

View File

@@ -31,9 +31,14 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo; 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 androidx.test.core.app.ApplicationProvider;
import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal;
import com.android.settingslib.RestrictedPreference; import com.android.settingslib.RestrictedPreference;
import org.junit.Rule; import org.junit.Rule;
@@ -45,6 +50,7 @@ import org.mockito.Spy;
import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule; import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException; import java.io.IOException;
@@ -53,6 +59,9 @@ import java.util.List;
/** Test for {@link RestrictedPreferenceHelper}. */ /** Test for {@link RestrictedPreferenceHelper}. */
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@Config(shadows = {
ShadowRestrictedLockUtilsInternal.class
})
public class RestrictedPreferenceHelperTest { public class RestrictedPreferenceHelperTest {
private static final String PACKAGE_NAME = "com.android.test"; private static final String PACKAGE_NAME = "com.android.test";
@@ -72,6 +81,11 @@ public class RestrictedPreferenceHelperTest {
private AccessibilityShortcutInfo mShortcutInfo; private AccessibilityShortcutInfo mShortcutInfo;
private final RestrictedPreferenceHelper mHelper = new RestrictedPreferenceHelper(mContext); private final RestrictedPreferenceHelper mHelper = new RestrictedPreferenceHelper(mContext);
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@Test @Test
public void createAccessibilityServicePreferenceList_hasOneInfo_containsSameKey() { public void createAccessibilityServicePreferenceList_hasOneInfo_containsSameKey() {
final String key = COMPONENT_NAME.flattenToString(); final String key = COMPONENT_NAME.flattenToString();
@@ -85,6 +99,37 @@ public class RestrictedPreferenceHelperTest {
assertThat(preference.getKey()).isEqualTo(key); 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<AccessibilityServiceInfo> infoList = new ArrayList<>(
singletonList(mServiceInfo));
final List<RestrictedPreference> 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<AccessibilityServiceInfo> infoList = new ArrayList<>(
singletonList(mServiceInfo));
final List<RestrictedPreference> preferenceList =
mHelper.createAccessibilityServicePreferenceList(infoList);
final RestrictedPreference preference = preferenceList.get(0);
assertThat(preference.isDisabledByEcm()).isFalse();
}
@Test @Test
public void createAccessibilityActivityPreferenceList_hasOneInfo_containsSameKey() { public void createAccessibilityActivityPreferenceList_hasOneInfo_containsSameKey() {
final String key = COMPONENT_NAME.flattenToString(); final String key = COMPONENT_NAME.flattenToString();

View File

@@ -16,13 +16,35 @@
package com.android.settings.applications.specialaccess.premiumsms; 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 static org.mockito.Mockito.verify;
import android.app.settings.SettingsEnums; import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.Process;
import android.telephony.SmsManager; 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.internal.logging.nano.MetricsProto;
import com.android.settings.datausage.AppStateDataUsageBridge;
import com.android.settings.testutils.FakeFeatureFactory; 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.Before;
import org.junit.Test; import org.junit.Test;
@@ -30,19 +52,28 @@ import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@Config(shadows = {
ShadowRestrictedLockUtilsInternal.class
})
public class PremiumSmsAccessTest { public class PremiumSmsAccessTest {
private FakeFeatureFactory mFeatureFactory; private FakeFeatureFactory mFeatureFactory;
private PremiumSmsAccess mFragment; private PremiumSmsAccess mFragment;
private Context mContext;
@Before @Before
public void setUp() { public void setUp() {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
mFeatureFactory = FakeFeatureFactory.setupForTest(); mFeatureFactory = FakeFeatureFactory.setupForTest();
mFragment = new PremiumSmsAccess(); mFragment = new PremiumSmsAccess();
mFragment.onAttach(RuntimeEnvironment.application); mContext = RuntimeEnvironment.application;
mFragment.onAttach(mContext);
} }
@Test @Test
@@ -74,4 +105,89 @@ public class PremiumSmsAccessTest {
"app", "app",
SmsManager.PREMIUM_SMS_CONSENT_ALWAYS_ALLOW); 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<ApplicationsState.AppEntry> createAppEntries(String... packageNames) {
final ArrayList<ApplicationsState.AppEntry> 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;
}
} }

View File

@@ -25,6 +25,7 @@ import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@@ -163,6 +164,81 @@ public class UnrestrictedDataAccessPreferenceControllerTest {
mController.onRebuildComplete(createAppEntries(testPkg1, testPkg2)); 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<AppEntry> createAppEntries(String... packageNames) { private ArrayList<AppEntry> createAppEntries(String... packageNames) {
final ArrayList<AppEntry> appEntries = new ArrayList<>(); final ArrayList<AppEntry> appEntries = new ArrayList<>();
for (int i = 0; i < packageNames.length; ++i) { for (int i = 0; i < packageNames.length; ++i) {

View File

@@ -15,9 +15,11 @@
*/ */
package com.android.settings.testutils.shadow; package com.android.settings.testutils.shadow;
import android.annotation.NonNull;
import android.annotation.UserIdInt; import android.annotation.UserIdInt;
import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManager;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import com.android.internal.util.ArrayUtils; import com.android.internal.util.ArrayUtils;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
@@ -38,6 +40,8 @@ public class ShadowRestrictedLockUtilsInternal {
private static DevicePolicyManager sDevicePolicyManager; private static DevicePolicyManager sDevicePolicyManager;
private static String[] sDisabledTypes; private static String[] sDisabledTypes;
private static int sKeyguardDisabledFeatures; private static int sKeyguardDisabledFeatures;
private static String[] sEcmRestrictedPkgs;
private static boolean sAccessibilityServiceRestricted;
@Resetter @Resetter
public static void reset() { public static void reset() {
@@ -47,6 +51,8 @@ public class ShadowRestrictedLockUtilsInternal {
sDisabledTypes = new String[0]; sDisabledTypes = new String[0];
sMaximumTimeToLockIsSet = false; sMaximumTimeToLockIsSet = false;
sMteOverridden = false; sMteOverridden = false;
sEcmRestrictedPkgs = new String[0];
sAccessibilityServiceRestricted = false;
} }
@Implementation @Implementation
@@ -108,10 +114,35 @@ public class ShadowRestrictedLockUtilsInternal {
return sMteOverridden ? new EnforcedAdmin() : null; 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) { public static void setRestricted(boolean restricted) {
sIsRestricted = restricted; sIsRestricted = restricted;
} }
public static void setEcmRestrictedPkgs(String... pkgs) {
sEcmRestrictedPkgs = pkgs;
}
public static void setRestrictedPkgs(String... pkgs) { public static void setRestrictedPkgs(String... pkgs) {
sRestrictedPkgs = pkgs; sRestrictedPkgs = pkgs;
} }

View File

@@ -19,11 +19,16 @@ package com.android.settings.spa.app.appinfo
import android.app.AppOpsManager import android.app.AppOpsManager
import android.app.KeyguardManager import android.app.KeyguardManager
import android.app.admin.DevicePolicyManager import android.app.admin.DevicePolicyManager
import android.app.ecm.EnhancedConfirmationManager
import android.content.Context import android.content.Context
import android.content.pm.ApplicationInfo import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.pm.UserInfo import android.content.pm.UserInfo
import android.os.UserManager 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.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertIsNotDisplayed import androidx.compose.ui.test.assertIsNotDisplayed
@@ -61,6 +66,9 @@ class AppInfoSettingsMoreOptionsTest {
@get:Rule @get:Rule
val composeTestRule = createComposeRule() val composeTestRule = createComposeRule()
@get:Rule
val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
private lateinit var mockSession: MockitoSession private lateinit var mockSession: MockitoSession
@Spy @Spy
@@ -81,6 +89,9 @@ class AppInfoSettingsMoreOptionsTest {
@Mock @Mock
private lateinit var appOpsManager: AppOpsManager private lateinit var appOpsManager: AppOpsManager
@Mock
private lateinit var enhancedConfirmationManager: EnhancedConfirmationManager
@Mock @Mock
private lateinit var keyguardManager: KeyguardManager private lateinit var keyguardManager: KeyguardManager
@@ -103,6 +114,8 @@ class AppInfoSettingsMoreOptionsTest {
whenever(context.devicePolicyManager).thenReturn(devicePolicyManager) whenever(context.devicePolicyManager).thenReturn(devicePolicyManager)
whenever(context.appOpsManager).thenReturn(appOpsManager) whenever(context.appOpsManager).thenReturn(appOpsManager)
whenever(context.getSystemService(KeyguardManager::class.java)).thenReturn(keyguardManager) whenever(context.getSystemService(KeyguardManager::class.java)).thenReturn(keyguardManager)
whenever(context.getSystemService(EnhancedConfirmationManager::class.java))
.thenReturn(enhancedConfirmationManager)
whenever(keyguardManager.isKeyguardSecure).thenReturn(false) whenever(keyguardManager.isKeyguardSecure).thenReturn(false)
whenever(Utils.isProfileOrDeviceOwner(userManager, devicePolicyManager, PACKAGE_NAME)) whenever(Utils.isProfileOrDeviceOwner(userManager, devicePolicyManager, PACKAGE_NAME))
.thenReturn(false) .thenReturn(false)
@@ -158,7 +171,9 @@ class AppInfoSettingsMoreOptionsTest {
} }
@Test @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( whenever(
appOpsManager.noteOpNoThrow( appOpsManager.noteOpNoThrow(
AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, UID, PACKAGE_NAME, null, null 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) { private fun setContent(app: ApplicationInfo) {
composeTestRule.setContent { composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) { CompositionLocalProvider(LocalContext provides context) {

View File

@@ -36,6 +36,9 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.platform.test.annotations.EnableFlags; 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 android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.core.app.ApplicationProvider; 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.settings.testutils.FakeFeatureFactory;
import com.android.settingslib.RestrictedSwitchPreference; import com.android.settingslib.RestrictedSwitchPreference;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
@@ -58,6 +62,8 @@ public class ApprovalPreferenceControllerTest {
@Rule @Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule( public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(
SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT); SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
private Context mContext; private Context mContext;
private FakeFeatureFactory mFeatureFactory; private FakeFeatureFactory mFeatureFactory;
@@ -122,6 +128,7 @@ public class ApprovalPreferenceControllerTest {
} }
@Test @Test
@RequiresFlagsDisabled(android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS)
public void updateState_checked() { public void updateState_checked() {
when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn( when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn(
AppOpsManager.MODE_ALLOWED); AppOpsManager.MODE_ALLOWED);
@@ -136,19 +143,26 @@ public class ApprovalPreferenceControllerTest {
} }
@Test @Test
@RequiresFlagsDisabled(android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS)
public void restrictedSettings_appOpsDisabled() { public void restrictedSettings_appOpsDisabled() {
when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn( Assert.assertFalse(android.security.Flags.extendEcmToAllSettings());
AppOpsManager.MODE_ERRORED); 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); when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(false);
RestrictedSwitchPreference pref = new RestrictedSwitchPreference( RestrictedSwitchPreference pref = new RestrictedSwitchPreference(
mContext); mContext);
pref.setAppOps(mAppOpsManager); pref.setAppOps(mAppOpsManager);
mController.setSettingIdentifier(AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS);
mController.updateState(pref); mController.updateState(pref);
verify(mAppOpsManager).noteOpNoThrow(anyInt(), anyInt(), anyString());
assertThat(pref.isEnabled()).isFalse(); assertThat(pref.isEnabled()).isFalse();
} }
@Test @Test
@RequiresFlagsDisabled(android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS)
public void restrictedSettings_serviceAlreadyEnabled() { public void restrictedSettings_serviceAlreadyEnabled() {
when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn( when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn(
AppOpsManager.MODE_ERRORED); AppOpsManager.MODE_ERRORED);