diff --git a/src/com/android/settings/applications/AppStorageSettings.java b/src/com/android/settings/applications/AppStorageSettings.java index 807f0432990..e45657fc067 100644 --- a/src/com/android/settings/applications/AppStorageSettings.java +++ b/src/com/android/settings/applications/AppStorageSettings.java @@ -53,6 +53,7 @@ import androidx.preference.PreferenceCategory; import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.deviceinfo.StorageWizardMoveConfirm; +import com.android.settings.fuelgauge.datasaver.DynamicDenylistManager; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.applications.AppUtils; import com.android.settingslib.applications.ApplicationsState.Callbacks; @@ -359,6 +360,8 @@ public class AppStorageSettings extends AppInfoWithHeader mButtonsPref.setButton1Enabled(false); // Invoke uninstall or clear user data based on sysPackage String packageName = mAppEntry.info.packageName; + DynamicDenylistManager.getInstance(getContext()) + .resetDenylistIfNeeded(packageName, /* force= */ false); Log.i(TAG, "Clearing user data for package : " + packageName); if (mClearDataObserver == null) { mClearDataObserver = new ClearUserDataObserver(); diff --git a/src/com/android/settings/applications/manageapplications/ResetAppsHelper.java b/src/com/android/settings/applications/manageapplications/ResetAppsHelper.java index 6da3e529065..b2b7512d560 100644 --- a/src/com/android/settings/applications/manageapplications/ResetAppsHelper.java +++ b/src/com/android/settings/applications/manageapplications/ResetAppsHelper.java @@ -39,6 +39,7 @@ import androidx.appcompat.app.AlertDialog; import com.android.settings.R; import com.android.settings.fuelgauge.BatteryOptimizeUtils; +import com.android.settings.fuelgauge.datasaver.DynamicDenylistManager; import java.util.Arrays; import java.util.List; @@ -155,6 +156,8 @@ public class ResetAppsHelper implements DialogInterface.OnClickListener, } mAom.resetAllModes(); BatteryOptimizeUtils.resetAppOptimizationMode(mContext, mIPm, mAom); + DynamicDenylistManager.getInstance(mContext) + .resetDenylistIfNeeded(/* packageName= */ null, /* force= */ true); final int[] restrictedUids = mNpm.getUidsWithPolicy(POLICY_REJECT_METERED_BACKGROUND); final int currentUserId = ActivityManager.getCurrentUser(); for (int uid : restrictedUids) { diff --git a/src/com/android/settings/datausage/AppDataUsage.java b/src/com/android/settings/datausage/AppDataUsage.java index 38f09f46159..fb28d6850c6 100644 --- a/src/com/android/settings/datausage/AppDataUsage.java +++ b/src/com/android/settings/datausage/AppDataUsage.java @@ -44,6 +44,7 @@ import com.android.settings.applications.AppInfoBase; import com.android.settings.datausage.lib.AppDataUsageDetailsRepository; import com.android.settings.datausage.lib.NetworkTemplates; import com.android.settings.datausage.lib.NetworkUsageDetailsData; +import com.android.settings.fuelgauge.datasaver.DynamicDenylistManager; import com.android.settings.network.SubscriptionUtil; import com.android.settings.widget.EntityHeaderController; import com.android.settingslib.AppItem; @@ -325,7 +326,8 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC private boolean getAppRestrictBackground() { final int uid = mAppItem.key; final int uidPolicy = services.mPolicyManager.getUidPolicy(uid); - return (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0; + return (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0 + && DynamicDenylistManager.getInstance(mContext).isInManualDenylist(uid); } private boolean getUnrestrictData() { diff --git a/src/com/android/settings/datausage/DataSaverBackend.java b/src/com/android/settings/datausage/DataSaverBackend.java index b4b6b8c886f..6e994537aa8 100644 --- a/src/com/android/settings/datausage/DataSaverBackend.java +++ b/src/com/android/settings/datausage/DataSaverBackend.java @@ -23,6 +23,7 @@ import android.content.Context; import android.net.NetworkPolicyManager; import android.util.SparseIntArray; +import com.android.settings.fuelgauge.datasaver.DynamicDenylistManager; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.utils.ThreadUtils; @@ -39,6 +40,7 @@ public class DataSaverBackend { private final MetricsFeatureProvider mMetricsFeatureProvider; private final NetworkPolicyManager mPolicyManager; + private final DynamicDenylistManager mDynamicDenylistManager; private final ArrayList mListeners = new ArrayList<>(); private SparseIntArray mUidPolicies = new SparseIntArray(); private boolean mAllowlistInitialized; @@ -50,6 +52,7 @@ public class DataSaverBackend { mContext = context.getApplicationContext(); mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(); mPolicyManager = NetworkPolicyManager.from(mContext); + mDynamicDenylistManager = DynamicDenylistManager.getInstance(mContext); } public void addListener(Listener listener) { @@ -83,7 +86,7 @@ public class DataSaverBackend { public void setIsAllowlisted(int uid, String packageName, boolean allowlisted) { final int policy = allowlisted ? POLICY_ALLOW_METERED_BACKGROUND : POLICY_NONE; - mPolicyManager.setUidPolicy(uid, policy); + mDynamicDenylistManager.setUidPolicyLocked(uid, policy); mUidPolicies.put(uid, policy); if (allowlisted) { mMetricsFeatureProvider.action( @@ -113,7 +116,7 @@ public class DataSaverBackend { public void setIsDenylisted(int uid, String packageName, boolean denylisted) { final int policy = denylisted ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE; - mPolicyManager.setUidPolicy(uid, policy); + mDynamicDenylistManager.setUidPolicyLocked(uid, policy); mUidPolicies.put(uid, policy); if (denylisted) { mMetricsFeatureProvider.action( @@ -123,7 +126,8 @@ public class DataSaverBackend { public boolean isDenylisted(int uid) { loadDenylist(); - return mUidPolicies.get(uid, POLICY_NONE) == POLICY_REJECT_METERED_BACKGROUND; + return mUidPolicies.get(uid, POLICY_NONE) == POLICY_REJECT_METERED_BACKGROUND + && mDynamicDenylistManager.isInManualDenylist(uid); } private void loadDenylist() { diff --git a/src/com/android/settings/fuelgauge/BatterySettingsMigrateChecker.java b/src/com/android/settings/fuelgauge/BatterySettingsMigrateChecker.java index 5d9d047eaec..dd49c8b2926 100644 --- a/src/com/android/settings/fuelgauge/BatterySettingsMigrateChecker.java +++ b/src/com/android/settings/fuelgauge/BatterySettingsMigrateChecker.java @@ -26,6 +26,7 @@ import android.util.Log; import androidx.annotation.VisibleForTesting; import com.android.settings.fuelgauge.batterysaver.BatterySaverScheduleRadioButtonsController; +import com.android.settings.fuelgauge.datasaver.DynamicDenylistManager; import com.android.settingslib.fuelgauge.BatterySaverUtils; import java.util.List; @@ -50,6 +51,8 @@ public final class BatterySettingsMigrateChecker extends BroadcastReceiver { context = context.getApplicationContext(); verifySaverConfiguration(context); verifyBatteryOptimizeModes(context); + // Initialize and sync settings into SharedPreferences for migration. + DynamicDenylistManager.getInstance(context); } /** Avoid users set important apps into the unexpected battery optimize modes */ diff --git a/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java b/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java index be72e56049f..7eae7eba6f1 100644 --- a/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java +++ b/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java @@ -16,12 +16,24 @@ package com.android.settings.fuelgauge.datasaver; +import static android.net.NetworkPolicyManager.POLICY_NONE; +import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND; + +import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME; +import static com.android.settings.fuelgauge.BatteryUtils.UID_ZERO; + import android.content.Context; import android.content.SharedPreferences; +import android.content.pm.PackageManager; import android.net.NetworkPolicyManager; +import android.util.ArraySet; +import android.util.Log; import androidx.annotation.VisibleForTesting; +import java.util.List; +import java.util.Set; + /** A class to dynamically manage per apps {@link NetworkPolicyManager} POLICY_ flags. */ public final class DynamicDenylistManager { @@ -29,47 +41,157 @@ public final class DynamicDenylistManager { private static final String PREF_KEY_MANUAL_DENY = "manual_denylist_preference"; private static final String PREF_KEY_DYNAMIC_DENY = "dynamic_denylist_preference"; + private static DynamicDenylistManager sInstance; + private final Context mContext; private final NetworkPolicyManager mNetworkPolicyManager; + private final Object mLock = new Object(); - private static DynamicDenylistManager sInstance; + @VisibleForTesting + static final String PREF_KEY_MANUAL_DENYLIST_SYNCED = "manual_denylist_synced"; /** @return a DynamicDenylistManager object */ public static DynamicDenylistManager getInstance(Context context) { synchronized (DynamicDenylistManager.class) { if (sInstance == null) { - sInstance = new DynamicDenylistManager(context); + sInstance = new DynamicDenylistManager( + context, NetworkPolicyManager.from(context)); } return sInstance; } } - DynamicDenylistManager(Context context) { + @VisibleForTesting + DynamicDenylistManager(Context context, NetworkPolicyManager networkPolicyManager) { mContext = context.getApplicationContext(); - mNetworkPolicyManager = NetworkPolicyManager.from(mContext); + mNetworkPolicyManager = networkPolicyManager; + syncPolicyIfNeeded(); } - /** Update the target uid policy in {@link #getManualDenylistPref()}. */ - public void updateManualDenylist(String uid, int policy) { - if (policy != NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND) { - getManualDenylistPref().edit().remove(uid).apply(); - } else { - getManualDenylistPref().edit().putInt(uid, policy).apply(); + /** Sync the policy from {@link NetworkPolicyManager} if needed. */ + private void syncPolicyIfNeeded() { + if (getManualDenylistPref().contains(PREF_KEY_MANUAL_DENYLIST_SYNCED)) { + Log.i(TAG, "syncPolicyIfNeeded() ignore synced manual denylist"); + return; + } + + final SharedPreferences.Editor editor = getManualDenylistPref().edit(); + final int[] existedUids = mNetworkPolicyManager + .getUidsWithPolicy(POLICY_REJECT_METERED_BACKGROUND); + if (existedUids != null && existedUids.length != 0) { + for (int uid : existedUids) { + editor.putInt(String.valueOf(uid), POLICY_REJECT_METERED_BACKGROUND); + } + } + editor.putInt(PREF_KEY_MANUAL_DENYLIST_SYNCED, POLICY_NONE).apply(); + } + + /** Set policy flags for specific UID. */ + public void setUidPolicyLocked(int uid, int policy) { + synchronized (mLock) { + mNetworkPolicyManager.setUidPolicy(uid, policy); + } + updateDenylistPref(uid, policy); + } + + /** Suggest a list of package to set as POLICY_REJECT. */ + public void setDenylist(List packageNameList) { + final Set denylistTargetUids = new ArraySet<>(packageNameList.size()); + for (String packageName : packageNameList) { + try { + final int uid = mContext.getPackageManager().getPackageUid(packageName, 0); + if (uid == UID_ZERO) { + continue; + } + denylistTargetUids.add(uid); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Unknown package name: " + packageName, e); + } + } + + final Set manualDenylistUids = getDenylistAllUids(getManualDenylistPref()); + denylistTargetUids.removeAll(manualDenylistUids); + + final Set lastDynamicDenylistUids = getDenylistAllUids(getDynamicDenylistPref()); + if (lastDynamicDenylistUids.equals(denylistTargetUids)) { + Log.i(TAG, "setDenylist() ignore the same denylist with size: " + + lastDynamicDenylistUids.size()); + return; + } + + // Store target denied uids into DynamicDenylistPref. + final SharedPreferences.Editor editor = getDynamicDenylistPref().edit(); + editor.clear(); + denylistTargetUids.forEach( + uid -> editor.putInt(String.valueOf(uid), POLICY_REJECT_METERED_BACKGROUND)); + editor.apply(); + + // Set new added UIDs into REJECT policy. + synchronized (mLock) { + for (int uid : denylistTargetUids) { + if (!lastDynamicDenylistUids.contains(uid)) { + mNetworkPolicyManager.setUidPolicy(uid, POLICY_REJECT_METERED_BACKGROUND); + } + } + } + // Unset removed UIDs back to NONE policy. + synchronized (mLock) { + for (int uid : lastDynamicDenylistUids) { + if (!denylistTargetUids.contains(uid)) { + mNetworkPolicyManager.setUidPolicy(uid, POLICY_NONE); + } + } } } /** Return true if the target uid is in {@link #getManualDenylistPref()}. */ - public boolean isInManualDenylist(String uid) { - return getManualDenylistPref().contains(uid); + public boolean isInManualDenylist(int uid) { + return getManualDenylistPref().contains(String.valueOf(uid)); } - /** Clear all data in {@link #getManualDenylistPref()} */ - public void clearManualDenylistPref() { + /** Reset the UIDs in the denylist if needed. */ + public void resetDenylistIfNeeded(String packageName, boolean force) { + if (!force && !SETTINGS_PACKAGE_NAME.equals(packageName)) { + return; + } + synchronized (mLock) { + for (int uid : mNetworkPolicyManager + .getUidsWithPolicy(POLICY_REJECT_METERED_BACKGROUND)) { + if (!getDenylistAllUids(getManualDenylistPref()).contains(uid)) { + mNetworkPolicyManager.setUidPolicy(uid, POLICY_NONE); + } + } + } + clearSharedPreferences(); + } + + private Set getDenylistAllUids(SharedPreferences sharedPreferences) { + final ArraySet uids = new ArraySet<>(); + for (String key : sharedPreferences.getAll().keySet()) { + if (PREF_KEY_MANUAL_DENYLIST_SYNCED.equals(key)) { + continue; + } + try { + uids.add(Integer.parseInt(key)); + } catch (NumberFormatException e) { + Log.e(TAG, "getDenylistAllUids() unexpected format for " + key); + } + } + return uids; + } + + void updateDenylistPref(int uid, int policy) { + final String uidString = String.valueOf(uid); + if (policy != POLICY_REJECT_METERED_BACKGROUND) { + getManualDenylistPref().edit().remove(uidString).apply(); + } else { + getManualDenylistPref().edit().putInt(uidString, policy).apply(); + } + getDynamicDenylistPref().edit().remove(uidString).apply(); + } + + void clearSharedPreferences() { getManualDenylistPref().edit().clear().apply(); - } - - /** Clear all data in {@link #getDynamicDenylistPref()} */ - public void clearDynamicDenylistPref() { getDynamicDenylistPref().edit().clear().apply(); } diff --git a/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java b/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java index 7b7c7a64f3f..1d841fa2505 100644 --- a/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java +++ b/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java @@ -239,6 +239,7 @@ public class AppDataUsageTest { ReflectionHelpers.setField(mFragment, "mUnrestrictedData", unrestrictedDataPref); ReflectionHelpers.setField(mFragment, "mDataSaverBackend", dataSaverBackend); ReflectionHelpers.setField(mFragment.services, "mPolicyManager", networkPolicyManager); + ReflectionHelpers.setField(mFragment, "mContext", RuntimeEnvironment.application); when(mFragment.getListView()).thenReturn(mock(RecyclerView.class)); ShadowRestrictedLockUtilsInternal.setRestricted(true); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManagerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManagerTest.java index cdf15146a21..bfa7cfae180 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManagerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManagerTest.java @@ -19,138 +19,373 @@ package com.android.settings.fuelgauge.datasaver; import static android.net.NetworkPolicyManager.POLICY_NONE; import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND; +import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME; +import static com.android.settings.fuelgauge.datasaver.DynamicDenylistManager.PREF_KEY_MANUAL_DENYLIST_SYNCED; + import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.content.Context; import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.net.NetworkPolicyManager; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import java.util.Collections; +import java.util.List; + @RunWith(RobolectricTestRunner.class) public class DynamicDenylistManagerTest { - private static final String FAKE_UID_1 = "package_uid_1"; - private static final String FAKE_UID_2 = "package_uid_2"; + private static final int[] EMPTY_ARRAY = new int[]{}; + private static final String FAKE_UID_1 = "1001"; + private static final String FAKE_UID_2 = "1002"; + private static final int FAKE_UID_1_INT = Integer.parseInt(FAKE_UID_1); + private static final int FAKE_UID_2_INT = Integer.parseInt(FAKE_UID_2); private SharedPreferences mManualDenyListPref; private SharedPreferences mDynamicDenyListPref; private DynamicDenylistManager mDynamicDenylistManager; - private Context mContext; + + @Mock + private NetworkPolicyManager mNetworkPolicyManager; + @Mock + private PackageManager mPackageManager; @Before public void setUp() { - mContext = RuntimeEnvironment.application.getApplicationContext(); - mDynamicDenylistManager = new DynamicDenylistManager(mContext); - mManualDenyListPref = mDynamicDenylistManager.getManualDenylistPref(); - mDynamicDenyListPref = mDynamicDenylistManager.getDynamicDenylistPref(); + MockitoAnnotations.initMocks(this); } @After public void tearDown() { - mDynamicDenylistManager.clearManualDenylistPref(); - mDynamicDenylistManager.clearDynamicDenylistPref(); + mDynamicDenylistManager.clearSharedPreferences(); } @Test - public void getManualDenylistPref_isEmpty() { - assertThat(mManualDenyListPref.getAll()).isEmpty(); + public void init_withoutExistedRejectPolicy_createWithExpectedValue() { + initDynamicDenylistManager(EMPTY_ARRAY); + + assertThat(mManualDenyListPref.getAll()).hasSize(1); + assertTrue(mManualDenyListPref.contains(PREF_KEY_MANUAL_DENYLIST_SYNCED)); } @Test - public void getDynamicDenylistPref_isEmpty() { - assertThat(mDynamicDenyListPref.getAll()).isEmpty(); + public void init_withExistedRejectPolicy_createWithExpectedValue() { + initDynamicDenylistManager(new int[]{FAKE_UID_1_INT, FAKE_UID_2_INT}); + + assertThat(mManualDenyListPref.getAll()).hasSize(3); + assertTrue(mManualDenyListPref.contains(PREF_KEY_MANUAL_DENYLIST_SYNCED)); + assertTrue(mManualDenyListPref.contains(FAKE_UID_1)); + assertTrue(mManualDenyListPref.contains(FAKE_UID_2)); } @Test public void getManualDenylistPref_initiated_containsExpectedValue() { - mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply(); + initDynamicDenylistManager(EMPTY_ARRAY); + + setupPreference(mManualDenyListPref, FAKE_UID_1); - assertThat(mManualDenyListPref.getAll().size()).isEqualTo(1); assertTrue(mManualDenyListPref.contains(FAKE_UID_1)); } @Test public void getDynamicDenylistPref_initiated_containsExpectedValue() { - mDynamicDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply(); + initDynamicDenylistManager(EMPTY_ARRAY); + + setupPreference(mDynamicDenyListPref, FAKE_UID_1); - assertThat(mDynamicDenyListPref.getAll()).hasSize(1); assertTrue(mDynamicDenyListPref.contains(FAKE_UID_1)); } @Test public void updateManualDenylist_policyReject_addsUid() { - mDynamicDenylistManager.updateManualDenylist(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND); + initDynamicDenylistManager(EMPTY_ARRAY); + + mDynamicDenylistManager.updateDenylistPref(FAKE_UID_1_INT, + POLICY_REJECT_METERED_BACKGROUND); - assertThat(mManualDenyListPref.getAll()).hasSize(1); assertTrue(mManualDenyListPref.contains(FAKE_UID_1)); } @Test public void updateManualDenylist_policyNone_removesUid() { - mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply(); + initDynamicDenylistManager(EMPTY_ARRAY); + setupPreference(mManualDenyListPref, FAKE_UID_1); assertTrue(mManualDenyListPref.contains(FAKE_UID_1)); - mDynamicDenylistManager.updateManualDenylist(FAKE_UID_1, POLICY_NONE); + mDynamicDenylistManager.updateDenylistPref(FAKE_UID_1_INT, POLICY_NONE); - assertThat(mManualDenyListPref.getAll()).isEmpty(); + assertFalse(mManualDenyListPref.contains(FAKE_UID_1)); } @Test public void updateManualDenylist_samePolicy_doNothing() { - mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply(); + initDynamicDenylistManager(EMPTY_ARRAY); + setupPreference(mManualDenyListPref, FAKE_UID_1); assertTrue(mManualDenyListPref.contains(FAKE_UID_1)); + assertThat(mManualDenyListPref.getAll()).hasSize(2); - mDynamicDenylistManager.updateManualDenylist(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND); + mDynamicDenylistManager.updateDenylistPref(FAKE_UID_1_INT, + POLICY_REJECT_METERED_BACKGROUND); - assertThat(mManualDenyListPref.getAll()).hasSize(1); + assertThat(mManualDenyListPref.getAll()).hasSize(2); } @Test - public void isManualDenylist_returnsFalse() { - assertFalse(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1)); + public void setUidPolicyLocked_invokeSetUidPolicy() { + initDynamicDenylistManager(EMPTY_ARRAY); + + mDynamicDenylistManager.setUidPolicyLocked(FAKE_UID_1_INT, + POLICY_REJECT_METERED_BACKGROUND); + + assertTrue(mManualDenyListPref.contains(FAKE_UID_1)); + verify(mNetworkPolicyManager).setUidPolicy(eq(FAKE_UID_1_INT), + eq(POLICY_REJECT_METERED_BACKGROUND)); } @Test - public void isManualDenylist_incorrectUid_returnsFalse() { + public void setDenylist_emptyListAndNoData_doNothing() { + initDynamicDenylistManager(EMPTY_ARRAY); + + mDynamicDenylistManager.setDenylist(Collections.emptyList()); + + verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), anyInt()); + } + + @Test + public void setDenylist_uidDeniedAlready_doNothing() + throws PackageManager.NameNotFoundException { + when(mPackageManager.getPackageUid(anyString(), eq(0))).thenReturn(FAKE_UID_1_INT); + initDynamicDenylistManager(new int[]{FAKE_UID_1_INT}); + + mDynamicDenylistManager.setDenylist(List.of(FAKE_UID_1)); + + verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), anyInt()); + } + + @Test + public void setDenylist_sameList_doNothing() throws PackageManager.NameNotFoundException { + when(mPackageManager.getPackageUid(eq(FAKE_UID_1), eq(0))).thenReturn(FAKE_UID_1_INT); + when(mPackageManager.getPackageUid(eq(FAKE_UID_2), eq(0))).thenReturn(FAKE_UID_2_INT); + initDynamicDenylistManager(EMPTY_ARRAY); + setupPreference(mDynamicDenyListPref, FAKE_UID_2, FAKE_UID_1); + + mDynamicDenylistManager.setDenylist(List.of(FAKE_UID_1, FAKE_UID_2)); + + verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), anyInt()); + } + + @Test + public void setDenylist_newListWithOldData_modifyPolicyNoneAndReject() + throws PackageManager.NameNotFoundException { + when(mPackageManager.getPackageUid(anyString(), eq(0))).thenReturn( + Integer.parseInt(FAKE_UID_1)); + initDynamicDenylistManager(EMPTY_ARRAY); + setupPreference(mDynamicDenyListPref, FAKE_UID_2); + + mDynamicDenylistManager.setDenylist(List.of(FAKE_UID_1)); + + verify(mNetworkPolicyManager).setUidPolicy(FAKE_UID_2_INT, POLICY_NONE); + verify(mNetworkPolicyManager).setUidPolicy(FAKE_UID_1_INT, + POLICY_REJECT_METERED_BACKGROUND); + assertThat(mDynamicDenyListPref.getAll()).hasSize(1); + assertTrue(mDynamicDenyListPref.contains(FAKE_UID_1)); + } + + @Test + public void setDenylist_newListWithoutOldData_modifyPolicyReject() + throws PackageManager.NameNotFoundException { + when(mPackageManager.getPackageUid(anyString(), eq(0))).thenReturn( + Integer.parseInt(FAKE_UID_1)); + initDynamicDenylistManager(EMPTY_ARRAY); + + mDynamicDenylistManager.setDenylist(List.of(FAKE_UID_1)); + + verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), eq(POLICY_NONE)); + verify(mNetworkPolicyManager).setUidPolicy(FAKE_UID_1_INT, + POLICY_REJECT_METERED_BACKGROUND); + assertThat(mDynamicDenyListPref.getAll()).hasSize(1); + assertTrue(mDynamicDenyListPref.contains(FAKE_UID_1)); + } + + @Test + public void setDenylist_emptyListWithOldData_modifyPolicyNone() { + initDynamicDenylistManager(EMPTY_ARRAY); + setupPreference(mDynamicDenyListPref, FAKE_UID_2); + + mDynamicDenylistManager.setDenylist(Collections.emptyList()); + + verify(mNetworkPolicyManager).setUidPolicy(FAKE_UID_2_INT, POLICY_NONE); + verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), + eq(POLICY_REJECT_METERED_BACKGROUND)); + assertThat(mDynamicDenyListPref.getAll()).isEmpty(); + } + + @Test + public void isInManualDenylist_returnsFalse() { + initDynamicDenylistManager(EMPTY_ARRAY); + + assertFalse(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1_INT)); + } + + @Test + public void isInManualDenylist_incorrectUid_returnsFalse() { + initDynamicDenylistManager(EMPTY_ARRAY); + mManualDenyListPref.edit().putInt(FAKE_UID_2, POLICY_REJECT_METERED_BACKGROUND).apply(); - assertFalse(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1)); + assertFalse(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1_INT)); } @Test - public void isManualDenylist_initiated_returnsTrue() { + public void isInManualDenylist_initiated_returnsTrue() { + initDynamicDenylistManager(EMPTY_ARRAY); + mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply(); - assertTrue(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1)); + assertTrue(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1_INT)); } @Test - public void clearManualDenylistPref_isEmpty() { - mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply(); + public void resetDenylistIfNeeded_nullPackageName_doNothing() { + initDynamicDenylistManager(new int[0], new int[]{FAKE_UID_1_INT, FAKE_UID_2_INT}); + + mDynamicDenylistManager.resetDenylistIfNeeded(null, false); + assertThat(mManualDenyListPref.getAll()).hasSize(1); - assertTrue(mManualDenyListPref.contains(FAKE_UID_1)); + verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), eq(POLICY_NONE)); + } - mDynamicDenylistManager.clearManualDenylistPref(); + @Test + public void resetDenylistIfNeeded_invalidPackageName_doNothing() { + initDynamicDenylistManager(new int[0], new int[]{FAKE_UID_1_INT, FAKE_UID_2_INT}); + + mDynamicDenylistManager.resetDenylistIfNeeded("invalid_package_name", false); + + assertThat(mManualDenyListPref.getAll()).hasSize(1); + verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), eq(POLICY_NONE)); + } + + @Test + public void resetDenylistIfNeeded_denylistUnchanged_doNothingWithPolicy() { + initDynamicDenylistManager(new int[]{FAKE_UID_1_INT, FAKE_UID_2_INT}); + + mDynamicDenylistManager.resetDenylistIfNeeded(SETTINGS_PACKAGE_NAME, false); + + verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), eq(POLICY_NONE)); + } + + @Test + public void resetDenylistIfNeeded_denylistChanged_resetAndClear() { + initDynamicDenylistManager(new int[0], new int[]{FAKE_UID_1_INT, FAKE_UID_2_INT}); + + mDynamicDenylistManager.resetDenylistIfNeeded(SETTINGS_PACKAGE_NAME, false); + + assertThat(mManualDenyListPref.getAll()).isEmpty(); + verify(mNetworkPolicyManager, times(2)).setUidPolicy(anyInt(), eq(POLICY_NONE)); + } + + @Test + public void resetDenylistIfNeeded_forceResetWithNullPackageName_resetAndClear() { + initDynamicDenylistManager(new int[0], new int[]{FAKE_UID_2_INT}); + + mDynamicDenylistManager.resetDenylistIfNeeded(null, true); + + assertThat(mManualDenyListPref.getAll()).isEmpty(); + verify(mNetworkPolicyManager).setUidPolicy(eq(FAKE_UID_2_INT), eq(POLICY_NONE)); + } + + @Test// 4 + public void resetDenylistIfNeeded_forceResetWithInvalidPackageName_resetAndClear() { + initDynamicDenylistManager(new int[0], new int[]{FAKE_UID_1_INT, FAKE_UID_2_INT}); + + mDynamicDenylistManager.resetDenylistIfNeeded("invalid_package_name", true); + + assertThat(mManualDenyListPref.getAll()).isEmpty(); + verify(mNetworkPolicyManager, times(2)).setUidPolicy(anyInt(), eq(POLICY_NONE)); + } + + @Test + public void resetDenylistIfNeeded_forceResetButDenylistUnchanged_doNothingWithPolicy() { + initDynamicDenylistManager(new int[]{FAKE_UID_1_INT}); + + mDynamicDenylistManager.resetDenylistIfNeeded(SETTINGS_PACKAGE_NAME, true); + + assertThat(mManualDenyListPref.getAll()).isEmpty(); + verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), eq(POLICY_NONE)); + } + + @Test + public void resetDenylistIfNeeded_forceResetWithDenylistChanged_resetAndClear() { + initDynamicDenylistManager(new int[0], new int[]{FAKE_UID_1_INT, FAKE_UID_2_INT}); + + mDynamicDenylistManager.resetDenylistIfNeeded(SETTINGS_PACKAGE_NAME, true); + + assertThat(mManualDenyListPref.getAll()).isEmpty(); + verify(mNetworkPolicyManager, times(2)).setUidPolicy(anyInt(), eq(POLICY_NONE)); + } + + @Test + public void clearSharedPreferences_manualDenyListPrefIsEmpty() { + initDynamicDenylistManager(EMPTY_ARRAY); + mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply(); + assertThat(mManualDenyListPref.getAll()).hasSize(2); + assertTrue(mManualDenyListPref.contains(FAKE_UID_1)); + assertTrue(mManualDenyListPref.contains(PREF_KEY_MANUAL_DENYLIST_SYNCED)); + + mDynamicDenylistManager.clearSharedPreferences(); assertThat(mManualDenyListPref.getAll()).isEmpty(); } @Test - public void clearDynamicDenylistPref_isEmpty() { + public void clearSharedPreferences_dynamicDenyListPrefIsEmpty() { + initDynamicDenylistManager(EMPTY_ARRAY); mDynamicDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply(); assertThat(mDynamicDenyListPref.getAll()).hasSize(1); assertTrue(mDynamicDenyListPref.contains(FAKE_UID_1)); - mDynamicDenylistManager.clearDynamicDenylistPref(); + mDynamicDenylistManager.clearSharedPreferences(); assertThat(mDynamicDenyListPref.getAll()).isEmpty(); } + + private void initDynamicDenylistManager(int[] preload) { + initDynamicDenylistManager(preload, preload); + } + private void initDynamicDenylistManager(int[] preload1, int[] preload2) { + final Context context = spy(RuntimeEnvironment.application.getApplicationContext()); + when(context.getApplicationContext()).thenReturn(context); + when(context.getPackageManager()).thenReturn(mPackageManager); + when(mNetworkPolicyManager.getUidsWithPolicy(anyInt())) + .thenReturn(preload1).thenReturn(preload2); + mDynamicDenylistManager = new DynamicDenylistManager(context, mNetworkPolicyManager); + mManualDenyListPref = mDynamicDenylistManager.getManualDenylistPref(); + mDynamicDenyListPref = mDynamicDenylistManager.getDynamicDenylistPref(); + } + + private void setupPreference(SharedPreferences sharedPreferences, String... uids) { + for (String uid : uids) { + sharedPreferences.edit().putInt(uid, POLICY_REJECT_METERED_BACKGROUND).apply(); + } + } }