Merge "register callback to pull NetworkBpfMapInfo atom"

This commit is contained in:
Motomu Utsumi 2022-09-09 08:11:37 +00:00 • committed by Gerrit Code Review
commit 3dc2fb5378
4 changed files with 112 additions and 1 deletions

View file

@ -143,6 +143,7 @@ java_library {
"src/**/*.java",
":framework-connectivity-shared-srcs",
":services-connectivity-shared-srcs",
":statslog-connectivity-java-gen",
],
libs: [
"framework-annotations-lib",
@ -152,6 +153,7 @@ java_library {
"framework-wifi.stubs.module_lib",
"unsupportedappusage",
"ServiceConnectivityResources",
"framework-statsd.stubs.module_lib",
],
static_libs: [
// Do not add libs here if they are already included
@ -332,3 +334,10 @@ java_genrule {
"--output $(out)",
visibility: ["//visibility:private"],
}
genrule {
name: "statslog-connectivity-java-gen",
tools: ["stats-log-api-gen"],
cmd: "$(location stats-log-api-gen) --java $(out) --module connectivity --javaPackage com.android.server --javaClass ConnectivityStatsLog",
out: ["com/android/server/ConnectivityStatsLog.java"],
}

View file

@ -33,6 +33,9 @@ import static android.system.OsConstants.ENODEV;
import static android.system.OsConstants.ENOENT;
import static android.system.OsConstants.EOPNOTSUPP;
import static com.android.server.ConnectivityStatsLog.NETWORK_BPF_MAP_INFO;
import android.app.StatsManager;
import android.content.Context;
import android.net.INetd;
import android.os.RemoteException;
@ -42,12 +45,15 @@ import android.system.ErrnoException;
import android.system.Os;
import android.util.ArraySet;
import android.util.Log;
import android.util.StatsEvent;
import com.android.internal.annotations.VisibleForTesting;
import com.android.modules.utils.BackgroundThread;
import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.BpfMap;
import com.android.net.module.util.DeviceConfigUtils;
import com.android.net.module.util.IBpfMap;
import com.android.net.module.util.Struct;
import com.android.net.module.util.Struct.U32;
import com.android.net.module.util.Struct.U8;
import com.android.net.module.util.bpf.CookieTagMapKey;
@ -55,6 +61,7 @@ import com.android.net.module.util.bpf.CookieTagMapValue;
import java.io.FileDescriptor;
import java.io.IOException;
import java.util.List;
import java.util.Set;
/**
@ -274,6 +281,15 @@ public class BpfNetMaps {
public int synchronizeKernelRCU() {
return native_synchronizeKernelRCU();
}
/**
* Build Stats Event for NETWORK_BPF_MAP_INFO atom
*/
public StatsEvent buildStatsEvent(final int cookieTagMapSize, final int uidOwnerMapSize,
final int uidPermissionMapSize) {
return ConnectivityStatsLog.buildStatsEvent(NETWORK_BPF_MAP_INFO, cookieTagMapSize,
uidOwnerMapSize, uidPermissionMapSize);
}
}
/** Constructor used after T that doesn't need to use netd anymore. */
@ -849,6 +865,43 @@ public class BpfNetMaps {
}
}
/** Register callback for statsd to pull atom. */
public void setPullAtomCallback(final Context context) {
throwIfPreT("setPullAtomCallback is not available on pre-T devices");
final StatsManager statsManager = context.getSystemService(StatsManager.class);
statsManager.setPullAtomCallback(NETWORK_BPF_MAP_INFO, null /* metadata */,
BackgroundThread.getExecutor(), this::pullBpfMapInfoAtom);
}
private <K extends Struct, V extends Struct> int getMapSize(IBpfMap<K, V> map)
throws ErrnoException {
// forEach could restart iteration from the beginning if there is a concurrent entry
// deletion. netd and skDestroyListener could delete CookieTagMap entry concurrently.
// So using Set to count the number of entry in the map.
Set<K> keySet = new ArraySet<>();
map.forEach((k, v) -> keySet.add(k));
return keySet.size();
}
/** Callback for StatsManager#setPullAtomCallback */
@VisibleForTesting
public int pullBpfMapInfoAtom(final int atomTag, final List<StatsEvent> data) {
if (atomTag != NETWORK_BPF_MAP_INFO) {
Log.e(TAG, "Unexpected atom tag: " + atomTag);
return StatsManager.PULL_SKIP;
}
try {
data.add(mDeps.buildStatsEvent(getMapSize(sCookieTagMap), getMapSize(sUidOwnerMap),
getMapSize(sUidPermissionMap)));
} catch (ErrnoException e) {
Log.e(TAG, "Failed to pull NETWORK_BPF_MAP_INFO atom: " + e);
return StatsManager.PULL_SKIP;
}
return StatsManager.PULL_SUCCESS;
}
/**
* Dump BPF maps
*

View file

@ -3040,6 +3040,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (!ConnectivitySettingsManager.getMobileDataPreferredUids(mContext).isEmpty()) {
updateMobileDataPreferredUids();
}
// On T+ devices, register callback for statsd to pull NETWORK_BPF_MAP_INFO atom
if (SdkLevel.isAtLeastT()) {
mBpfNetMaps.setPullAtomCallback(mContext);
}
}
/**

View file

@ -30,6 +30,7 @@ import static android.net.INetd.PERMISSION_INTERNET;
import static android.net.INetd.PERMISSION_NONE;
import static android.net.INetd.PERMISSION_UNINSTALLED;
import static android.net.INetd.PERMISSION_UPDATE_DEVICE_STATS;
import static android.system.OsConstants.EINVAL;
import static android.system.OsConstants.EPERM;
import static com.android.server.BpfNetMaps.DOZABLE_MATCH;
@ -40,6 +41,7 @@ import static com.android.server.BpfNetMaps.NO_MATCH;
import static com.android.server.BpfNetMaps.PENALTY_BOX_MATCH;
import static com.android.server.BpfNetMaps.POWERSAVE_MATCH;
import static com.android.server.BpfNetMaps.RESTRICTED_MATCH;
import static com.android.server.ConnectivityStatsLog.NETWORK_BPF_MAP_INFO;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@ -47,13 +49,18 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.app.StatsManager;
import android.content.Context;
import android.net.INetd;
import android.os.Build;
import android.os.ServiceSpecificException;
import android.system.ErrnoException;
import androidx.test.filters.SmallTest;
@ -76,6 +83,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
@RunWith(DevSdkIgnoreRunner.class)
@ -120,7 +128,7 @@ public final class BpfNetMapsTest {
new TestBpfMap<>(U32.class, UidOwnerValue.class);
private final IBpfMap<U32, U8> mUidPermissionMap = new TestBpfMap<>(U32.class, U8.class);
private final IBpfMap<CookieTagMapKey, CookieTagMapValue> mCookieTagMap =
new TestBpfMap<>(CookieTagMapKey.class, CookieTagMapValue.class);
spy(new TestBpfMap<>(CookieTagMapKey.class, CookieTagMapValue.class));
@Before
public void setUp() throws Exception {
@ -882,4 +890,40 @@ public final class BpfNetMapsTest {
assertThrows(ServiceSpecificException.class, () -> mBpfNetMaps.swapActiveStatsMap());
}
@Test
@IgnoreUpTo(Build.VERSION_CODES.S_V2)
public void testPullBpfMapInfo() throws Exception {
// mCookieTagMap has 1 entry
mCookieTagMap.updateEntry(new CookieTagMapKey(0), new CookieTagMapValue(0, 0));
// mUidOwnerMap has 2 entries
mUidOwnerMap.updateEntry(new U32(0), new UidOwnerValue(0, 0));
mUidOwnerMap.updateEntry(new U32(1), new UidOwnerValue(0, 0));
// mUidPermissionMap has 3 entries
mUidPermissionMap.updateEntry(new U32(0), new U8((short) 0));
mUidPermissionMap.updateEntry(new U32(1), new U8((short) 0));
mUidPermissionMap.updateEntry(new U32(2), new U8((short) 0));
final int ret = mBpfNetMaps.pullBpfMapInfoAtom(NETWORK_BPF_MAP_INFO, new ArrayList<>());
assertEquals(StatsManager.PULL_SUCCESS, ret);
verify(mDeps).buildStatsEvent(
1 /* cookieTagMapSize */, 2 /* uidOwnerMapSize */, 3 /* uidPermissionMapSize */);
}
@Test
@IgnoreUpTo(Build.VERSION_CODES.S_V2)
public void testPullBpfMapInfoGetMapSizeFailure() throws Exception {
doThrow(new ErrnoException("", EINVAL)).when(mCookieTagMap).forEach(any());
final int ret = mBpfNetMaps.pullBpfMapInfoAtom(NETWORK_BPF_MAP_INFO, new ArrayList<>());
assertEquals(StatsManager.PULL_SKIP, ret);
}
@Test
@IgnoreUpTo(Build.VERSION_CODES.S_V2)
public void testPullBpfMapInfoUnexpectedAtomTag() {
final int ret = mBpfNetMaps.pullBpfMapInfoAtom(-1 /* atomTag */, new ArrayList<>());
assertEquals(StatsManager.PULL_SKIP, ret);
}
}