Rename BpfNetMapsReader to NetworkStackBpfNetMaps
BpfNetMapsReader is loaded to bootclasspath. So this class should not contain unnecessary codes. To make this clear, this CL renames to NetowrkStackBpfNetMaps and add comments Bug: 328732146 Test: TH Change-Id: I557627c9fae087eb6119c2ae31d46526fa9e5d7e
This commit is contained in:
parent
a8e3bfb7df
commit
fc504042f6
4 changed files with 29 additions and 26 deletions
|
|
@ -6286,7 +6286,7 @@ public class ConnectivityManager {
|
|||
if (!SdkLevel.isAtLeastU()) {
|
||||
Log.wtf(TAG, "isUidNetworkingBlocked is not supported on pre-U devices");
|
||||
}
|
||||
final BpfNetMapsReader reader = BpfNetMapsReader.getInstance();
|
||||
final NetworkStackBpfNetMaps reader = NetworkStackBpfNetMaps.getInstance();
|
||||
// Note that before V, the data saver status in bpf is written by ConnectivityService
|
||||
// when receiving {@link #ACTION_RESTRICT_BACKGROUND_CHANGED}. Thus,
|
||||
// the status is not synchronized.
|
||||
|
|
|
|||
290
framework/src/android/net/NetworkStackBpfNetMaps.java
Normal file
290
framework/src/android/net/NetworkStackBpfNetMaps.java
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
/*
|
||||
* 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 android.net;
|
||||
|
||||
import static android.net.BpfNetMapsConstants.CONFIGURATION_MAP_PATH;
|
||||
import static android.net.BpfNetMapsConstants.DATA_SAVER_ENABLED;
|
||||
import static android.net.BpfNetMapsConstants.DATA_SAVER_ENABLED_KEY;
|
||||
import static android.net.BpfNetMapsConstants.DATA_SAVER_ENABLED_MAP_PATH;
|
||||
import static android.net.BpfNetMapsConstants.HAPPY_BOX_MATCH;
|
||||
import static android.net.BpfNetMapsConstants.PENALTY_BOX_MATCH;
|
||||
import static android.net.BpfNetMapsConstants.UID_OWNER_MAP_PATH;
|
||||
import static android.net.BpfNetMapsConstants.UID_RULES_CONFIGURATION_KEY;
|
||||
import static android.net.BpfNetMapsUtils.getMatchByFirewallChain;
|
||||
import static android.net.BpfNetMapsUtils.isFirewallAllowList;
|
||||
import static android.net.BpfNetMapsUtils.throwIfPreT;
|
||||
import static android.net.ConnectivityManager.FIREWALL_RULE_ALLOW;
|
||||
import static android.net.ConnectivityManager.FIREWALL_RULE_DENY;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.RequiresApi;
|
||||
import android.os.Build;
|
||||
import android.os.ServiceSpecificException;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.modules.utils.build.SdkLevel;
|
||||
import com.android.net.module.util.BpfMap;
|
||||
import com.android.net.module.util.IBpfMap;
|
||||
import com.android.net.module.util.Struct.S32;
|
||||
import com.android.net.module.util.Struct.U32;
|
||||
import com.android.net.module.util.Struct.U8;
|
||||
|
||||
/**
|
||||
* A helper class to *read* java BpfMaps for network stack.
|
||||
* BpfMap operations that are not used from network stack should be in
|
||||
* {@link com.android.server.BpfNetMaps}
|
||||
* @hide
|
||||
*/
|
||||
@RequiresApi(Build.VERSION_CODES.TIRAMISU) // BPF maps were only mainlined in T
|
||||
public class NetworkStackBpfNetMaps {
|
||||
private static final String TAG = NetworkStackBpfNetMaps.class.getSimpleName();
|
||||
|
||||
// Locally store the handle of bpf maps. The FileDescriptors are statically cached inside the
|
||||
// BpfMap implementation.
|
||||
|
||||
// Bpf map to store various networking configurations, the format of the value is different
|
||||
// for different keys. See BpfNetMapsConstants#*_CONFIGURATION_KEY for keys.
|
||||
private final IBpfMap<S32, U32> mConfigurationMap;
|
||||
// Bpf map to store per uid traffic control configurations.
|
||||
// See {@link UidOwnerValue} for more detail.
|
||||
private final IBpfMap<S32, UidOwnerValue> mUidOwnerMap;
|
||||
private final IBpfMap<S32, U8> mDataSaverEnabledMap;
|
||||
private final Dependencies mDeps;
|
||||
|
||||
// Bitmaps for calculating whether a given uid is blocked by firewall chains.
|
||||
private static final long sMaskDropIfSet;
|
||||
private static final long sMaskDropIfUnset;
|
||||
|
||||
static {
|
||||
long maskDropIfSet = 0L;
|
||||
long maskDropIfUnset = 0L;
|
||||
|
||||
for (int chain : BpfNetMapsConstants.ALLOW_CHAINS) {
|
||||
final long match = getMatchByFirewallChain(chain);
|
||||
maskDropIfUnset |= match;
|
||||
}
|
||||
for (int chain : BpfNetMapsConstants.DENY_CHAINS) {
|
||||
final long match = getMatchByFirewallChain(chain);
|
||||
maskDropIfSet |= match;
|
||||
}
|
||||
sMaskDropIfSet = maskDropIfSet;
|
||||
sMaskDropIfUnset = maskDropIfUnset;
|
||||
}
|
||||
|
||||
private static class SingletonHolder {
|
||||
static final NetworkStackBpfNetMaps sInstance = new NetworkStackBpfNetMaps();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static NetworkStackBpfNetMaps getInstance() {
|
||||
return SingletonHolder.sInstance;
|
||||
}
|
||||
|
||||
private NetworkStackBpfNetMaps() {
|
||||
this(new Dependencies());
|
||||
}
|
||||
|
||||
// While the production code uses the singleton to optimize for performance and deal with
|
||||
// concurrent access, the test needs to use a non-static approach for dependency injection and
|
||||
// mocking virtual bpf maps.
|
||||
@VisibleForTesting
|
||||
public NetworkStackBpfNetMaps(@NonNull Dependencies deps) {
|
||||
if (!SdkLevel.isAtLeastT()) {
|
||||
throw new UnsupportedOperationException(
|
||||
NetworkStackBpfNetMaps.class.getSimpleName()
|
||||
+ " is not supported below Android T");
|
||||
}
|
||||
mDeps = deps;
|
||||
mConfigurationMap = mDeps.getConfigurationMap();
|
||||
mUidOwnerMap = mDeps.getUidOwnerMap();
|
||||
mDataSaverEnabledMap = mDeps.getDataSaverEnabledMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dependencies of BpfNetMapReader, for injection in tests.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static class Dependencies {
|
||||
/** Get the configuration map. */
|
||||
public IBpfMap<S32, U32> getConfigurationMap() {
|
||||
try {
|
||||
return new BpfMap<>(CONFIGURATION_MAP_PATH, BpfMap.BPF_F_RDONLY,
|
||||
S32.class, U32.class);
|
||||
} catch (ErrnoException e) {
|
||||
throw new IllegalStateException("Cannot open configuration map", e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the uid owner map. */
|
||||
public IBpfMap<S32, UidOwnerValue> getUidOwnerMap() {
|
||||
try {
|
||||
return new BpfMap<>(UID_OWNER_MAP_PATH, BpfMap.BPF_F_RDONLY,
|
||||
S32.class, UidOwnerValue.class);
|
||||
} catch (ErrnoException e) {
|
||||
throw new IllegalStateException("Cannot open uid owner map", e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the data saver enabled map. */
|
||||
public IBpfMap<S32, U8> getDataSaverEnabledMap() {
|
||||
try {
|
||||
return new BpfMap<>(DATA_SAVER_ENABLED_MAP_PATH, BpfMap.BPF_F_RDONLY, S32.class,
|
||||
U8.class);
|
||||
} catch (ErrnoException e) {
|
||||
throw new IllegalStateException("Cannot open data saver enabled map", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the specified firewall chain's status.
|
||||
*
|
||||
* @param chain target chain
|
||||
* @return {@code true} if chain is enabled, {@code false} if chain is not enabled.
|
||||
* @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 boolean isChainEnabled(final int chain) {
|
||||
return isChainEnabled(mConfigurationMap, chain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get firewall rule of specified firewall chain on specified uid.
|
||||
*
|
||||
* @param chain target chain
|
||||
* @param uid target uid
|
||||
* @return either {@link ConnectivityManager#FIREWALL_RULE_ALLOW} or
|
||||
* {@link ConnectivityManager#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 chain, final int uid) {
|
||||
return getUidRule(mUidOwnerMap, chain, uid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the specified firewall chain's status.
|
||||
*
|
||||
* @param configurationMap target configurationMap
|
||||
* @param chain target chain
|
||||
* @return {@code true} if chain is enabled, {@code false} if chain is not enabled.
|
||||
* @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 static boolean isChainEnabled(
|
||||
final IBpfMap<S32, U32> configurationMap, final int chain) {
|
||||
throwIfPreT("isChainEnabled is not available on pre-T devices");
|
||||
|
||||
final long match = getMatchByFirewallChain(chain);
|
||||
try {
|
||||
final U32 config = configurationMap.getValue(UID_RULES_CONFIGURATION_KEY);
|
||||
return (config.val & match) != 0;
|
||||
} catch (ErrnoException e) {
|
||||
throw new ServiceSpecificException(e.errno,
|
||||
"Unable to get firewall chain status: " + Os.strerror(e.errno));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get firewall rule of specified firewall chain on specified uid.
|
||||
*
|
||||
* @param uidOwnerMap target uidOwnerMap.
|
||||
* @param chain 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 static int getUidRule(final IBpfMap<S32, UidOwnerValue> uidOwnerMap,
|
||||
final int chain, final int uid) {
|
||||
throwIfPreT("getUidRule is not available on pre-T devices");
|
||||
|
||||
final long match = getMatchByFirewallChain(chain);
|
||||
final boolean isAllowList = isFirewallAllowList(chain);
|
||||
try {
|
||||
final UidOwnerValue uidMatch = uidOwnerMap.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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the network is blocked by firewall chains for the given uid.
|
||||
*
|
||||
* @param uid The target uid.
|
||||
* @param isNetworkMetered Whether the target network is metered.
|
||||
* @param isDataSaverEnabled Whether the data saver is enabled.
|
||||
*
|
||||
* @return True if the network is blocked. Otherwise, false.
|
||||
* @throws ServiceSpecificException if the read fails.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public boolean isUidNetworkingBlocked(final int uid, boolean isNetworkMetered,
|
||||
boolean isDataSaverEnabled) {
|
||||
throwIfPreT("isUidBlockedByFirewallChains is not available on pre-T devices");
|
||||
|
||||
final long uidRuleConfig;
|
||||
final long uidMatch;
|
||||
try {
|
||||
uidRuleConfig = mConfigurationMap.getValue(UID_RULES_CONFIGURATION_KEY).val;
|
||||
final UidOwnerValue value = mUidOwnerMap.getValue(new S32(uid));
|
||||
uidMatch = (value != null) ? value.rule : 0L;
|
||||
} catch (ErrnoException e) {
|
||||
throw new ServiceSpecificException(e.errno,
|
||||
"Unable to get firewall chain status: " + Os.strerror(e.errno));
|
||||
}
|
||||
|
||||
final boolean blockedByAllowChains = 0 != (uidRuleConfig & ~uidMatch & sMaskDropIfUnset);
|
||||
final boolean blockedByDenyChains = 0 != (uidRuleConfig & uidMatch & sMaskDropIfSet);
|
||||
if (blockedByAllowChains || blockedByDenyChains) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!isNetworkMetered) return false;
|
||||
if ((uidMatch & PENALTY_BOX_MATCH) != 0) return true;
|
||||
if ((uidMatch & HAPPY_BOX_MATCH) != 0) return false;
|
||||
return isDataSaverEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Data Saver enabled or disabled
|
||||
*
|
||||
* @return whether Data Saver is enabled or disabled.
|
||||
* @throws ServiceSpecificException in case of failure, with an error code indicating the
|
||||
* cause of the failure.
|
||||
*/
|
||||
public boolean getDataSaverEnabled() {
|
||||
throwIfPreT("getDataSaverEnabled is not available on pre-T devices");
|
||||
|
||||
try {
|
||||
return mDataSaverEnabledMap.getValue(DATA_SAVER_ENABLED_KEY).val == DATA_SAVER_ENABLED;
|
||||
} catch (ErrnoException e) {
|
||||
throw new ServiceSpecificException(e.errno, "Unable to get data saver: "
|
||||
+ Os.strerror(e.errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -49,8 +49,8 @@ import static com.android.server.ConnectivityStatsLog.NETWORK_BPF_MAP_INFO;
|
|||
|
||||
import android.app.StatsManager;
|
||||
import android.content.Context;
|
||||
import android.net.BpfNetMapsReader;
|
||||
import android.net.INetd;
|
||||
import android.net.NetworkStackBpfNetMaps;
|
||||
import android.net.UidOwnerValue;
|
||||
import android.os.Build;
|
||||
import android.os.RemoteException;
|
||||
|
|
@ -535,14 +535,11 @@ public class BpfNetMaps {
|
|||
* @throws UnsupportedOperationException if called on pre-T devices.
|
||||
* @throws ServiceSpecificException in case of failure, with an error code indicating the
|
||||
* cause of the failure.
|
||||
*
|
||||
* @deprecated Use {@link BpfNetMapsReader#isChainEnabled} instead.
|
||||
*/
|
||||
// TODO: Migrate the callers to use {@link BpfNetMapsReader#isChainEnabled} instead.
|
||||
@Deprecated
|
||||
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
||||
public boolean isChainEnabled(final int childChain) {
|
||||
return BpfNetMapsReader.isChainEnabled(sConfigurationMap, childChain);
|
||||
return NetworkStackBpfNetMaps.isChainEnabled(sConfigurationMap, childChain);
|
||||
}
|
||||
|
||||
private Set<Integer> asSet(final int[] uids) {
|
||||
|
|
@ -635,12 +632,9 @@ public class BpfNetMaps {
|
|||
* @throws UnsupportedOperationException if called on pre-T devices.
|
||||
* @throws ServiceSpecificException in case of failure, with an error code indicating the
|
||||
* cause of the failure.
|
||||
*
|
||||
* @deprecated use {@link BpfNetMapsReader#getUidRule} instead.
|
||||
*/
|
||||
// TODO: Migrate the callers to use {@link BpfNetMapsReader#getUidRule} instead.
|
||||
public int getUidRule(final int childChain, final int uid) {
|
||||
return BpfNetMapsReader.getUidRule(sUidOwnerMap, childChain, uid);
|
||||
return NetworkStackBpfNetMaps.getUidRule(sUidOwnerMap, childChain, uid);
|
||||
}
|
||||
|
||||
private Set<Integer> getUidsMatchEnabled(final int childChain) throws ErrnoException {
|
||||
|
|
|
|||
229
tests/unit/java/android/net/NetworkStackBpfNetMapsTest.kt
Normal file
229
tests/unit/java/android/net/NetworkStackBpfNetMapsTest.kt
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
* 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 android.net
|
||||
|
||||
import android.net.BpfNetMapsConstants.DATA_SAVER_DISABLED
|
||||
import android.net.BpfNetMapsConstants.DATA_SAVER_ENABLED
|
||||
import android.net.BpfNetMapsConstants.DATA_SAVER_ENABLED_KEY
|
||||
import android.net.BpfNetMapsConstants.DOZABLE_MATCH
|
||||
import android.net.BpfNetMapsConstants.HAPPY_BOX_MATCH
|
||||
import android.net.BpfNetMapsConstants.PENALTY_BOX_MATCH
|
||||
import android.net.BpfNetMapsConstants.STANDBY_MATCH
|
||||
import android.net.BpfNetMapsConstants.UID_RULES_CONFIGURATION_KEY
|
||||
import android.net.BpfNetMapsUtils.getMatchByFirewallChain
|
||||
import android.os.Build.VERSION_CODES
|
||||
import com.android.net.module.util.IBpfMap
|
||||
import com.android.net.module.util.Struct.S32
|
||||
import com.android.net.module.util.Struct.U32
|
||||
import com.android.net.module.util.Struct.U8
|
||||
import com.android.testutils.DevSdkIgnoreRule
|
||||
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
|
||||
import com.android.testutils.DevSdkIgnoreRunner
|
||||
import com.android.testutils.TestBpfMap
|
||||
import java.lang.reflect.Modifier
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
private const val TEST_UID1 = 1234
|
||||
private const val TEST_UID2 = TEST_UID1 + 1
|
||||
private const val TEST_UID3 = TEST_UID2 + 1
|
||||
private const val NO_IIF = 0
|
||||
|
||||
// pre-T devices does not support Bpf.
|
||||
@RunWith(DevSdkIgnoreRunner::class)
|
||||
@IgnoreUpTo(VERSION_CODES.S_V2)
|
||||
class NetworkStackBpfNetMapsTest {
|
||||
@Rule
|
||||
@JvmField
|
||||
val ignoreRule = DevSdkIgnoreRule()
|
||||
|
||||
private val testConfigurationMap: IBpfMap<S32, U32> = TestBpfMap()
|
||||
private val testUidOwnerMap: IBpfMap<S32, UidOwnerValue> = TestBpfMap()
|
||||
private val testDataSaverEnabledMap: IBpfMap<S32, U8> = TestBpfMap()
|
||||
private val bpfNetMapsReader = NetworkStackBpfNetMaps(
|
||||
TestDependencies(testConfigurationMap, testUidOwnerMap, testDataSaverEnabledMap)
|
||||
)
|
||||
|
||||
class TestDependencies(
|
||||
private val configMap: IBpfMap<S32, U32>,
|
||||
private val uidOwnerMap: IBpfMap<S32, UidOwnerValue>,
|
||||
private val dataSaverEnabledMap: IBpfMap<S32, U8>
|
||||
) : NetworkStackBpfNetMaps.Dependencies() {
|
||||
override fun getConfigurationMap() = configMap
|
||||
override fun getUidOwnerMap() = uidOwnerMap
|
||||
override fun getDataSaverEnabledMap() = dataSaverEnabledMap
|
||||
}
|
||||
|
||||
private fun doTestIsChainEnabled(chain: Int) {
|
||||
testConfigurationMap.updateEntry(
|
||||
UID_RULES_CONFIGURATION_KEY,
|
||||
U32(getMatchByFirewallChain(chain))
|
||||
)
|
||||
assertTrue(bpfNetMapsReader.isChainEnabled(chain))
|
||||
testConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, U32(0))
|
||||
assertFalse(bpfNetMapsReader.isChainEnabled(chain))
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testIsChainEnabled() {
|
||||
doTestIsChainEnabled(ConnectivityManager.FIREWALL_CHAIN_DOZABLE)
|
||||
doTestIsChainEnabled(ConnectivityManager.FIREWALL_CHAIN_STANDBY)
|
||||
doTestIsChainEnabled(ConnectivityManager.FIREWALL_CHAIN_POWERSAVE)
|
||||
doTestIsChainEnabled(ConnectivityManager.FIREWALL_CHAIN_RESTRICTED)
|
||||
doTestIsChainEnabled(ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFirewallChainList() {
|
||||
// Verify that when a firewall chain constant is added, it should also be included in
|
||||
// firewall chain list.
|
||||
val declaredChains = ConnectivityManager::class.java.declaredFields.filter {
|
||||
Modifier.isStatic(it.modifiers) && it.name.startsWith("FIREWALL_CHAIN_")
|
||||
}
|
||||
// Verify the size matches, this also verifies no common item in allow and deny chains.
|
||||
assertEquals(
|
||||
BpfNetMapsConstants.ALLOW_CHAINS.size +
|
||||
BpfNetMapsConstants.DENY_CHAINS.size,
|
||||
declaredChains.size
|
||||
)
|
||||
declaredChains.forEach {
|
||||
assertTrue(
|
||||
BpfNetMapsConstants.ALLOW_CHAINS.contains(it.get(null)) ||
|
||||
BpfNetMapsConstants.DENY_CHAINS.contains(it.get(null))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun mockChainEnabled(chain: Int, enabled: Boolean) {
|
||||
val config = testConfigurationMap.getValue(UID_RULES_CONFIGURATION_KEY).`val`
|
||||
val newConfig = if (enabled) {
|
||||
config or getMatchByFirewallChain(chain)
|
||||
} else {
|
||||
config and getMatchByFirewallChain(chain).inv()
|
||||
}
|
||||
testConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, U32(newConfig))
|
||||
}
|
||||
|
||||
fun isUidNetworkingBlocked(uid: Int, metered: Boolean = false, dataSaver: Boolean = false) =
|
||||
bpfNetMapsReader.isUidNetworkingBlocked(uid, metered, dataSaver)
|
||||
|
||||
@Test
|
||||
fun testIsUidNetworkingBlockedByFirewallChains_allowChain() {
|
||||
// With everything disabled by default, verify the return value is false.
|
||||
testConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, U32(0))
|
||||
assertFalse(isUidNetworkingBlocked(TEST_UID1))
|
||||
|
||||
// Enable dozable chain but does not provide allowed list. Verify the network is blocked
|
||||
// for all uids.
|
||||
mockChainEnabled(ConnectivityManager.FIREWALL_CHAIN_DOZABLE, true)
|
||||
assertTrue(isUidNetworkingBlocked(TEST_UID1))
|
||||
assertTrue(isUidNetworkingBlocked(TEST_UID2))
|
||||
|
||||
// Add uid1 to dozable allowed list. Verify the network is not blocked for uid1, while
|
||||
// uid2 is blocked.
|
||||
testUidOwnerMap.updateEntry(S32(TEST_UID1), UidOwnerValue(NO_IIF, DOZABLE_MATCH))
|
||||
assertFalse(isUidNetworkingBlocked(TEST_UID1))
|
||||
assertTrue(isUidNetworkingBlocked(TEST_UID2))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIsUidNetworkingBlockedByFirewallChains_denyChain() {
|
||||
// Enable standby chain but does not provide denied list. Verify the network is allowed
|
||||
// for all uids.
|
||||
testConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, U32(0))
|
||||
mockChainEnabled(ConnectivityManager.FIREWALL_CHAIN_STANDBY, true)
|
||||
assertFalse(isUidNetworkingBlocked(TEST_UID1))
|
||||
assertFalse(isUidNetworkingBlocked(TEST_UID2))
|
||||
|
||||
// Add uid1 to standby allowed list. Verify the network is blocked for uid1, while
|
||||
// uid2 is not blocked.
|
||||
testUidOwnerMap.updateEntry(S32(TEST_UID1), UidOwnerValue(NO_IIF, STANDBY_MATCH))
|
||||
assertTrue(isUidNetworkingBlocked(TEST_UID1))
|
||||
assertFalse(isUidNetworkingBlocked(TEST_UID2))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIsUidNetworkingBlockedByFirewallChains_blockedWithAllowed() {
|
||||
// Uids blocked by powersave chain but allowed by standby chain, verify the blocking
|
||||
// takes higher priority.
|
||||
testConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, U32(0))
|
||||
mockChainEnabled(ConnectivityManager.FIREWALL_CHAIN_POWERSAVE, true)
|
||||
mockChainEnabled(ConnectivityManager.FIREWALL_CHAIN_STANDBY, true)
|
||||
assertTrue(isUidNetworkingBlocked(TEST_UID1))
|
||||
}
|
||||
|
||||
@IgnoreUpTo(VERSION_CODES.S_V2)
|
||||
@Test
|
||||
fun testIsUidNetworkingBlockedByDataSaver() {
|
||||
// With everything disabled by default, verify the return value is false.
|
||||
testConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, U32(0))
|
||||
assertFalse(isUidNetworkingBlocked(TEST_UID1, metered = true))
|
||||
|
||||
// Add uid1 to penalty box, verify the network is blocked for uid1, while uid2 is not
|
||||
// affected.
|
||||
testUidOwnerMap.updateEntry(S32(TEST_UID1), UidOwnerValue(NO_IIF, PENALTY_BOX_MATCH))
|
||||
assertTrue(isUidNetworkingBlocked(TEST_UID1, metered = true))
|
||||
assertFalse(isUidNetworkingBlocked(TEST_UID2, metered = true))
|
||||
|
||||
// Enable data saver, verify the network is blocked for uid1, uid2, but uid3 in happy box
|
||||
// is not affected.
|
||||
testUidOwnerMap.updateEntry(S32(TEST_UID3), UidOwnerValue(NO_IIF, HAPPY_BOX_MATCH))
|
||||
assertTrue(isUidNetworkingBlocked(TEST_UID1, metered = true, dataSaver = true))
|
||||
assertTrue(isUidNetworkingBlocked(TEST_UID2, metered = true, dataSaver = true))
|
||||
assertFalse(isUidNetworkingBlocked(TEST_UID3, metered = true, dataSaver = true))
|
||||
|
||||
// Add uid1 to happy box as well, verify nothing is changed because penalty box has higher
|
||||
// priority.
|
||||
testUidOwnerMap.updateEntry(
|
||||
S32(TEST_UID1),
|
||||
UidOwnerValue(NO_IIF, PENALTY_BOX_MATCH or HAPPY_BOX_MATCH)
|
||||
)
|
||||
assertTrue(isUidNetworkingBlocked(TEST_UID1, metered = true, dataSaver = true))
|
||||
assertTrue(isUidNetworkingBlocked(TEST_UID2, metered = true, dataSaver = true))
|
||||
assertFalse(isUidNetworkingBlocked(TEST_UID3, metered = true, dataSaver = true))
|
||||
|
||||
// Enable doze mode, verify uid3 is blocked even if it is in happy box.
|
||||
mockChainEnabled(ConnectivityManager.FIREWALL_CHAIN_DOZABLE, true)
|
||||
assertTrue(isUidNetworkingBlocked(TEST_UID1, metered = true, dataSaver = true))
|
||||
assertTrue(isUidNetworkingBlocked(TEST_UID2, metered = true, dataSaver = true))
|
||||
assertTrue(isUidNetworkingBlocked(TEST_UID3, metered = true, dataSaver = true))
|
||||
|
||||
// Disable doze mode and data saver, only uid1 which is in penalty box is blocked.
|
||||
mockChainEnabled(ConnectivityManager.FIREWALL_CHAIN_DOZABLE, false)
|
||||
assertTrue(isUidNetworkingBlocked(TEST_UID1, metered = true))
|
||||
assertFalse(isUidNetworkingBlocked(TEST_UID2, metered = true))
|
||||
assertFalse(isUidNetworkingBlocked(TEST_UID3, metered = true))
|
||||
|
||||
// Make the network non-metered, nothing is blocked.
|
||||
assertFalse(isUidNetworkingBlocked(TEST_UID1))
|
||||
assertFalse(isUidNetworkingBlocked(TEST_UID2))
|
||||
assertFalse(isUidNetworkingBlocked(TEST_UID3))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetDataSaverEnabled() {
|
||||
testDataSaverEnabledMap.updateEntry(DATA_SAVER_ENABLED_KEY, U8(DATA_SAVER_DISABLED))
|
||||
assertFalse(bpfNetMapsReader.dataSaverEnabled)
|
||||
testDataSaverEnabledMap.updateEntry(DATA_SAVER_ENABLED_KEY, U8(DATA_SAVER_ENABLED))
|
||||
assertTrue(bpfNetMapsReader.dataSaverEnabled)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue