[BR11] Read data saver status from bpf
Writing data saver status to bpf is supported on Android V or later devices. Thus, read that from bpf if available. Test: atest FrameworksNetTests:android.net.connectivity.android.net.BpfNetMapsReaderTest Test: atest ConnectivityCoverageTests:android.net.connectivity.android.net.ConnectivityManagerTest Fix: 310801259 Change-Id: Ibd2616328d83f72ee6d2665239c3a44379d1ebf5
This commit is contained in:
@@ -17,6 +17,9 @@
|
|||||||
package android.net;
|
package android.net;
|
||||||
|
|
||||||
import static android.net.BpfNetMapsConstants.CONFIGURATION_MAP_PATH;
|
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.HAPPY_BOX_MATCH;
|
||||||
import static android.net.BpfNetMapsConstants.PENALTY_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_OWNER_MAP_PATH;
|
||||||
@@ -33,14 +36,15 @@ import android.os.Build;
|
|||||||
import android.os.ServiceSpecificException;
|
import android.os.ServiceSpecificException;
|
||||||
import android.system.ErrnoException;
|
import android.system.ErrnoException;
|
||||||
import android.system.Os;
|
import android.system.Os;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.modules.utils.build.SdkLevel;
|
import com.android.modules.utils.build.SdkLevel;
|
||||||
import com.android.net.module.util.BpfMap;
|
import com.android.net.module.util.BpfMap;
|
||||||
import com.android.net.module.util.IBpfMap;
|
import com.android.net.module.util.IBpfMap;
|
||||||
import com.android.net.module.util.Struct;
|
|
||||||
import com.android.net.module.util.Struct.S32;
|
import com.android.net.module.util.Struct.S32;
|
||||||
import com.android.net.module.util.Struct.U32;
|
import com.android.net.module.util.Struct.U32;
|
||||||
|
import com.android.net.module.util.Struct.U8;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A helper class to *read* java BpfMaps.
|
* A helper class to *read* java BpfMaps.
|
||||||
@@ -48,6 +52,8 @@ import com.android.net.module.util.Struct.U32;
|
|||||||
*/
|
*/
|
||||||
@RequiresApi(Build.VERSION_CODES.TIRAMISU) // BPF maps were only mainlined in T
|
@RequiresApi(Build.VERSION_CODES.TIRAMISU) // BPF maps were only mainlined in T
|
||||||
public class BpfNetMapsReader {
|
public class BpfNetMapsReader {
|
||||||
|
private static final String TAG = BpfNetMapsReader.class.getSimpleName();
|
||||||
|
|
||||||
// Locally store the handle of bpf maps. The FileDescriptors are statically cached inside the
|
// Locally store the handle of bpf maps. The FileDescriptors are statically cached inside the
|
||||||
// BpfMap implementation.
|
// BpfMap implementation.
|
||||||
|
|
||||||
@@ -57,6 +63,7 @@ public class BpfNetMapsReader {
|
|||||||
// Bpf map to store per uid traffic control configurations.
|
// Bpf map to store per uid traffic control configurations.
|
||||||
// See {@link UidOwnerValue} for more detail.
|
// See {@link UidOwnerValue} for more detail.
|
||||||
private final IBpfMap<S32, UidOwnerValue> mUidOwnerMap;
|
private final IBpfMap<S32, UidOwnerValue> mUidOwnerMap;
|
||||||
|
private final IBpfMap<S32, U8> mDataSaverEnabledMap;
|
||||||
private final Dependencies mDeps;
|
private final Dependencies mDeps;
|
||||||
|
|
||||||
// Bitmaps for calculating whether a given uid is blocked by firewall chains.
|
// Bitmaps for calculating whether a given uid is blocked by firewall chains.
|
||||||
@@ -104,6 +111,7 @@ public class BpfNetMapsReader {
|
|||||||
mDeps = deps;
|
mDeps = deps;
|
||||||
mConfigurationMap = mDeps.getConfigurationMap();
|
mConfigurationMap = mDeps.getConfigurationMap();
|
||||||
mUidOwnerMap = mDeps.getUidOwnerMap();
|
mUidOwnerMap = mDeps.getUidOwnerMap();
|
||||||
|
mDataSaverEnabledMap = mDeps.getDataSaverEnabledMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -130,6 +138,16 @@ public class BpfNetMapsReader {
|
|||||||
throw new IllegalStateException("Cannot open uid owner map", 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -171,12 +189,12 @@ public class BpfNetMapsReader {
|
|||||||
* cause of the failure.
|
* cause of the failure.
|
||||||
*/
|
*/
|
||||||
public static boolean isChainEnabled(
|
public static boolean isChainEnabled(
|
||||||
final IBpfMap<Struct.S32, Struct.U32> configurationMap, final int chain) {
|
final IBpfMap<S32, U32> configurationMap, final int chain) {
|
||||||
throwIfPreT("isChainEnabled is not available on pre-T devices");
|
throwIfPreT("isChainEnabled is not available on pre-T devices");
|
||||||
|
|
||||||
final long match = getMatchByFirewallChain(chain);
|
final long match = getMatchByFirewallChain(chain);
|
||||||
try {
|
try {
|
||||||
final Struct.U32 config = configurationMap.getValue(UID_RULES_CONFIGURATION_KEY);
|
final U32 config = configurationMap.getValue(UID_RULES_CONFIGURATION_KEY);
|
||||||
return (config.val & match) != 0;
|
return (config.val & match) != 0;
|
||||||
} catch (ErrnoException e) {
|
} catch (ErrnoException e) {
|
||||||
throw new ServiceSpecificException(e.errno,
|
throw new ServiceSpecificException(e.errno,
|
||||||
@@ -195,14 +213,14 @@ public class BpfNetMapsReader {
|
|||||||
* @throws ServiceSpecificException in case of failure, with an error code indicating the
|
* @throws ServiceSpecificException in case of failure, with an error code indicating the
|
||||||
* cause of the failure.
|
* cause of the failure.
|
||||||
*/
|
*/
|
||||||
public static int getUidRule(final IBpfMap<Struct.S32, UidOwnerValue> uidOwnerMap,
|
public static int getUidRule(final IBpfMap<S32, UidOwnerValue> uidOwnerMap,
|
||||||
final int chain, final int uid) {
|
final int chain, final int uid) {
|
||||||
throwIfPreT("getUidRule is not available on pre-T devices");
|
throwIfPreT("getUidRule is not available on pre-T devices");
|
||||||
|
|
||||||
final long match = getMatchByFirewallChain(chain);
|
final long match = getMatchByFirewallChain(chain);
|
||||||
final boolean isAllowList = isFirewallAllowList(chain);
|
final boolean isAllowList = isFirewallAllowList(chain);
|
||||||
try {
|
try {
|
||||||
final UidOwnerValue uidMatch = uidOwnerMap.getValue(new Struct.S32(uid));
|
final UidOwnerValue uidMatch = uidOwnerMap.getValue(new S32(uid));
|
||||||
final boolean isMatchEnabled = uidMatch != null && (uidMatch.rule & match) != 0;
|
final boolean isMatchEnabled = uidMatch != null && (uidMatch.rule & match) != 0;
|
||||||
return isMatchEnabled == isAllowList ? FIREWALL_RULE_ALLOW : FIREWALL_RULE_DENY;
|
return isMatchEnabled == isAllowList ? FIREWALL_RULE_ALLOW : FIREWALL_RULE_DENY;
|
||||||
} catch (ErrnoException e) {
|
} catch (ErrnoException e) {
|
||||||
@@ -249,4 +267,29 @@ public class BpfNetMapsReader {
|
|||||||
if ((uidMatch & HAPPY_BOX_MATCH) != 0) return false;
|
if ((uidMatch & HAPPY_BOX_MATCH) != 0) return false;
|
||||||
return isDataSaverEnabled;
|
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");
|
||||||
|
|
||||||
|
// Note that this is not expected to be called until V given that it relies on the
|
||||||
|
// counterpart platform solution to set data saver status to bpf.
|
||||||
|
// See {@code NetworkManagementService#setDataSaverModeEnabled}.
|
||||||
|
if (!SdkLevel.isAtLeastV()) {
|
||||||
|
Log.wtf(TAG, "getDataSaverEnabled is not expected to be called on pre-V 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ import android.util.SparseIntArray;
|
|||||||
|
|
||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
import com.android.modules.utils.build.SdkLevel;
|
||||||
|
|
||||||
import libcore.net.event.NetworkEventDispatcher;
|
import libcore.net.event.NetworkEventDispatcher;
|
||||||
|
|
||||||
@@ -6371,10 +6372,13 @@ public class ConnectivityManager {
|
|||||||
final BpfNetMapsReader reader = BpfNetMapsReader.getInstance();
|
final BpfNetMapsReader reader = BpfNetMapsReader.getInstance();
|
||||||
|
|
||||||
final boolean isDataSaverEnabled;
|
final boolean isDataSaverEnabled;
|
||||||
// TODO: For U-QPR3+ devices, get data saver status from bpf configuration map directly.
|
if (SdkLevel.isAtLeastV()) {
|
||||||
|
isDataSaverEnabled = reader.getDataSaverEnabled();
|
||||||
|
} else {
|
||||||
final DataSaverStatusTracker dataSaverStatusTracker =
|
final DataSaverStatusTracker dataSaverStatusTracker =
|
||||||
DataSaverStatusTracker.getInstance(mContext);
|
DataSaverStatusTracker.getInstance(mContext);
|
||||||
isDataSaverEnabled = dataSaverStatusTracker.getDataSaverEnabled();
|
isDataSaverEnabled = dataSaverStatusTracker.getDataSaverEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
return reader.isUidNetworkingBlocked(uid, isNetworkMetered, isDataSaverEnabled);
|
return reader.isUidNetworkingBlocked(uid, isNetworkMetered, isDataSaverEnabled);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,9 @@
|
|||||||
|
|
||||||
package android.net
|
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.DOZABLE_MATCH
|
||||||
import android.net.BpfNetMapsConstants.HAPPY_BOX_MATCH
|
import android.net.BpfNetMapsConstants.HAPPY_BOX_MATCH
|
||||||
import android.net.BpfNetMapsConstants.PENALTY_BOX_MATCH
|
import android.net.BpfNetMapsConstants.PENALTY_BOX_MATCH
|
||||||
@@ -26,6 +29,8 @@ import android.os.Build.VERSION_CODES
|
|||||||
import com.android.net.module.util.IBpfMap
|
import com.android.net.module.util.IBpfMap
|
||||||
import com.android.net.module.util.Struct.S32
|
import com.android.net.module.util.Struct.S32
|
||||||
import com.android.net.module.util.Struct.U32
|
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.DevSdkIgnoreRule.IgnoreUpTo
|
||||||
import com.android.testutils.DevSdkIgnoreRunner
|
import com.android.testutils.DevSdkIgnoreRunner
|
||||||
import com.android.testutils.TestBpfMap
|
import com.android.testutils.TestBpfMap
|
||||||
@@ -33,6 +38,7 @@ import java.lang.reflect.Modifier
|
|||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFalse
|
import kotlin.test.assertFalse
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
@@ -45,17 +51,24 @@ private const val NO_IIF = 0
|
|||||||
@RunWith(DevSdkIgnoreRunner::class)
|
@RunWith(DevSdkIgnoreRunner::class)
|
||||||
@IgnoreUpTo(VERSION_CODES.S_V2)
|
@IgnoreUpTo(VERSION_CODES.S_V2)
|
||||||
class BpfNetMapsReaderTest {
|
class BpfNetMapsReaderTest {
|
||||||
|
@Rule
|
||||||
|
@JvmField
|
||||||
|
val ignoreRule = DevSdkIgnoreRule()
|
||||||
|
|
||||||
private val testConfigurationMap: IBpfMap<S32, U32> = TestBpfMap()
|
private val testConfigurationMap: IBpfMap<S32, U32> = TestBpfMap()
|
||||||
private val testUidOwnerMap: IBpfMap<S32, UidOwnerValue> = TestBpfMap()
|
private val testUidOwnerMap: IBpfMap<S32, UidOwnerValue> = TestBpfMap()
|
||||||
|
private val testDataSaverEnabledMap: IBpfMap<S32, U8> = TestBpfMap()
|
||||||
private val bpfNetMapsReader = BpfNetMapsReader(
|
private val bpfNetMapsReader = BpfNetMapsReader(
|
||||||
TestDependencies(testConfigurationMap, testUidOwnerMap))
|
TestDependencies(testConfigurationMap, testUidOwnerMap, testDataSaverEnabledMap))
|
||||||
|
|
||||||
class TestDependencies(
|
class TestDependencies(
|
||||||
private val configMap: IBpfMap<S32, U32>,
|
private val configMap: IBpfMap<S32, U32>,
|
||||||
private val uidOwnerMap: IBpfMap<S32, UidOwnerValue>
|
private val uidOwnerMap: IBpfMap<S32, UidOwnerValue>,
|
||||||
|
private val dataSaverEnabledMap: IBpfMap<S32, U8>
|
||||||
) : BpfNetMapsReader.Dependencies() {
|
) : BpfNetMapsReader.Dependencies() {
|
||||||
override fun getConfigurationMap() = configMap
|
override fun getConfigurationMap() = configMap
|
||||||
override fun getUidOwnerMap() = uidOwnerMap
|
override fun getUidOwnerMap() = uidOwnerMap
|
||||||
|
override fun getDataSaverEnabledMap() = dataSaverEnabledMap
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun doTestIsChainEnabled(chain: Int) {
|
private fun doTestIsChainEnabled(chain: Int) {
|
||||||
@@ -199,4 +212,13 @@ class BpfNetMapsReaderTest {
|
|||||||
assertFalse(isUidNetworkingBlocked(TEST_UID2))
|
assertFalse(isUidNetworkingBlocked(TEST_UID2))
|
||||||
assertFalse(isUidNetworkingBlocked(TEST_UID3))
|
assertFalse(isUidNetworkingBlocked(TEST_UID3))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@IgnoreUpTo(VERSION_CODES.UPSIDE_DOWN_CAKE)
|
||||||
|
@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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ import com.android.testutils.DevSdkIgnoreRule;
|
|||||||
import com.android.testutils.DevSdkIgnoreRunner;
|
import com.android.testutils.DevSdkIgnoreRunner;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
@@ -102,6 +103,8 @@ import java.lang.ref.WeakReference;
|
|||||||
@SmallTest
|
@SmallTest
|
||||||
@DevSdkIgnoreRule.IgnoreUpTo(VERSION_CODES.R)
|
@DevSdkIgnoreRule.IgnoreUpTo(VERSION_CODES.R)
|
||||||
public class ConnectivityManagerTest {
|
public class ConnectivityManagerTest {
|
||||||
|
@Rule
|
||||||
|
public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
|
||||||
private static final int TIMEOUT_MS = 30_000;
|
private static final int TIMEOUT_MS = 30_000;
|
||||||
private static final int SHORT_TIMEOUT_MS = 150;
|
private static final int SHORT_TIMEOUT_MS = 150;
|
||||||
|
|
||||||
@@ -524,6 +527,7 @@ public class ConnectivityManagerTest {
|
|||||||
+ " attempts", ref.get());
|
+ " attempts", ref.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DevSdkIgnoreRule.IgnoreAfter(VERSION_CODES.UPSIDE_DOWN_CAKE)
|
||||||
@Test
|
@Test
|
||||||
public void testDataSaverStatusTracker() {
|
public void testDataSaverStatusTracker() {
|
||||||
mockService(NetworkPolicyManager.class, Context.NETWORK_POLICY_SERVICE, mNpm);
|
mockService(NetworkPolicyManager.class, Context.NETWORK_POLICY_SERVICE, mNpm);
|
||||||
|
|||||||
Reference in New Issue
Block a user