Add getUidRule method to BpfNetMaps

Upcoming CLs use this method to add chain status to failure log of
ConnectivityManagerTest#testFirewallBlocking which is a flaky test

Test: atest BpfNetMapsTest
Bug: 262141231
Change-Id: Ib130a28c708f675c79b889bf293dfe6b0f8d7f60
diff --git a/service/src/com/android/server/BpfNetMaps.java b/service/src/com/android/server/BpfNetMaps.java
index 26ec37a..b4fce37 100644
--- a/service/src/com/android/server/BpfNetMaps.java
+++ b/service/src/com/android/server/BpfNetMaps.java
@@ -721,6 +721,31 @@
     }
 
     /**
+     * Get firewall rule of specified firewall chain on specified uid.
+     *
+     * @param childChain target chain
+     * @param uid        target uid
+     * @return either FIREWALL_RULE_ALLOW or FIREWALL_RULE_DENY
+     * @throws UnsupportedOperationException if called on pre-T devices.
+     * @throws ServiceSpecificException in case of failure, with an error code indicating the
+     *                                  cause of the failure.
+     */
+    public int getUidRule(final int childChain, final int uid) {
+        throwIfPreT("isUidChainEnabled is not available on pre-T devices");
+
+        final long match = getMatchByFirewallChain(childChain);
+        final boolean isAllowList = isFirewallAllowList(childChain);
+        try {
+            final UidOwnerValue uidMatch = sUidOwnerMap.getValue(new S32(uid));
+            final boolean isMatchEnabled = uidMatch != null && (uidMatch.rule & match) != 0;
+            return isMatchEnabled == isAllowList ? FIREWALL_RULE_ALLOW : FIREWALL_RULE_DENY;
+        } catch (ErrnoException e) {
+            throw new ServiceSpecificException(e.errno,
+                    "Unable to get uid rule status: " + Os.strerror(e.errno));
+        }
+    }
+
+    /**
      * Add ingress interface filtering rules to a list of UIDs
      *
      * For a given uid, once a filtering rule is added, the kernel will only allow packets from the
diff --git a/tests/unit/java/com/android/server/BpfNetMapsTest.java b/tests/unit/java/com/android/server/BpfNetMapsTest.java
index 0e17cd7..d189848 100644
--- a/tests/unit/java/com/android/server/BpfNetMapsTest.java
+++ b/tests/unit/java/com/android/server/BpfNetMapsTest.java
@@ -690,6 +690,80 @@
                 mBpfNetMaps.setUidRule(FIREWALL_CHAIN_DOZABLE, TEST_UID, FIREWALL_RULE_ALLOW));
     }
 
+    private void doTestGetUidRule(final List<Integer> enableChains) throws Exception {
+        mUidOwnerMap.updateEntry(new S32(TEST_UID), new UidOwnerValue(0, getMatch(enableChains)));
+
+        for (final int chain: FIREWALL_CHAINS) {
+            final String testCase = "EnabledChains: " + enableChains + " CheckedChain: " + chain;
+            if (enableChains.contains(chain)) {
+                final int expectedRule = mBpfNetMaps.isFirewallAllowList(chain)
+                        ? FIREWALL_RULE_ALLOW : FIREWALL_RULE_DENY;
+                assertEquals(testCase, expectedRule, mBpfNetMaps.getUidRule(chain, TEST_UID));
+            } else {
+                final int expectedRule = mBpfNetMaps.isFirewallAllowList(chain)
+                        ? FIREWALL_RULE_DENY : FIREWALL_RULE_ALLOW;
+                assertEquals(testCase, expectedRule, mBpfNetMaps.getUidRule(chain, TEST_UID));
+            }
+        }
+    }
+
+    private void doTestGetUidRule(final int enableChain) throws Exception {
+        doTestGetUidRule(List.of(enableChain));
+    }
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+    public void testGetUidRule() throws Exception {
+        doTestGetUidRule(FIREWALL_CHAIN_DOZABLE);
+        doTestGetUidRule(FIREWALL_CHAIN_STANDBY);
+        doTestGetUidRule(FIREWALL_CHAIN_POWERSAVE);
+        doTestGetUidRule(FIREWALL_CHAIN_RESTRICTED);
+        doTestGetUidRule(FIREWALL_CHAIN_LOW_POWER_STANDBY);
+        doTestGetUidRule(FIREWALL_CHAIN_OEM_DENY_1);
+        doTestGetUidRule(FIREWALL_CHAIN_OEM_DENY_2);
+        doTestGetUidRule(FIREWALL_CHAIN_OEM_DENY_3);
+    }
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+    public void testGetUidRuleMultipleChainEnabled() throws Exception {
+        doTestGetUidRule(List.of(
+                FIREWALL_CHAIN_DOZABLE,
+                FIREWALL_CHAIN_STANDBY));
+        doTestGetUidRule(List.of(
+                FIREWALL_CHAIN_DOZABLE,
+                FIREWALL_CHAIN_STANDBY,
+                FIREWALL_CHAIN_POWERSAVE,
+                FIREWALL_CHAIN_RESTRICTED));
+        doTestGetUidRule(FIREWALL_CHAINS);
+    }
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+    public void testGetUidRuleNoEntry() throws Exception {
+        mUidOwnerMap.clear();
+        for (final int chain: FIREWALL_CHAINS) {
+            final int expectedRule = mBpfNetMaps.isFirewallAllowList(chain)
+                    ? FIREWALL_RULE_DENY : FIREWALL_RULE_ALLOW;
+            assertEquals(expectedRule, mBpfNetMaps.getUidRule(chain, TEST_UID));
+        }
+    }
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+    public void testGetUidRuleInvalidChain() {
+        final Class<ServiceSpecificException> expected = ServiceSpecificException.class;
+        assertThrows(expected, () -> mBpfNetMaps.getUidRule(-1 /* childChain */, TEST_UID));
+        assertThrows(expected, () -> mBpfNetMaps.getUidRule(1000 /* childChain */, TEST_UID));
+    }
+
+    @Test
+    @IgnoreAfter(Build.VERSION_CODES.S_V2)
+    public void testGetUidRuleBeforeT() {
+        assertThrows(UnsupportedOperationException.class,
+                () -> mBpfNetMaps.getUidRule(FIREWALL_CHAIN_DOZABLE, TEST_UID));
+    }
+
     @Test
     @IgnoreUpTo(Build.VERSION_CODES.S_V2)
     public void testReplaceUidChain() throws Exception {