Add a skeleton for dynamic deny list class

 - Create a class with default sharedpreference management

Bug: 306329984
Test: make SettingsRoboTests
Change-Id: I06cf7c5ea82a44a61b05b7bbcddb6040cc917ec5
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 0000000..be72e56
--- /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 0000000..cdf1514
--- /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();
+    }
+}