Write Data Saver setting to BPF map

The information is needed by modules who want to know whether a
specific UID is blocked by Data Saver feature.

1. Add a one-element map data_saver_enabled_map.
2. Update current data saver setting to the map.

Bug: 288340533
Test: atest FrameworksNetTests:android.net.connectivity.com.android.serv
er.BpfNetMapsTest
Test: atest bpf_existence_test

Change-Id: I981da4b569247c33cba2d365cb6f2691f673474e
This commit is contained in:
Ken Chen 2023-10-20 13:02:14 +08:00
parent 6b134f18f4
commit 243301748e
7 changed files with 123 additions and 0 deletions

View file

@ -112,6 +112,9 @@ DEFINE_BPF_RINGBUF_EXT(packet_trace_ringbuf, PacketTrace, PACKET_TRACE_BUF_SIZE,
BPFLOADER_IGNORED_ON_VERSION, BPFLOADER_MAX_VER, LOAD_ON_ENG,
LOAD_ON_USER, LOAD_ON_USERDEBUG);
DEFINE_BPF_MAP_RO_NETD(data_saver_enabled_map, ARRAY, uint32_t, bool,
DATA_SAVER_ENABLED_MAP_SIZE)
// iptables xt_bpf programs need to be usable by both netd and netutils_wrappers
// selinux contexts, because even non-xt_bpf iptables mutations are implemented as
// a full table dump, followed by an update in userspace, and then a reload into the kernel,

View file

@ -126,6 +126,7 @@ static const int CONFIGURATION_MAP_SIZE = 2;
static const int UID_OWNER_MAP_SIZE = 4000;
static const int INGRESS_DISCARD_MAP_SIZE = 100;
static const int PACKET_TRACE_BUF_SIZE = 32 * 1024;
static const int DATA_SAVER_ENABLED_MAP_SIZE = 1;
#ifdef __cplusplus
@ -172,6 +173,7 @@ ASSERT_STRING_EQUAL(XT_BPF_DENYLIST_PROG_PATH, BPF_NETD_PATH "prog_netd_skfilte
#define INGRESS_DISCARD_MAP_PATH BPF_NETD_PATH "map_netd_ingress_discard_map"
#define PACKET_TRACE_RINGBUF_PATH BPF_NETD_PATH "map_netd_packet_trace_ringbuf"
#define PACKET_TRACE_ENABLED_MAP_PATH BPF_NETD_PATH "map_netd_packet_trace_enabled_map"
#define DATA_SAVER_ENABLED_MAP_PATH BPF_NETD_PATH "map_netd_data_saver_enabled_map"
#endif // __cplusplus

View file

@ -43,8 +43,14 @@ public class BpfNetMapsConstants {
"/sys/fs/bpf/netd_shared/map_netd_uid_permission_map";
public static final String COOKIE_TAG_MAP_PATH =
"/sys/fs/bpf/netd_shared/map_netd_cookie_tag_map";
public static final String DATA_SAVER_ENABLED_MAP_PATH =
"/sys/fs/bpf/netd_shared/map_netd_data_saver_enabled_map";
public static final Struct.S32 UID_RULES_CONFIGURATION_KEY = new Struct.S32(0);
public static final Struct.S32 CURRENT_STATS_MAP_CONFIGURATION_KEY = new Struct.S32(1);
public static final Struct.S32 DATA_SAVER_ENABLED_KEY = new Struct.S32(0);
public static final short DATA_SAVER_DISABLED = 0;
public static final short DATA_SAVER_ENABLED = 1;
// LINT.IfChange(match_type)
public static final long NO_MATCH = 0;

View file

@ -19,6 +19,10 @@ package com.android.server;
import static android.net.BpfNetMapsConstants.CONFIGURATION_MAP_PATH;
import static android.net.BpfNetMapsConstants.COOKIE_TAG_MAP_PATH;
import static android.net.BpfNetMapsConstants.CURRENT_STATS_MAP_CONFIGURATION_KEY;
import static android.net.BpfNetMapsConstants.DATA_SAVER_DISABLED;
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.IIF_MATCH;
import static android.net.BpfNetMapsConstants.LOCKDOWN_VPN_MATCH;
@ -130,6 +134,8 @@ public class BpfNetMaps {
private static IBpfMap<S32, UidOwnerValue> sUidOwnerMap = null;
private static IBpfMap<S32, U8> sUidPermissionMap = null;
private static IBpfMap<CookieTagMapKey, CookieTagMapValue> sCookieTagMap = null;
// TODO: Add BOOL class and replace U8?
private static IBpfMap<S32, U8> sDataSaverEnabledMap = null;
private static final List<Pair<Integer, String>> PERMISSION_LIST = Arrays.asList(
Pair.create(PERMISSION_INTERNET, "PERMISSION_INTERNET"),
@ -177,6 +183,14 @@ public class BpfNetMaps {
sCookieTagMap = cookieTagMap;
}
/**
* Set dataSaverEnabledMap for test.
*/
@VisibleForTesting
public static void setDataSaverEnabledMapForTest(IBpfMap<S32, U8> dataSaverEnabledMap) {
sDataSaverEnabledMap = dataSaverEnabledMap;
}
private static IBpfMap<S32, U32> getConfigurationMap() {
try {
return new BpfMap<>(
@ -213,6 +227,15 @@ public class BpfNetMaps {
}
}
private static IBpfMap<S32, U8> getDataSaverEnabledMap() {
try {
return new BpfMap<>(
DATA_SAVER_ENABLED_MAP_PATH, BpfMap.BPF_F_RDWR, S32.class, U8.class);
} catch (ErrnoException e) {
throw new IllegalStateException("Cannot open data saver enabled map", e);
}
}
private static void initBpfMaps() {
if (sConfigurationMap == null) {
sConfigurationMap = getConfigurationMap();
@ -246,6 +269,15 @@ public class BpfNetMaps {
if (sCookieTagMap == null) {
sCookieTagMap = getCookieTagMap();
}
if (sDataSaverEnabledMap == null) {
sDataSaverEnabledMap = getDataSaverEnabledMap();
}
try {
sDataSaverEnabledMap.updateEntry(DATA_SAVER_ENABLED_KEY, new U8(DATA_SAVER_DISABLED));
} catch (ErrnoException e) {
throw new IllegalStateException("Failed to initialize data saver configuration", e);
}
}
/**
@ -926,6 +958,27 @@ public class BpfNetMaps {
}
}
/**
* Set Data Saver enabled or disabled
*
* @param enable whether Data Saver is enabled or disabled.
* @throws UnsupportedOperationException if called on pre-T devices.
* @throws ServiceSpecificException in case of failure, with an error code indicating the
* cause of the failure.
*/
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
public void setDataSaverEnabled(boolean enable) {
throwIfPreT("setDataSaverEnabled is not available on pre-T devices");
try {
final short config = enable ? DATA_SAVER_ENABLED : DATA_SAVER_DISABLED;
sDataSaverEnabledMap.updateEntry(DATA_SAVER_ENABLED_KEY, new U8(config));
} catch (ErrnoException e) {
throw new ServiceSpecificException(e.errno, "Unable to set data saver: "
+ Os.strerror(e.errno));
}
}
/** Register callback for statsd to pull atom. */
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
public void setPullAtomCallback(final Context context) {
@ -1008,6 +1061,15 @@ public class BpfNetMaps {
}
}
private void dumpDataSaverConfig(final IndentingPrintWriter pw) {
try {
final short config = sDataSaverEnabledMap.getValue(DATA_SAVER_ENABLED_KEY).val;
// Any non-zero value converted from short to boolean is true by convention.
pw.println("sDataSaverEnabledMap: " + (config != DATA_SAVER_DISABLED));
} catch (ErrnoException e) {
pw.println("Failed to read data saver configuration: " + e);
}
}
/**
* Dump BPF maps
*
@ -1058,6 +1120,8 @@ public class BpfNetMaps {
});
BpfDump.dumpMap(sUidPermissionMap, pw, "sUidPermissionMap",
(uid, permission) -> uid.val + " " + permissionToString(permission.val));
dumpDataSaverConfig(pw);
pw.decreaseIndent();
}
}

View file

@ -12556,6 +12556,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
@TargetApi(Build.VERSION_CODES.TIRAMISU)
@Override
public void setDataSaverEnabled(final boolean enable) {
enforceNetworkStackOrSettingsPermission();
@ -12568,6 +12569,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
// Lack of permission or binder errors.
throw new IllegalStateException(e);
}
try {
mBpfNetMaps.setDataSaverEnabled(enable);
} catch (ServiceSpecificException | UnsupportedOperationException e) {
Log.e(TAG, "Failed to set data saver " + enable + " : " + e);
}
}
@Override

View file

@ -94,6 +94,7 @@ static const set<string> MAINLINE_FOR_T_PLUS = {
NETD "map_netd_app_uid_stats_map",
NETD "map_netd_configuration_map",
NETD "map_netd_cookie_tag_map",
NETD "map_netd_data_saver_enabled_map",
NETD "map_netd_iface_index_name_map",
NETD "map_netd_iface_stats_map",
NETD "map_netd_ingress_discard_map",

View file

@ -17,6 +17,9 @@
package com.android.server;
import static android.net.BpfNetMapsConstants.CURRENT_STATS_MAP_CONFIGURATION_KEY;
import static android.net.BpfNetMapsConstants.DATA_SAVER_ENABLED_KEY;
import static android.net.BpfNetMapsConstants.DATA_SAVER_DISABLED;
import static android.net.BpfNetMapsConstants.DATA_SAVER_ENABLED;
import static android.net.BpfNetMapsConstants.DOZABLE_MATCH;
import static android.net.BpfNetMapsConstants.HAPPY_BOX_MATCH;
import static android.net.BpfNetMapsConstants.IIF_MATCH;
@ -141,6 +144,7 @@ public final class BpfNetMapsTest {
private final IBpfMap<S32, U8> mUidPermissionMap = new TestBpfMap<>(S32.class, U8.class);
private final IBpfMap<CookieTagMapKey, CookieTagMapValue> mCookieTagMap =
spy(new TestBpfMap<>(CookieTagMapKey.class, CookieTagMapValue.class));
private final IBpfMap<S32, U8> mDataSaverEnabledMap = new TestBpfMap<>(S32.class, U8.class);
@Before
public void setUp() throws Exception {
@ -155,6 +159,8 @@ public final class BpfNetMapsTest {
BpfNetMaps.setUidOwnerMapForTest(mUidOwnerMap);
BpfNetMaps.setUidPermissionMapForTest(mUidPermissionMap);
BpfNetMaps.setCookieTagMapForTest(mCookieTagMap);
BpfNetMaps.setDataSaverEnabledMapForTest(mDataSaverEnabledMap);
mDataSaverEnabledMap.updateEntry(DATA_SAVER_ENABLED_KEY, new U8(DATA_SAVER_DISABLED));
mBpfNetMaps = new BpfNetMaps(mContext, mNetd, mDeps);
}
@ -1155,6 +1161,21 @@ public final class BpfNetMapsTest {
assertDumpContains(getDump(), "cookie=123 tag=0x789 uid=456");
}
private void doTestDumpDataSaverConfig(final short value, final boolean expected)
throws Exception {
mDataSaverEnabledMap.updateEntry(DATA_SAVER_ENABLED_KEY, new U8(value));
assertDumpContains(getDump(),
"sDataSaverEnabledMap: " + expected);
}
@Test
@IgnoreUpTo(Build.VERSION_CODES.S_V2)
public void testDumpDataSaverConfig() throws Exception {
doTestDumpDataSaverConfig(DATA_SAVER_DISABLED, false);
doTestDumpDataSaverConfig(DATA_SAVER_ENABLED, true);
doTestDumpDataSaverConfig((short) 2, true);
}
@Test
public void testGetUids() throws ErrnoException {
final int uid0 = TEST_UIDS[0];
@ -1183,4 +1204,23 @@ public final class BpfNetMapsTest {
assertThrows(expected,
() -> mBpfNetMaps.getUidsWithAllowRuleOnAllowListChain(FIREWALL_CHAIN_OEM_DENY_1));
}
@Test
@IgnoreAfter(Build.VERSION_CODES.S_V2)
public void testSetDataSaverEnabledBeforeT() {
for (boolean enable : new boolean[] {true, false}) {
assertThrows(UnsupportedOperationException.class,
() -> mBpfNetMaps.setDataSaverEnabled(enable));
}
}
@Test
@IgnoreUpTo(Build.VERSION_CODES.S_V2)
public void testSetDataSaverEnabled() throws Exception {
for (boolean enable : new boolean[] {true, false}) {
mBpfNetMaps.setDataSaverEnabled(enable);
assertEquals(enable ? DATA_SAVER_ENABLED : DATA_SAVER_DISABLED,
mDataSaverEnabledMap.getValue(DATA_SAVER_ENABLED_KEY).val);
}
}
}