Loading res/xml/app_data_usage.xml +10 −4 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" xmlns:settings="http://schemas.android.com/apk/res-auto" android:key="app_data_usage_screen" android:title="@string/data_usage_app_summary_title"> <com.android.settings.datausage.SpinnerPreference Loading Loading @@ -50,15 +52,19 @@ android:key="app_settings" android:title="@string/data_usage_app_settings" /> <SwitchPreference <com.android.settingslib.RestrictedSwitchPreference android:key="restrict_background" android:title="@string/data_usage_app_restrict_background" android:summary="@string/data_usage_app_restrict_background_summary" /> android:summary="@string/data_usage_app_restrict_background_summary" settings:useAdditionalSummary="true" settings:restrictedSwitchSummary="@string/disabled_by_admin" /> <SwitchPreference <com.android.settingslib.RestrictedSwitchPreference android:key="unrestricted_data_saver" android:title="@string/unrestricted_app_title" android:summary="@string/unrestricted_app_summary" /> android:summary="@string/unrestricted_app_summary" settings:useAdditionalSummary="true" settings:restrictedSwitchSummary="@string/disabled_by_admin" /> </PreferenceCategory> Loading src/com/android/settings/datausage/AppDataUsage.java +13 −5 Original line number Diff line number Diff line Loading @@ -33,7 +33,6 @@ import android.os.Bundle; import android.os.RemoteException; import android.os.UserHandle; import android.support.annotation.VisibleForTesting; import android.support.v14.preference.SwitchPreference; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceCategory; import android.text.format.Formatter; Loading @@ -48,6 +47,9 @@ import com.android.settings.R; import com.android.settings.applications.AppInfoBase; import com.android.settings.widget.EntityHeaderController; import com.android.settingslib.AppItem; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import com.android.settingslib.RestrictedSwitchPreference; import com.android.settingslib.net.ChartData; import com.android.settingslib.net.ChartDataLoader; import com.android.settingslib.net.UidDetail; Loading Loading @@ -80,7 +82,7 @@ public class AppDataUsage extends DataUsageBase implements Preference.OnPreferen private Preference mForegroundUsage; private Preference mBackgroundUsage; private Preference mAppSettings; private SwitchPreference mRestrictBackground; private RestrictedSwitchPreference mRestrictBackground; private PreferenceCategory mAppList; private Drawable mIcon; Loading @@ -97,7 +99,7 @@ public class AppDataUsage extends DataUsageBase implements Preference.OnPreferen private AppItem mAppItem; private Intent mAppSettingsIntent; private SpinnerPreference mCycle; private SwitchPreference mUnrestrictedData; private RestrictedSwitchPreference mUnrestrictedData; private DataSaverBackend mDataSaverBackend; @Override Loading Loading @@ -160,9 +162,11 @@ public class AppDataUsage extends DataUsageBase implements Preference.OnPreferen removePreference(KEY_UNRESTRICTED_DATA); removePreference(KEY_RESTRICT_BACKGROUND); } else { mRestrictBackground = (SwitchPreference) findPreference(KEY_RESTRICT_BACKGROUND); mRestrictBackground = (RestrictedSwitchPreference) findPreference( KEY_RESTRICT_BACKGROUND); mRestrictBackground.setOnPreferenceChangeListener(this); mUnrestrictedData = (SwitchPreference) findPreference(KEY_UNRESTRICTED_DATA); mUnrestrictedData = (RestrictedSwitchPreference) findPreference( KEY_UNRESTRICTED_DATA); mUnrestrictedData.setOnPreferenceChangeListener(this); } mDataSaverBackend = new DataSaverBackend(getContext()); Loading Loading @@ -261,8 +265,11 @@ public class AppDataUsage extends DataUsageBase implements Preference.OnPreferen } private void updatePrefs(boolean restrictBackground, boolean unrestrictData) { final EnforcedAdmin admin = RestrictedLockUtils.checkIfMeteredDataRestricted( getContext(), mPackageName, UserHandle.getUserId(mAppItem.key)); if (mRestrictBackground != null) { mRestrictBackground.setChecked(!restrictBackground); mRestrictBackground.setDisabledByAdmin(admin); } if (mUnrestrictedData != null) { if (restrictBackground) { Loading @@ -270,6 +277,7 @@ public class AppDataUsage extends DataUsageBase implements Preference.OnPreferen } else { mUnrestrictedData.setVisible(true); mUnrestrictedData.setChecked(unrestrictData); mUnrestrictedData.setDisabledByAdmin(admin); } } } Loading src/com/android/settings/datausage/UnrestrictedDataAccess.java +50 −5 Original line number Diff line number Diff line Loading @@ -14,6 +14,8 @@ package com.android.settings.datausage; import static com.android.settingslib.RestrictedLockUtils.checkIfMeteredDataRestricted; import android.app.Application; import android.content.Context; import android.os.Bundle; Loading @@ -37,6 +39,8 @@ import com.android.settings.core.FeatureFlags; import com.android.settings.datausage.AppStateDataUsageBridge.DataUsageState; import com.android.settings.overlay.FeatureFactory; import com.android.settings.widget.AppSwitchPreference; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import com.android.settingslib.RestrictedPreferenceHelper; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.ApplicationsState.AppEntry; import com.android.settingslib.applications.ApplicationsState.AppFilter; Loading Loading @@ -172,6 +176,8 @@ public class UnrestrictedDataAccess extends SettingsPreferenceFragment preference.setOnPreferenceChangeListener(this); getPreferenceScreen().addPreference(preference); } else { preference.setDisabledByAdmin(checkIfMeteredDataRestricted(getContext(), entry.info.packageName, UserHandle.getUserId(entry.info.uid))); preference.reuse(); } preference.setOrder(i); Loading Loading @@ -242,16 +248,22 @@ public class UnrestrictedDataAccess extends SettingsPreferenceFragment return app != null && UserHandle.isApp(app.info.uid); } private class AccessPreference extends AppSwitchPreference @VisibleForTesting class AccessPreference extends AppSwitchPreference implements DataSaverBackend.Listener { private final AppEntry mEntry; private final DataUsageState mState; private final RestrictedPreferenceHelper mHelper; public AccessPreference(final Context context, AppEntry entry) { super(context); setWidgetLayoutResource(R.layout.restricted_switch_widget); mHelper = new RestrictedPreferenceHelper(context, this, null); mEntry = entry; mState = (DataUsageState) mEntry.extraInfo; mEntry.ensureLabel(getContext()); setDisabledByAdmin(checkIfMeteredDataRestricted(context, entry.info.packageName, UserHandle.getUserId(entry.info.uid))); setState(); if (mEntry.icon != null) { setIcon(mEntry.icon); Loading Loading @@ -291,12 +303,21 @@ public class UnrestrictedDataAccess extends SettingsPreferenceFragment } } @Override public void performClick() { if (!mHelper.performClick()) { super.performClick(); } } // Sets UI state based on whitelist/blacklist status. private void setState() { setTitle(mEntry.label); if (mState != null) { setChecked(mState.isDataSaverWhitelisted); if (mState.isDataSaverBlacklisted) { if (isDisabledByAdmin()) { setSummary(R.string.disabled_by_admin); } else if (mState.isDataSaverBlacklisted) { setSummary(R.string.restrict_background_blacklisted); } else { setSummary(""); Loading @@ -323,10 +344,21 @@ public class UnrestrictedDataAccess extends SettingsPreferenceFragment } }); } holder.findViewById(android.R.id.widget_frame) .setVisibility(mState != null && mState.isDataSaverBlacklisted final boolean disabledByAdmin = isDisabledByAdmin(); final View widgetFrame = holder.findViewById(android.R.id.widget_frame); if (disabledByAdmin) { widgetFrame.setVisibility(View.VISIBLE); } else { widgetFrame.setVisibility(mState != null && mState.isDataSaverBlacklisted ? View.INVISIBLE : View.VISIBLE); } super.onBindViewHolder(holder); mHelper.onBindViewHolder(holder); holder.findViewById(R.id.restricted_icon).setVisibility( disabledByAdmin ? View.VISIBLE : View.GONE); holder.findViewById(android.R.id.switch_widget).setVisibility( disabledByAdmin ? View.GONE : View.VISIBLE); } @Override Loading @@ -348,6 +380,19 @@ public class UnrestrictedDataAccess extends SettingsPreferenceFragment reuse(); } } public void setDisabledByAdmin(EnforcedAdmin admin) { mHelper.setDisabledByAdmin(admin); } public boolean isDisabledByAdmin() { return mHelper.isDisabledByAdmin(); } @VisibleForTesting public AppEntry getEntryForTest() { return mEntry; } } } tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java +36 −3 Original line number Diff line number Diff line Loading @@ -29,8 +29,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.pm.PackageManager; import android.net.NetworkPolicyManager; import android.os.Bundle; import android.support.v14.preference.SwitchPreference; import android.support.v7.preference.PreferenceManager; import android.support.v7.preference.PreferenceScreen; import android.util.ArraySet; Loading @@ -40,8 +40,11 @@ import com.android.settings.TestConfig; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.ShadowEntityHeaderController; import com.android.settings.testutils.shadow.ShadowRestrictedLockUtils; import com.android.settings.widget.EntityHeaderController; import com.android.settingslib.AppItem; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import com.android.settingslib.RestrictedSwitchPreference; import com.android.settingslib.wrapper.PackageManagerWrapper; import org.junit.After; Loading @@ -57,7 +60,10 @@ import org.robolectric.util.ReflectionHelpers; @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION, shadows = ShadowEntityHeaderController.class) shadows = { ShadowEntityHeaderController.class, ShadowRestrictedLockUtils.class }) public class AppDataUsageTest { @Mock(answer = Answers.RETURNS_DEEP_STUBS) Loading Loading @@ -134,7 +140,7 @@ public class AppDataUsageTest { public void changePreference_backgroundData_shouldUpdateUI() { mFragment = spy(new AppDataUsage()); final AppItem appItem = new AppItem(123456789); final SwitchPreference pref = mock(SwitchPreference.class); final RestrictedSwitchPreference pref = mock(RestrictedSwitchPreference.class); final DataSaverBackend dataSaverBackend = mock(DataSaverBackend.class); ReflectionHelpers.setField(mFragment, "mAppItem", appItem); ReflectionHelpers.setField(mFragment, "mRestrictBackground", pref); Loading @@ -146,4 +152,31 @@ public class AppDataUsageTest { verify(mFragment).updatePrefs(); } @Test public void updatePrefs_restrictedByAdmin_shouldDisablePreference() { mFragment = spy(new AppDataUsage()); final int testUid = 123123; final AppItem appItem = new AppItem(testUid); final RestrictedSwitchPreference restrictBackgroundPref = mock(RestrictedSwitchPreference.class); final RestrictedSwitchPreference unrestrictedDataPref = mock(RestrictedSwitchPreference.class); final DataSaverBackend dataSaverBackend = mock(DataSaverBackend.class); final NetworkPolicyManager networkPolicyManager = mock(NetworkPolicyManager.class); ReflectionHelpers.setField(mFragment, "mAppItem", appItem); ReflectionHelpers.setField(mFragment, "mRestrictBackground", restrictBackgroundPref); ReflectionHelpers.setField(mFragment, "mUnrestrictedData", unrestrictedDataPref); ReflectionHelpers.setField(mFragment, "mDataSaverBackend", dataSaverBackend); ReflectionHelpers.setField(mFragment.services, "mPolicyManager", networkPolicyManager); ShadowRestrictedLockUtils.setRestricted(true); doReturn(NetworkPolicyManager.POLICY_NONE).when(networkPolicyManager) .getUidPolicy(testUid); mFragment.updatePrefs(); verify(restrictBackgroundPref).setDisabledByAdmin(any(EnforcedAdmin.class)); verify(unrestrictedDataPref).setDisabledByAdmin(any(EnforcedAdmin.class)); } } tests/robotests/src/com/android/settings/datausage/UnrestrictedDataAccessTest.java +93 −4 Original line number Diff line number Diff line Loading @@ -16,41 +16,68 @@ package com.android.settings.datausage; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.content.Context; import android.content.pm.ApplicationInfo; import android.os.Process; import android.support.v7.preference.PreferenceManager; import android.support.v7.preference.PreferenceScreen; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; import com.android.settings.TestConfig; import com.android.settings.datausage.AppStateDataUsageBridge.DataUsageState; import com.android.settings.datausage.UnrestrictedDataAccess.AccessPreference; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settingslib.applications.ApplicationsState; import com.android.settings.testutils.shadow.ShadowRestrictedLockUtils; import com.android.settingslib.applications.ApplicationsState.AppEntry; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.util.ReflectionHelpers; import java.util.ArrayList; @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION, shadows = { ShadowRestrictedLockUtils.class }) public class UnrestrictedDataAccessTest { @Mock private ApplicationsState.AppEntry mAppEntry; private AppEntry mAppEntry; private UnrestrictedDataAccess mFragment; private FakeFeatureFactory mFeatureFactory; @Mock private PreferenceScreen mPreferenceScreen; @Mock private PreferenceManager mPreferenceManager; @Mock private DataSaverBackend mDataSaverBackend; @Before public void setUp() { MockitoAnnotations.initMocks(this); mFeatureFactory = FakeFeatureFactory.setupForTest(); mFragment = new UnrestrictedDataAccess(); mFragment = spy(new UnrestrictedDataAccess()); } @Test Loading Loading @@ -80,4 +107,66 @@ public class UnrestrictedDataAccessTest { eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_UNL_DATA_DENY), eq("app")); } @Test public void testOnRebuildComplete_restricted_shouldBeDisabled() { final Context context = RuntimeEnvironment.application; doReturn(context).when(mFragment).getContext(); doReturn(context).when(mPreferenceManager).getContext(); doReturn(true).when(mFragment).shouldAddPreference(any(AppEntry.class)); doNothing().when(mFragment).setLoading(anyBoolean(), anyBoolean()); doReturn(mPreferenceScreen).when(mFragment).getPreferenceScreen(); doReturn(mPreferenceManager).when(mFragment).getPreferenceManager(); ReflectionHelpers.setField(mFragment, "mDataSaverBackend", mDataSaverBackend); final String testPkg1 = "com.example.one"; final String testPkg2 = "com.example.two"; ShadowRestrictedLockUtils.setRestrictedPkgs(testPkg2); doAnswer((invocation) -> { final AccessPreference preference = invocation.getArgument(0); final AppEntry entry = preference.getEntryForTest(); // Verify preference is disabled by admin and the summary is changed accordingly. if (testPkg1.equals(entry.info.packageName)) { assertThat(preference.isDisabledByAdmin()).isFalse(); assertThat(preference.getSummary()).isEqualTo(""); } else if (testPkg2.equals(entry.info.packageName)) { assertThat(preference.isDisabledByAdmin()).isTrue(); assertThat(preference.getSummary()).isEqualTo( context.getString(R.string.disabled_by_admin)); } assertThat(preference.isChecked()).isFalse(); preference.performClick(); // Verify that when the preference is clicked, support details intent is launched // if the preference is disabled by admin, otherwise the switch is toggled. if (testPkg1.equals(entry.info.packageName)) { assertThat(preference.isChecked()).isTrue(); assertThat(ShadowRestrictedLockUtils.hasAdminSupportDetailsIntentLaunched()) .isFalse(); } else if (testPkg2.equals(entry.info.packageName)) { assertThat(preference.isChecked()).isFalse(); assertThat(ShadowRestrictedLockUtils.hasAdminSupportDetailsIntentLaunched()) .isTrue(); } ShadowRestrictedLockUtils.clearAdminSupportDetailsIntentLaunch(); return null; }).when(mPreferenceScreen).addPreference(any(AccessPreference.class)); mFragment.onRebuildComplete(createAppEntries(testPkg1, testPkg2)); } private ArrayList<AppEntry> createAppEntries(String... packageNames) { final ArrayList<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 AppEntry appEntry = spy(new AppEntry(RuntimeEnvironment.application, info, i)); appEntry.extraInfo = new DataUsageState(false, false); doNothing().when(appEntry).ensureLabel(any(Context.class)); ReflectionHelpers.setField(appEntry, "info", info); appEntries.add(appEntry); } return appEntries; } } Loading
res/xml/app_data_usage.xml +10 −4 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" xmlns:settings="http://schemas.android.com/apk/res-auto" android:key="app_data_usage_screen" android:title="@string/data_usage_app_summary_title"> <com.android.settings.datausage.SpinnerPreference Loading Loading @@ -50,15 +52,19 @@ android:key="app_settings" android:title="@string/data_usage_app_settings" /> <SwitchPreference <com.android.settingslib.RestrictedSwitchPreference android:key="restrict_background" android:title="@string/data_usage_app_restrict_background" android:summary="@string/data_usage_app_restrict_background_summary" /> android:summary="@string/data_usage_app_restrict_background_summary" settings:useAdditionalSummary="true" settings:restrictedSwitchSummary="@string/disabled_by_admin" /> <SwitchPreference <com.android.settingslib.RestrictedSwitchPreference android:key="unrestricted_data_saver" android:title="@string/unrestricted_app_title" android:summary="@string/unrestricted_app_summary" /> android:summary="@string/unrestricted_app_summary" settings:useAdditionalSummary="true" settings:restrictedSwitchSummary="@string/disabled_by_admin" /> </PreferenceCategory> Loading
src/com/android/settings/datausage/AppDataUsage.java +13 −5 Original line number Diff line number Diff line Loading @@ -33,7 +33,6 @@ import android.os.Bundle; import android.os.RemoteException; import android.os.UserHandle; import android.support.annotation.VisibleForTesting; import android.support.v14.preference.SwitchPreference; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceCategory; import android.text.format.Formatter; Loading @@ -48,6 +47,9 @@ import com.android.settings.R; import com.android.settings.applications.AppInfoBase; import com.android.settings.widget.EntityHeaderController; import com.android.settingslib.AppItem; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import com.android.settingslib.RestrictedSwitchPreference; import com.android.settingslib.net.ChartData; import com.android.settingslib.net.ChartDataLoader; import com.android.settingslib.net.UidDetail; Loading Loading @@ -80,7 +82,7 @@ public class AppDataUsage extends DataUsageBase implements Preference.OnPreferen private Preference mForegroundUsage; private Preference mBackgroundUsage; private Preference mAppSettings; private SwitchPreference mRestrictBackground; private RestrictedSwitchPreference mRestrictBackground; private PreferenceCategory mAppList; private Drawable mIcon; Loading @@ -97,7 +99,7 @@ public class AppDataUsage extends DataUsageBase implements Preference.OnPreferen private AppItem mAppItem; private Intent mAppSettingsIntent; private SpinnerPreference mCycle; private SwitchPreference mUnrestrictedData; private RestrictedSwitchPreference mUnrestrictedData; private DataSaverBackend mDataSaverBackend; @Override Loading Loading @@ -160,9 +162,11 @@ public class AppDataUsage extends DataUsageBase implements Preference.OnPreferen removePreference(KEY_UNRESTRICTED_DATA); removePreference(KEY_RESTRICT_BACKGROUND); } else { mRestrictBackground = (SwitchPreference) findPreference(KEY_RESTRICT_BACKGROUND); mRestrictBackground = (RestrictedSwitchPreference) findPreference( KEY_RESTRICT_BACKGROUND); mRestrictBackground.setOnPreferenceChangeListener(this); mUnrestrictedData = (SwitchPreference) findPreference(KEY_UNRESTRICTED_DATA); mUnrestrictedData = (RestrictedSwitchPreference) findPreference( KEY_UNRESTRICTED_DATA); mUnrestrictedData.setOnPreferenceChangeListener(this); } mDataSaverBackend = new DataSaverBackend(getContext()); Loading Loading @@ -261,8 +265,11 @@ public class AppDataUsage extends DataUsageBase implements Preference.OnPreferen } private void updatePrefs(boolean restrictBackground, boolean unrestrictData) { final EnforcedAdmin admin = RestrictedLockUtils.checkIfMeteredDataRestricted( getContext(), mPackageName, UserHandle.getUserId(mAppItem.key)); if (mRestrictBackground != null) { mRestrictBackground.setChecked(!restrictBackground); mRestrictBackground.setDisabledByAdmin(admin); } if (mUnrestrictedData != null) { if (restrictBackground) { Loading @@ -270,6 +277,7 @@ public class AppDataUsage extends DataUsageBase implements Preference.OnPreferen } else { mUnrestrictedData.setVisible(true); mUnrestrictedData.setChecked(unrestrictData); mUnrestrictedData.setDisabledByAdmin(admin); } } } Loading
src/com/android/settings/datausage/UnrestrictedDataAccess.java +50 −5 Original line number Diff line number Diff line Loading @@ -14,6 +14,8 @@ package com.android.settings.datausage; import static com.android.settingslib.RestrictedLockUtils.checkIfMeteredDataRestricted; import android.app.Application; import android.content.Context; import android.os.Bundle; Loading @@ -37,6 +39,8 @@ import com.android.settings.core.FeatureFlags; import com.android.settings.datausage.AppStateDataUsageBridge.DataUsageState; import com.android.settings.overlay.FeatureFactory; import com.android.settings.widget.AppSwitchPreference; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import com.android.settingslib.RestrictedPreferenceHelper; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.ApplicationsState.AppEntry; import com.android.settingslib.applications.ApplicationsState.AppFilter; Loading Loading @@ -172,6 +176,8 @@ public class UnrestrictedDataAccess extends SettingsPreferenceFragment preference.setOnPreferenceChangeListener(this); getPreferenceScreen().addPreference(preference); } else { preference.setDisabledByAdmin(checkIfMeteredDataRestricted(getContext(), entry.info.packageName, UserHandle.getUserId(entry.info.uid))); preference.reuse(); } preference.setOrder(i); Loading Loading @@ -242,16 +248,22 @@ public class UnrestrictedDataAccess extends SettingsPreferenceFragment return app != null && UserHandle.isApp(app.info.uid); } private class AccessPreference extends AppSwitchPreference @VisibleForTesting class AccessPreference extends AppSwitchPreference implements DataSaverBackend.Listener { private final AppEntry mEntry; private final DataUsageState mState; private final RestrictedPreferenceHelper mHelper; public AccessPreference(final Context context, AppEntry entry) { super(context); setWidgetLayoutResource(R.layout.restricted_switch_widget); mHelper = new RestrictedPreferenceHelper(context, this, null); mEntry = entry; mState = (DataUsageState) mEntry.extraInfo; mEntry.ensureLabel(getContext()); setDisabledByAdmin(checkIfMeteredDataRestricted(context, entry.info.packageName, UserHandle.getUserId(entry.info.uid))); setState(); if (mEntry.icon != null) { setIcon(mEntry.icon); Loading Loading @@ -291,12 +303,21 @@ public class UnrestrictedDataAccess extends SettingsPreferenceFragment } } @Override public void performClick() { if (!mHelper.performClick()) { super.performClick(); } } // Sets UI state based on whitelist/blacklist status. private void setState() { setTitle(mEntry.label); if (mState != null) { setChecked(mState.isDataSaverWhitelisted); if (mState.isDataSaverBlacklisted) { if (isDisabledByAdmin()) { setSummary(R.string.disabled_by_admin); } else if (mState.isDataSaverBlacklisted) { setSummary(R.string.restrict_background_blacklisted); } else { setSummary(""); Loading @@ -323,10 +344,21 @@ public class UnrestrictedDataAccess extends SettingsPreferenceFragment } }); } holder.findViewById(android.R.id.widget_frame) .setVisibility(mState != null && mState.isDataSaverBlacklisted final boolean disabledByAdmin = isDisabledByAdmin(); final View widgetFrame = holder.findViewById(android.R.id.widget_frame); if (disabledByAdmin) { widgetFrame.setVisibility(View.VISIBLE); } else { widgetFrame.setVisibility(mState != null && mState.isDataSaverBlacklisted ? View.INVISIBLE : View.VISIBLE); } super.onBindViewHolder(holder); mHelper.onBindViewHolder(holder); holder.findViewById(R.id.restricted_icon).setVisibility( disabledByAdmin ? View.VISIBLE : View.GONE); holder.findViewById(android.R.id.switch_widget).setVisibility( disabledByAdmin ? View.GONE : View.VISIBLE); } @Override Loading @@ -348,6 +380,19 @@ public class UnrestrictedDataAccess extends SettingsPreferenceFragment reuse(); } } public void setDisabledByAdmin(EnforcedAdmin admin) { mHelper.setDisabledByAdmin(admin); } public boolean isDisabledByAdmin() { return mHelper.isDisabledByAdmin(); } @VisibleForTesting public AppEntry getEntryForTest() { return mEntry; } } }
tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java +36 −3 Original line number Diff line number Diff line Loading @@ -29,8 +29,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.pm.PackageManager; import android.net.NetworkPolicyManager; import android.os.Bundle; import android.support.v14.preference.SwitchPreference; import android.support.v7.preference.PreferenceManager; import android.support.v7.preference.PreferenceScreen; import android.util.ArraySet; Loading @@ -40,8 +40,11 @@ import com.android.settings.TestConfig; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.ShadowEntityHeaderController; import com.android.settings.testutils.shadow.ShadowRestrictedLockUtils; import com.android.settings.widget.EntityHeaderController; import com.android.settingslib.AppItem; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import com.android.settingslib.RestrictedSwitchPreference; import com.android.settingslib.wrapper.PackageManagerWrapper; import org.junit.After; Loading @@ -57,7 +60,10 @@ import org.robolectric.util.ReflectionHelpers; @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION, shadows = ShadowEntityHeaderController.class) shadows = { ShadowEntityHeaderController.class, ShadowRestrictedLockUtils.class }) public class AppDataUsageTest { @Mock(answer = Answers.RETURNS_DEEP_STUBS) Loading Loading @@ -134,7 +140,7 @@ public class AppDataUsageTest { public void changePreference_backgroundData_shouldUpdateUI() { mFragment = spy(new AppDataUsage()); final AppItem appItem = new AppItem(123456789); final SwitchPreference pref = mock(SwitchPreference.class); final RestrictedSwitchPreference pref = mock(RestrictedSwitchPreference.class); final DataSaverBackend dataSaverBackend = mock(DataSaverBackend.class); ReflectionHelpers.setField(mFragment, "mAppItem", appItem); ReflectionHelpers.setField(mFragment, "mRestrictBackground", pref); Loading @@ -146,4 +152,31 @@ public class AppDataUsageTest { verify(mFragment).updatePrefs(); } @Test public void updatePrefs_restrictedByAdmin_shouldDisablePreference() { mFragment = spy(new AppDataUsage()); final int testUid = 123123; final AppItem appItem = new AppItem(testUid); final RestrictedSwitchPreference restrictBackgroundPref = mock(RestrictedSwitchPreference.class); final RestrictedSwitchPreference unrestrictedDataPref = mock(RestrictedSwitchPreference.class); final DataSaverBackend dataSaverBackend = mock(DataSaverBackend.class); final NetworkPolicyManager networkPolicyManager = mock(NetworkPolicyManager.class); ReflectionHelpers.setField(mFragment, "mAppItem", appItem); ReflectionHelpers.setField(mFragment, "mRestrictBackground", restrictBackgroundPref); ReflectionHelpers.setField(mFragment, "mUnrestrictedData", unrestrictedDataPref); ReflectionHelpers.setField(mFragment, "mDataSaverBackend", dataSaverBackend); ReflectionHelpers.setField(mFragment.services, "mPolicyManager", networkPolicyManager); ShadowRestrictedLockUtils.setRestricted(true); doReturn(NetworkPolicyManager.POLICY_NONE).when(networkPolicyManager) .getUidPolicy(testUid); mFragment.updatePrefs(); verify(restrictBackgroundPref).setDisabledByAdmin(any(EnforcedAdmin.class)); verify(unrestrictedDataPref).setDisabledByAdmin(any(EnforcedAdmin.class)); } }
tests/robotests/src/com/android/settings/datausage/UnrestrictedDataAccessTest.java +93 −4 Original line number Diff line number Diff line Loading @@ -16,41 +16,68 @@ package com.android.settings.datausage; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.content.Context; import android.content.pm.ApplicationInfo; import android.os.Process; import android.support.v7.preference.PreferenceManager; import android.support.v7.preference.PreferenceScreen; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; import com.android.settings.TestConfig; import com.android.settings.datausage.AppStateDataUsageBridge.DataUsageState; import com.android.settings.datausage.UnrestrictedDataAccess.AccessPreference; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settingslib.applications.ApplicationsState; import com.android.settings.testutils.shadow.ShadowRestrictedLockUtils; import com.android.settingslib.applications.ApplicationsState.AppEntry; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.util.ReflectionHelpers; import java.util.ArrayList; @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION, shadows = { ShadowRestrictedLockUtils.class }) public class UnrestrictedDataAccessTest { @Mock private ApplicationsState.AppEntry mAppEntry; private AppEntry mAppEntry; private UnrestrictedDataAccess mFragment; private FakeFeatureFactory mFeatureFactory; @Mock private PreferenceScreen mPreferenceScreen; @Mock private PreferenceManager mPreferenceManager; @Mock private DataSaverBackend mDataSaverBackend; @Before public void setUp() { MockitoAnnotations.initMocks(this); mFeatureFactory = FakeFeatureFactory.setupForTest(); mFragment = new UnrestrictedDataAccess(); mFragment = spy(new UnrestrictedDataAccess()); } @Test Loading Loading @@ -80,4 +107,66 @@ public class UnrestrictedDataAccessTest { eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_UNL_DATA_DENY), eq("app")); } @Test public void testOnRebuildComplete_restricted_shouldBeDisabled() { final Context context = RuntimeEnvironment.application; doReturn(context).when(mFragment).getContext(); doReturn(context).when(mPreferenceManager).getContext(); doReturn(true).when(mFragment).shouldAddPreference(any(AppEntry.class)); doNothing().when(mFragment).setLoading(anyBoolean(), anyBoolean()); doReturn(mPreferenceScreen).when(mFragment).getPreferenceScreen(); doReturn(mPreferenceManager).when(mFragment).getPreferenceManager(); ReflectionHelpers.setField(mFragment, "mDataSaverBackend", mDataSaverBackend); final String testPkg1 = "com.example.one"; final String testPkg2 = "com.example.two"; ShadowRestrictedLockUtils.setRestrictedPkgs(testPkg2); doAnswer((invocation) -> { final AccessPreference preference = invocation.getArgument(0); final AppEntry entry = preference.getEntryForTest(); // Verify preference is disabled by admin and the summary is changed accordingly. if (testPkg1.equals(entry.info.packageName)) { assertThat(preference.isDisabledByAdmin()).isFalse(); assertThat(preference.getSummary()).isEqualTo(""); } else if (testPkg2.equals(entry.info.packageName)) { assertThat(preference.isDisabledByAdmin()).isTrue(); assertThat(preference.getSummary()).isEqualTo( context.getString(R.string.disabled_by_admin)); } assertThat(preference.isChecked()).isFalse(); preference.performClick(); // Verify that when the preference is clicked, support details intent is launched // if the preference is disabled by admin, otherwise the switch is toggled. if (testPkg1.equals(entry.info.packageName)) { assertThat(preference.isChecked()).isTrue(); assertThat(ShadowRestrictedLockUtils.hasAdminSupportDetailsIntentLaunched()) .isFalse(); } else if (testPkg2.equals(entry.info.packageName)) { assertThat(preference.isChecked()).isFalse(); assertThat(ShadowRestrictedLockUtils.hasAdminSupportDetailsIntentLaunched()) .isTrue(); } ShadowRestrictedLockUtils.clearAdminSupportDetailsIntentLaunch(); return null; }).when(mPreferenceScreen).addPreference(any(AccessPreference.class)); mFragment.onRebuildComplete(createAppEntries(testPkg1, testPkg2)); } private ArrayList<AppEntry> createAppEntries(String... packageNames) { final ArrayList<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 AppEntry appEntry = spy(new AppEntry(RuntimeEnvironment.application, info, i)); appEntry.extraInfo = new DataUsageState(false, false); doNothing().when(appEntry).ensureLabel(any(Context.class)); ReflectionHelpers.setField(appEntry, "info", info); appEntries.add(appEntry); } return appEntries; } }