Wired remaining SpecialAppAccess for ECM.

This change enables the remaining settings for ECM, and adds tests for
both this and previous ECM changes.

Bug: 297372999
Test: Tested on device
Test: atest SpaPrivilegedLibTests
Test: atest com.android.settings.applications.specialaccess.notificationaccess
Test: atest com.android.settings.datausage
Test: atest PremiumSmsAccessTest
Test: atest RestrictedPreferenceHelperTest
Change-Id: I73d39d765dba0c1a75111c37b29ccf1c85d2cdd8
This commit is contained in:
Hani Kazmi
2023-11-10 16:19:51 +00:00
parent 73b273d781
commit 96ab2b9e25
11 changed files with 343 additions and 14 deletions

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,8 @@ 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,
appEntry.info.uid);
} }
private int getCurrentValue() { private int getCurrentValue() {

View File

@@ -21,6 +21,7 @@ import android.os.UserHandle;
import android.view.View; import android.view.View;
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 +37,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_RESTRICTION_KEY = "android:unrestricted_data_access";
private final ApplicationsState mApplicationsState; private final ApplicationsState mApplicationsState;
private final AppEntry mEntry; private final AppEntry mEntry;
@@ -58,6 +60,8 @@ 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_RESTRICTION_KEY, entry.info.packageName,
entry.info.uid);
updateState(); updateState();
setKey(generateKey(mEntry)); setKey(generateKey(mEntry));
@@ -166,10 +170,25 @@ 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
* @param uid the uid of the package
*/
public void checkEcmRestrictionAndSetDisabled(@Nullable String packageName, int uid) {
mHelper.checkEcmRestrictionAndSetDisabled(ECM_RESTRICTION_KEY, packageName, uid);
}
// 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 +198,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,8 @@ 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,
entry.info.uid);
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, app.uid);
} }
pref.setOnPreferenceClickListener(preference -> { pref.setOnPreferenceClickListener(preference -> {
AppInfoBase.startAppInfoFragment( AppInfoBase.startAppInfoFragment(

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,10 @@ public abstract class ManagedServiceSettings extends EmptyTextSettings {
} }
}); });
pref.setKey(cn.flattenToString()); pref.setKey(cn.flattenToString());
if (!pref.isChecked()) {
pref.checkEcmRestrictionAndSetDisabled(mConfig.permission, service.packageName,
service.applicationInfo.uid);
}
screen.addPreference(pref); screen.addPreference(pref);
} }
highlightPreferenceIfNeeded(); highlightPreferenceIfNeeded();

View File

@@ -72,11 +72,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 +96,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 +124,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 restriction The key identifying the setting
* @param packageName the package to check the restriction for
* @param uid the uid of the package
*/
public void checkEcmRestrictionAndSetDisabled(String restriction, String packageName, int uid) {
mHelper.checkEcmRestrictionAndSetDisabled(restriction, packageName, uid);
}
} }

View File

@@ -31,9 +31,12 @@ 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.flag.junit.SetFlagsRule;
import android.security.Flags;
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 +48,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 +57,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 +79,9 @@ 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();
@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 +95,35 @@ public class RestrictedPreferenceHelperTest {
assertThat(preference.getKey()).isEqualTo(key); assertThat(preference.getKey()).isEqualTo(key);
} }
@Test
public void createAccessibilityServicePreferenceList_ecmRestricted_prefIsEcmRestricted() {
mSetFlagsRule.enableFlags(Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS);
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
public void createAccessibilityServicePreferenceList_ecmNotRestricted_prefIsNotEcmRestricted() {
mSetFlagsRule.enableFlags(Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS);
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,94 @@ 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);
final ApplicationsState.AppEntry entry = ReflectionHelpers
.getField(preference, "mAppEntry");
// 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";
doNothing().when(mContext).startActivity(any());
ShadowRestrictedLockUtilsInternal.setEcmRestrictedPkgs();
doAnswer((invocation) -> {
final RestrictedDropDownPreference preference = invocation.getArgument(0);
final ApplicationsState.AppEntry entry = ReflectionHelpers
.getField(preference, "mAppEntry");
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,83 @@ public class UnrestrictedDataAccessPreferenceControllerTest {
mController.onRebuildComplete(createAppEntries(testPkg1, testPkg2)); mController.onRebuildComplete(createAppEntries(testPkg1, testPkg2));
} }
@Test
public void onRebuildComplete_ecmRestricted_shouldBeDisabled() {
mFragment = spy(new UnrestrictedDataAccess());
doNothing().when(mFragment).setLoading(anyBoolean(), anyBoolean());
mController.setParentFragment(mFragment);
final Context context = spy(mContext);
mPreferenceManager = new PreferenceManager(context);
mPreferenceScreen = spy(mPreferenceManager.createPreferenceScreen(context));
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(context).startActivity(any());
ShadowRestrictedLockUtilsInternal.setEcmRestrictedPkgs(testPkg);
doAnswer((invocation) -> {
final UnrestrictedDataAccessPreference preference = invocation.getArgument(0);
final AppEntry entry = preference.getEntry();
// 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(context).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());
doNothing().when(mFragment).setLoading(anyBoolean(), anyBoolean());
mController.setParentFragment(mFragment);
final Context context = spy(mContext);
mPreferenceManager = new PreferenceManager(context);
mPreferenceScreen = spy(mPreferenceManager.createPreferenceScreen(context));
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(context).startActivity(any());
ShadowRestrictedLockUtilsInternal.setEcmRestrictedPkgs();
doAnswer((invocation) -> {
final UnrestrictedDataAccessPreference preference = invocation.getArgument(0);
final AppEntry entry = preference.getEntry();
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(context, 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,12 @@
*/ */
package com.android.settings.testutils.shadow; package com.android.settings.testutils.shadow;
import android.annotation.NonNull;
import android.annotation.Nullable;
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 +41,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 +52,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 +115,31 @@ 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 restriction,
int uid,
@Nullable String packageName) {
if (ArrayUtils.contains(sEcmRestrictedPkgs, packageName)) {
return new Intent();
}
return null;
}
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

@@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
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.spy; import static org.mockito.Mockito.spy;
@@ -137,14 +138,19 @@ public class ApprovalPreferenceControllerTest {
@Test @Test
public void restrictedSettings_appOpsDisabled() { public void restrictedSettings_appOpsDisabled() {
when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn( when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString(), isNull(), isNull()))
AppOpsManager.MODE_ERRORED); .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.setAppOpStr(AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS);
mController.updateState(pref); mController.updateState(pref);
verify(mContext).getSystemService(Context.APP_OPS_SERVICE);
verify(mAppOpsManager).noteOpNoThrow(anyInt(), anyInt(), anyString(), isNull(), isNull());
assertThat(pref.isEnabled()).isFalse(); assertThat(pref.isEnabled()).isFalse();
} }