diff --git a/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java b/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java new file mode 100644 index 00000000000..be72e56049f --- /dev/null +++ b/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.fuelgauge.datasaver; + +import android.content.Context; +import android.content.SharedPreferences; +import android.net.NetworkPolicyManager; + +import androidx.annotation.VisibleForTesting; + +/** A class to dynamically manage per apps {@link NetworkPolicyManager} POLICY_ flags. */ +public final class DynamicDenylistManager { + + private static final String TAG = "DynamicDenylistManager"; + private static final String PREF_KEY_MANUAL_DENY = "manual_denylist_preference"; + private static final String PREF_KEY_DYNAMIC_DENY = "dynamic_denylist_preference"; + + private final Context mContext; + private final NetworkPolicyManager mNetworkPolicyManager; + + private static DynamicDenylistManager sInstance; + + /** @return a DynamicDenylistManager object */ + public static DynamicDenylistManager getInstance(Context context) { + synchronized (DynamicDenylistManager.class) { + if (sInstance == null) { + sInstance = new DynamicDenylistManager(context); + } + return sInstance; + } + } + + DynamicDenylistManager(Context context) { + mContext = context.getApplicationContext(); + mNetworkPolicyManager = NetworkPolicyManager.from(mContext); + } + + /** 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(); + } + } + + /** Return true if the target uid is in {@link #getManualDenylistPref()}. */ + public boolean isInManualDenylist(String uid) { + return getManualDenylistPref().contains(uid); + } + + /** Clear all data in {@link #getManualDenylistPref()} */ + public void clearManualDenylistPref() { + getManualDenylistPref().edit().clear().apply(); + } + + /** Clear all data in {@link #getDynamicDenylistPref()} */ + public void clearDynamicDenylistPref() { + getDynamicDenylistPref().edit().clear().apply(); + } + + @VisibleForTesting + SharedPreferences getManualDenylistPref() { + return mContext.getSharedPreferences(PREF_KEY_MANUAL_DENY, Context.MODE_PRIVATE); + } + + @VisibleForTesting + SharedPreferences getDynamicDenylistPref() { + return mContext.getSharedPreferences(PREF_KEY_DYNAMIC_DENY, Context.MODE_PRIVATE); + } +} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManagerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManagerTest.java new file mode 100644 index 00000000000..cdf15146a21 --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManagerTest.java @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +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.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.Context; +import android.content.SharedPreferences; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@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 SharedPreferences mManualDenyListPref; + private SharedPreferences mDynamicDenyListPref; + private DynamicDenylistManager mDynamicDenylistManager; + private Context mContext; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application.getApplicationContext(); + mDynamicDenylistManager = new DynamicDenylistManager(mContext); + mManualDenyListPref = mDynamicDenylistManager.getManualDenylistPref(); + mDynamicDenyListPref = mDynamicDenylistManager.getDynamicDenylistPref(); + } + + @After + public void tearDown() { + mDynamicDenylistManager.clearManualDenylistPref(); + mDynamicDenylistManager.clearDynamicDenylistPref(); + } + + @Test + public void getManualDenylistPref_isEmpty() { + assertThat(mManualDenyListPref.getAll()).isEmpty(); + } + + @Test + public void getDynamicDenylistPref_isEmpty() { + assertThat(mDynamicDenyListPref.getAll()).isEmpty(); + } + + @Test + public void getManualDenylistPref_initiated_containsExpectedValue() { + mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply(); + + 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(); + + 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); + + 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(); + assertTrue(mManualDenyListPref.contains(FAKE_UID_1)); + + mDynamicDenylistManager.updateManualDenylist(FAKE_UID_1, POLICY_NONE); + + assertThat(mManualDenyListPref.getAll()).isEmpty(); + } + + @Test + public void updateManualDenylist_samePolicy_doNothing() { + mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply(); + assertTrue(mManualDenyListPref.contains(FAKE_UID_1)); + + mDynamicDenylistManager.updateManualDenylist(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND); + + assertThat(mManualDenyListPref.getAll()).hasSize(1); + } + + @Test + public void isManualDenylist_returnsFalse() { + assertFalse(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1)); + } + + @Test + public void isManualDenylist_incorrectUid_returnsFalse() { + mManualDenyListPref.edit().putInt(FAKE_UID_2, POLICY_REJECT_METERED_BACKGROUND).apply(); + + assertFalse(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1)); + } + + @Test + public void isManualDenylist_initiated_returnsTrue() { + mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply(); + + assertTrue(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1)); + } + + @Test + public void clearManualDenylistPref_isEmpty() { + mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply(); + assertThat(mManualDenyListPref.getAll()).hasSize(1); + assertTrue(mManualDenyListPref.contains(FAKE_UID_1)); + + mDynamicDenylistManager.clearManualDenylistPref(); + + assertThat(mManualDenyListPref.getAll()).isEmpty(); + } + + @Test + public void clearDynamicDenylistPref_isEmpty() { + mDynamicDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply(); + assertThat(mDynamicDenyListPref.getAll()).hasSize(1); + assertTrue(mDynamicDenyListPref.contains(FAKE_UID_1)); + + mDynamicDenylistManager.clearDynamicDenylistPref(); + + assertThat(mDynamicDenyListPref.getAll()).isEmpty(); + } +}