[BR06] Support check whether network is blocked by data saver

This change adds a DataSaverStatusTracker, which is a helper
class to continuously track data saver status through NPMS
public API and intents. ConnectivityManager#isUidNetworkingBlocked
would use this cached information along with bpf maps to decide
whether networking of an uid is blocked.

Test: atest FrameworksNetTests:android.net.connectivity.android.net.BpfNetMapsReaderTest
Test: atest ConnectivityCoverageTests:android.net.connectivity.android.net.ConnectivityManagerTest
Bug: 297836825
Change-Id: I7e13191759430f3ea1f4dec7facc02f16be7146d
diff --git a/tests/unit/java/android/net/ConnectivityManagerTest.java b/tests/unit/java/android/net/ConnectivityManagerTest.java
index 45a9dbc..b8c5447 100644
--- a/tests/unit/java/android/net/ConnectivityManagerTest.java
+++ b/tests/unit/java/android/net/ConnectivityManagerTest.java
@@ -16,6 +16,13 @@
 
 package android.net;
 
+import static android.content.Context.RECEIVER_NOT_EXPORTED;
+import static android.content.pm.ApplicationInfo.FLAG_PERSISTENT;
+import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
+import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
 import static android.net.ConnectivityManager.TYPE_NONE;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
@@ -39,6 +46,7 @@
 
 import static com.android.testutils.MiscAsserts.assertThrows;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
@@ -51,6 +59,7 @@
 import static org.mockito.Mockito.after;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -61,7 +70,10 @@
 
 import android.app.PendingIntent;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
+import android.net.ConnectivityManager.DataSaverStatusTracker;
 import android.net.ConnectivityManager.NetworkCallback;
 import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
@@ -95,6 +107,7 @@
 
     @Mock Context mCtx;
     @Mock IConnectivityManager mService;
+    @Mock NetworkPolicyManager mNpm;
 
     @Before
     public void setUp() {
@@ -510,4 +523,54 @@
         assertNull("ConnectivityManager weak reference still not null after " + attempts
                     + " attempts", ref.get());
     }
+
+    @Test
+    public void testDataSaverStatusTracker() {
+        mockService(NetworkPolicyManager.class, Context.NETWORK_POLICY_SERVICE, mNpm);
+        // Mock proper application info.
+        doReturn(mCtx).when(mCtx).getApplicationContext();
+        final ApplicationInfo mockAppInfo = new ApplicationInfo();
+        mockAppInfo.flags = FLAG_PERSISTENT | FLAG_SYSTEM;
+        doReturn(mockAppInfo).when(mCtx).getApplicationInfo();
+        // Enable data saver.
+        doReturn(RESTRICT_BACKGROUND_STATUS_ENABLED).when(mNpm)
+                .getRestrictBackgroundStatus(anyInt());
+
+        final DataSaverStatusTracker tracker = new DataSaverStatusTracker(mCtx);
+        // Verify the data saver status is correct right after initialization.
+        assertTrue(tracker.getDataSaverEnabled());
+
+        // Verify the tracker register receiver with expected intent filter.
+        final ArgumentCaptor<IntentFilter> intentFilterCaptor =
+                ArgumentCaptor.forClass(IntentFilter.class);
+        verify(mCtx).registerReceiver(
+                any(), intentFilterCaptor.capture(), eq(RECEIVER_NOT_EXPORTED));
+        assertEquals(ACTION_RESTRICT_BACKGROUND_CHANGED,
+                intentFilterCaptor.getValue().getAction(0));
+
+        // Mock data saver status changed event and verify the tracker tracks the
+        // status accordingly.
+        doReturn(RESTRICT_BACKGROUND_STATUS_DISABLED).when(mNpm)
+                .getRestrictBackgroundStatus(anyInt());
+        tracker.onReceive(mCtx, new Intent(ACTION_RESTRICT_BACKGROUND_CHANGED));
+        assertFalse(tracker.getDataSaverEnabled());
+
+        doReturn(RESTRICT_BACKGROUND_STATUS_WHITELISTED).when(mNpm)
+                .getRestrictBackgroundStatus(anyInt());
+        tracker.onReceive(mCtx, new Intent(ACTION_RESTRICT_BACKGROUND_CHANGED));
+        assertTrue(tracker.getDataSaverEnabled());
+    }
+
+    private <T> void mockService(Class<T> clazz, String name, T service) {
+        doReturn(service).when(mCtx).getSystemService(name);
+        doReturn(name).when(mCtx).getSystemServiceName(clazz);
+
+        // If the test suite uses the inline mock maker library, such as for coverage tests,
+        // then the final version of getSystemService must also be mocked, as the real
+        // method will not be called by the test and null object is returned since no mock.
+        // Otherwise, mocking a final method will fail the test.
+        if (mCtx.getSystemService(clazz) == null) {
+            doReturn(service).when(mCtx).getSystemService(clazz);
+        }
+    }
 }