Snap for 14099086 from a22d4e1061 to 25Q4-release

Change-Id: I40687ca1e6a1ea2951a6d377e9225e2cf1d1a690
This commit is contained in:
Android Build Coastguard Worker 2025-09-13 14:23:04 -07:00
commit 98c16d1361
34 changed files with 945 additions and 281 deletions

View file

@ -22,7 +22,10 @@ package {
java_defaults {
name: "TetheringApiLevel",
sdk_version: "module_current",
min_sdk_version: "30",
min_sdk_version: select(release_flag("RELEASE_DEPRECATE_MAINLINE_R_SUPPORT"), {
true: "31",
default: "30",
}),
}
java_defaults {

View file

@ -53,5 +53,8 @@ android_test {
"TetheringApiCurrentLib",
],
compile_multilib: "both",
min_sdk_version: "30",
min_sdk_version: select(release_flag("RELEASE_DEPRECATE_MAINLINE_R_SUPPORT"), {
true: "31",
default: "30",
}),
}

View file

@ -45,7 +45,10 @@ java_library {
java_defaults {
name: "TetheringTestsDefaults",
min_sdk_version: "30",
min_sdk_version: select(release_flag("RELEASE_DEPRECATE_MAINLINE_R_SUPPORT"), {
true: "31",
default: "30",
}),
srcs: [
"src/**/*.java",
"src/**/*.kt",

View file

@ -834,6 +834,7 @@ public class VcnGatewayConnection extends StateMachine {
mDeps.newUnderlyingNetworkController(
mVcnContext,
mConnectionConfig,
mId,
subscriptionGroup,
mLastSnapshot,
mUnderlyingNetworkControllerCallback);
@ -2815,11 +2816,17 @@ public class VcnGatewayConnection extends StateMachine {
public UnderlyingNetworkController newUnderlyingNetworkController(
VcnContext vcnContext,
VcnGatewayConnectionConfig connectionConfig,
int gatewayConnectionId,
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
UnderlyingNetworkControllerCallback callback) {
return new UnderlyingNetworkController(
vcnContext, connectionConfig, subscriptionGroup, snapshot, callback);
vcnContext,
connectionConfig,
gatewayConnectionId,
subscriptionGroup,
snapshot,
callback);
}
/** Builds a new IkeSession. */

View file

@ -16,7 +16,6 @@
package com.android.server.vcn.metrics;
import static com.android.server.vcn.metrics.VcnStatsLog.VCN_GATEWAY_CONNECTION_STATE_CHANGED;
import static com.android.server.vcn.metrics.VcnStatsLog.VCN_GATEWAY_CONNECTION_STATE_CHANGED__GW_TEARDOWN_REASON__GATEWAY_TEARDOWN_REASON_INTERNAL_ERROR;
import static com.android.server.vcn.metrics.VcnStatsLog.VCN_GATEWAY_CONNECTION_STATE_CHANGED__GW_TEARDOWN_REASON__GATEWAY_TEARDOWN_REASON_NETWORK_AGENT_UNWANTED;
import static com.android.server.vcn.metrics.VcnStatsLog.VCN_GATEWAY_CONNECTION_STATE_CHANGED__GW_TEARDOWN_REASON__GATEWAY_TEARDOWN_REASON_NONE;
@ -56,7 +55,7 @@ public class VcnMetrics {
/** Log an atom when a VcnGatewayConnection has entered safe mode. */
public void logEnterSafeMode(int gatewayConnectionId) {
VcnStatsLog.write(
VCN_GATEWAY_CONNECTION_STATE_CHANGED,
VcnStatsLog.VCN_GATEWAY_CONNECTION_STATE_CHANGED,
gatewayConnectionId,
GATEWAY_TEARDOWN_REASON_NONE,
true /* isInSafeMode */);
@ -125,4 +124,12 @@ public class VcnMetrics {
true /* isConnected */,
false /* isValidated */);
}
/**
* Log an atom about number of validated underlying network available for VCN network selection.
*/
public void logValidatedUnderlyingNetworkCount(int gatewayConnectionId, int count) {
VcnStatsLog.write(
VcnStatsLog.VCN_UNDERLYING_NETWORKS_STATE_CHANGED, gatewayConnectionId, count);
}
}

View file

@ -56,6 +56,7 @@ import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.modules.utils.HandlerExecutor;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.VcnContext;
import com.android.server.vcn.metrics.VcnMetrics;
import com.android.server.vcn.routeselection.UnderlyingNetworkEvaluator.NetworkEvaluatorCallback;
import java.util.ArrayList;
@ -105,19 +106,31 @@ public class UnderlyingNetworkController {
@Nullable private UnderlyingNetworkRecord mCurrentRecord;
@Nullable private UnderlyingNetworkRecord.Builder mRecordInProgress;
private final int mGatewayConnectionId;
private final VcnMetrics mVcnMetrics;
public UnderlyingNetworkController(
@NonNull VcnContext vcnContext,
@NonNull VcnGatewayConnectionConfig connectionConfig,
int gatewayConnectionId,
@NonNull ParcelUuid subscriptionGroup,
@NonNull TelephonySubscriptionSnapshot snapshot,
@NonNull UnderlyingNetworkControllerCallback cb) {
this(vcnContext, connectionConfig, subscriptionGroup, snapshot, cb, new Dependencies());
this(
vcnContext,
connectionConfig,
gatewayConnectionId,
subscriptionGroup,
snapshot,
cb,
new Dependencies());
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
UnderlyingNetworkController(
@NonNull VcnContext vcnContext,
@NonNull VcnGatewayConnectionConfig connectionConfig,
int gatewayConnectionId,
@NonNull ParcelUuid subscriptionGroup,
@NonNull TelephonySubscriptionSnapshot snapshot,
@NonNull UnderlyingNetworkControllerCallback cb,
@ -128,6 +141,8 @@ public class UnderlyingNetworkController {
mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
mCb = Objects.requireNonNull(cb, "Missing cb");
mDeps = Objects.requireNonNull(deps, "Missing deps");
mGatewayConnectionId = gatewayConnectionId;
mVcnMetrics = mDeps.newVcnMetrics();
mHandler = new Handler(mVcnContext.getLooper());
@ -468,6 +483,9 @@ public class UnderlyingNetworkController {
.getContext()
.getSystemService(TelephonyManager.class)
.unregisterTelephonyCallback(mActiveDataSubIdListener);
// Log empty validated network count left, as a clean up state.
mVcnMetrics.logValidatedUnderlyingNetworkCount(mGatewayConnectionId, 0);
}
private TreeSet<UnderlyingNetworkEvaluator> getSortedUnderlyingNetworks() {
@ -489,6 +507,7 @@ public class UnderlyingNetworkController {
}
TreeSet<UnderlyingNetworkEvaluator> sorted = getSortedUnderlyingNetworks();
mVcnMetrics.logValidatedUnderlyingNetworkCount(mGatewayConnectionId, sorted.size());
UnderlyingNetworkEvaluator candidateEvaluator = sorted.isEmpty() ? null : sorted.first();
UnderlyingNetworkRecord candidate =
@ -734,7 +753,9 @@ public class UnderlyingNetworkController {
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
/* External dependencies, for injection in tests */
public static class Dependencies {
/** Builds a new UnderlyingNetworkEvaluator. */
public UnderlyingNetworkEvaluator newUnderlyingNetworkEvaluator(
@NonNull VcnContext vcnContext,
@NonNull Network network,
@ -752,5 +773,10 @@ public class UnderlyingNetworkController {
carrierConfig,
evaluatorCallback);
}
/** Builds a new VcnMetrics. */
public VcnMetrics newVcnMetrics() {
return new VcnMetrics();
}
}
}

View file

@ -25,6 +25,7 @@ import static com.android.server.vcn.VcnTestUtils.setupIpSecManager;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.atLeastOnce;
@ -228,7 +229,7 @@ public class VcnGatewayConnectionTestBase {
doReturn(mUnderlyingNetworkController)
.when(mDeps)
.newUnderlyingNetworkController(any(), any(), any(), any(), any());
.newUnderlyingNetworkController(any(), any(), anyInt(), any(), any(), any());
doReturn(mVcnMetrics).when(mDeps).newVcnMetrics();
doReturn(mWakeLock)
.when(mDeps)

View file

@ -71,6 +71,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.VcnContext;
import com.android.server.vcn.VcnNetworkProvider;
import com.android.server.vcn.metrics.VcnMetrics;
import com.android.server.vcn.routeselection.UnderlyingNetworkController.Dependencies;
import com.android.server.vcn.routeselection.UnderlyingNetworkController.NetworkBringupCallback;
import com.android.server.vcn.routeselection.UnderlyingNetworkController.UnderlyingNetworkControllerCallback;
@ -100,6 +101,7 @@ public class UnderlyingNetworkControllerTest {
private static final int INITIAL_SUB_ID_1 = 1;
private static final int INITIAL_SUB_ID_2 = 2;
private static final int UPDATED_SUB_ID = 3;
private static final int GATEWAY_CONNECTION_ID = 1234;
private static final Set<Integer> INITIAL_SUB_IDS =
new ArraySet<>(Arrays.asList(INITIAL_SUB_ID_1, INITIAL_SUB_ID_2));
@ -164,6 +166,7 @@ public class UnderlyingNetworkControllerTest {
@Mock private UnderlyingNetworkControllerCallback mNetworkControllerCb;
@Mock private NetworkEvaluatorCallback mEvaluatorCallback;
@Mock private Network mNetwork;
@Mock private VcnMetrics mVcnMetrics;
@Spy private Dependencies mDependencies = new Dependencies();
@ -218,11 +221,13 @@ public class UnderlyingNetworkControllerTest {
doReturn(mNetworkEvaluator)
.when(mDependencies)
.newUnderlyingNetworkEvaluator(any(), any(), any(), any(), any(), any(), any());
doReturn(mVcnMetrics).when(mDependencies).newVcnMetrics();
mUnderlyingNetworkController =
new UnderlyingNetworkController(
mVcnContext,
VcnGatewayConnectionConfigTest.buildTestConfig(),
GATEWAY_CONNECTION_ID,
SUB_GROUP,
mSubscriptionSnapshot,
mNetworkControllerCb,
@ -268,6 +273,7 @@ public class UnderlyingNetworkControllerTest {
new UnderlyingNetworkController(
vcnContext,
VcnGatewayConnectionConfigTest.buildTestConfig(),
GATEWAY_CONNECTION_ID,
SUB_GROUP,
mSubscriptionSnapshot,
mNetworkControllerCb);
@ -320,6 +326,7 @@ public class UnderlyingNetworkControllerTest {
new UnderlyingNetworkController(
mVcnContext,
VcnGatewayConnectionConfigTest.buildTestConfig(networkTemplates),
GATEWAY_CONNECTION_ID,
SUB_GROUP,
mSubscriptionSnapshot,
mNetworkControllerCb);
@ -361,6 +368,7 @@ public class UnderlyingNetworkControllerTest {
new UnderlyingNetworkController(
mVcnContext,
VcnGatewayConnectionConfigTest.buildTestConfig(networkTemplates),
GATEWAY_CONNECTION_ID,
SUB_GROUP,
mSubscriptionSnapshot,
mNetworkControllerCb);
@ -515,6 +523,7 @@ public class UnderlyingNetworkControllerTest {
.unregisterNetworkCallback(any(NetworkBringupCallback.class));
verify(mConnectivityManager)
.unregisterNetworkCallback(any(UnderlyingNetworkListener.class));
verify(mVcnMetrics).logValidatedUnderlyingNetworkCount(eq(GATEWAY_CONNECTION_ID), eq(0));
}
private static UnderlyingNetworkRecord getTestNetworkRecord(
@ -780,6 +789,7 @@ public class UnderlyingNetworkControllerTest {
new UnderlyingNetworkController(
mVcnContext,
VcnGatewayConnectionConfigTest.buildTestConfig(networkTemplates),
GATEWAY_CONNECTION_ID,
SUB_GROUP,
mSubscriptionSnapshot,
mNetworkControllerCb,
@ -834,6 +844,24 @@ public class UnderlyingNetworkControllerTest {
verify(expectedEvaluator, never()).setIsSelected(eq(true), any(), any(), any(), any());
}
@Test
public void testValidatedUnderlyingNetworkCountsLogged() {
final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList();
networkTemplates.add(CELL_TEMPLATE_DUN);
networkTemplates.add(CELL_TEMPLATE_CBS);
// Bring up CBS network
UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates);
bringupNetworkAndGetEvaluator(cb, CBS_NETWORK_CAPABILITIES, networkTemplates);
verify(mVcnMetrics).logValidatedUnderlyingNetworkCount(eq(GATEWAY_CONNECTION_ID), eq(1));
// Bring up DUN network
bringupNetworkAndGetEvaluator(cb, DUN_NETWORK_CAPABILITIES, networkTemplates);
verify(mVcnMetrics).logValidatedUnderlyingNetworkCount(eq(GATEWAY_CONNECTION_ID), eq(2));
}
@Test
public void testSelectMorePreferredNetwork() {
final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList();

View file

@ -46,7 +46,7 @@ class BpfRingbufTest : public ::testing::Test {
mRingbufPath("/sys/fs/bpf/map_bpfRingbufProg_test_ringbuf") {}
void SetUp() {
if (!android::bpf::isAtLeastKernelVersion(5, 8, 0)) {
if (!android::bpf::isAtLeastKernelVersion(5, 8)) {
GTEST_SKIP() << "BPF ring buffers not supported below 5.8";
}

View file

@ -107,7 +107,7 @@ class BpfMapRO {
protected:
void abortOnMismatch(bool writable) const {
if (!mMapFd.ok()) Abort(errno, "mMapFd %d is not valid", mMapFd.get());
if (isAtLeastKernelVersion(4, 14, 0)) {
if (isAtLeastKernelVersion(4, 14)) {
int flags = bpfGetFdMapFlags(mMapFd);
if (flags < 0) Abort(errno, "bpfGetFdMapFlags fail: flags=%d", flags);
if (flags & BPF_F_WRONLY) Abort(0, "map is write-only (flags=0x%X)", flags);
@ -228,7 +228,7 @@ class BpfMapRO {
// Does not allow early termination (via f erroring out) - may be implemented with bulk api
Result<void> forAll(const function<void(const Key &)> &f) const {
// No kernel bpfmap bulk lookup api which doesn't return both keys & values.
if (isAtLeastKernelVersion(5, 10, 0)) return doBulkLookupAndMaybeDelete(/*delete*/ false,
if (isAtLeastKernelVersion(5, 10)) return doBulkLookupAndMaybeDelete(/*delete*/ false,
[&f](const Key &key, const Value &) {
f(key);
}
@ -258,7 +258,7 @@ class BpfMapRO {
// Does not allow early termination (via f erroring out) - maybe implemented with bulk api
Result<void> forAll(const function<void(const Key &, const Value &)> &f) const {
if (isAtLeastKernelVersion(5, 10, 0)) return doBulkLookupAndMaybeDelete(/*delete*/ false, f);
if (isAtLeastKernelVersion(5, 10)) return doBulkLookupAndMaybeDelete(/*delete*/ false, f);
return iterate(
[&f](const Key &key, const Value &value) -> Result<void> {
f(key, value);
@ -379,7 +379,7 @@ class BpfMap : public BpfMapRW<Key, Value> {
}
Result<Value> readAndDeleteValue(const Key& key) {
if (isAtLeastKernelVersion(5, 4, 0)) {
if (isAtLeastKernelVersion(5, 4)) {
Value value;
if (!findAndDeleteMapEntry(mMapFd, &key, &value)) return value;
if (errno == ENOENT) return ERROR_FROM_ERRNO("read&DeleteVal");
@ -423,7 +423,7 @@ class BpfMap : public BpfMapRW<Key, Value> {
// Does not allow early termination (via f erroring out) - maybe implemented with bulk api
Result<void> consume(const std::function<void(const Key&, const Value&)>& f) {
if (isAtLeastKernelVersion(5, 10, 0)) return doBulkLookupAndMaybeDelete(/*delete*/true, f);
if (isAtLeastKernelVersion(5, 10)) return doBulkLookupAndMaybeDelete(/*delete*/true, f);
Result<Key> curKey = getFirstKey();
while (curKey.ok()) {
const Result<Key> &nextKey = getNextKey(curKey.value());

View file

@ -24,6 +24,7 @@
#include <string.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/system_properties.h>
#include <sys/utsname.h>
#include <android-base/properties.h>
@ -34,19 +35,44 @@
namespace android {
namespace bpf {
const bool unreleased = (base::GetProperty("ro.build.version.codename", "REL") != "REL");
const int api_level = unreleased ? 10000 : android_get_device_api_level();
const bool isAtLeastR = (api_level >= 30);
const bool isAtLeastS = (api_level >= 31);
// Sv2 is 32
const bool isAtLeastT = (api_level >= 33);
const bool isAtLeastU = (api_level >= 34);
const bool isAtLeastV = (api_level >= 35);
const bool isAtLeast25Q2 = (api_level >= 36); // 36.0
const bool isAtLeast25Q3 = (api_level > 36); // 36.0+ (TODO: fix this!)
const bool isAtLeast25Q4 = false; // 36.1
const bool isAtLeast26Q1 = false; // 36.1+
const bool isAtLeast26Q2 = false; // 37.0
static inline int get_api_level_full() {
// This fetches/parses 'ro.build.version.sdk' system property.
const int api_level = android_get_device_api_level();
// Before Baklava/25Q2 there is no 'sdk_full'
if (api_level < 36) return api_level * 100; // 3x -> 3x00
// Fetch and parse the 'sdk_full' system property.
char value[92] = {};
if (__system_property_get("ro.build.version.sdk_full", value) < 1) abort();
int major, minor;
if (sscanf(value, "%d.%d", &major, &minor) != 2) abort();
if (major < 36 || minor < 0 || minor > 9) abort();
const int api_level_full = major * 100 + minor * 10; // 3x.y -> 3xy0
// Fetch and parse our platform provided .rc file - this provides quarterly info as well
FILE * f = fopen("/system/etc/init/netbpfload.rc", "re");
if (!f) abort();
int y, q, a, b, c;
if (fscanf(f, "# %d %d %d %d %d #", &y, &q, &a, &b, &c) != 5) abort();
if (a < 36 || b < 0 || b > 9 || c < 0 || c > 4) abort();
fclose(f);
const int api_level_fuller = a * 100 + b * 10 + c * 2; // 3x.y.z -> 3xy[2z]
const bool unreleased = (base::GetProperty("ro.build.version.codename", "REL") != "REL");
return std::max(api_level_fuller, api_level_full) + unreleased;
}
const int api_level_full = get_api_level_full();
const bool isAtLeastS = (api_level_full >= 3100); // 31
// 32 is Sv2
const bool isAtLeastT = (api_level_full >= 3300); // 33
const bool isAtLeastU = (api_level_full >= 3400); // 34
const bool isAtLeastV = (api_level_full >= 3500); // 35
const bool isAtLeast25Q2 = (api_level_full >= 3600); // 36.0
const bool isAtLeast25Q4 = (api_level_full >= 3610); // 36.1
const bool isAtLeast26Q1 = (api_level_full >= 3612); // 36.1+
const bool isAtLeast26Q2 = (api_level_full >= 3700); // 37.0
// See kernel's net/core/sock_diag.c __sock_gen_cookie()
// the implementation of which guarantees 0 will never be returned,

View file

@ -81,14 +81,14 @@ static constexpr unsigned minSupportedKernelVer =
#error "__ANDROID_API__ is pre-R"
#endif
static inline bool isAtLeastKernelVersion(unsigned major, unsigned minor, unsigned sub) {
static inline bool isAtLeastKernelVersion(unsigned major, unsigned minor, unsigned sub = 0) {
unsigned k = KVER(major, minor, sub);
if (k <= minSupportedKernelVer) return true;
return kernelVer >= k;
}
static inline bool isKernelVersion(unsigned major, unsigned minor) {
return isAtLeastKernelVersion(major, minor, 0) && !isAtLeastKernelVersion(major, minor + 1, 0);
return isAtLeastKernelVersion(major, minor) && !isAtLeastKernelVersion(major, minor + 1);
}
static inline bool __unused isLtsKernel() {

View file

@ -66,10 +66,6 @@
#include "bpf/BpfUtils.h"
#include "bpf_map_def.h"
// The following matches bpf_helpers.h, which is only for inclusion in bpf code
#define BPFLOADER_MAINLINE_S_VERSION 42u
#define BPFLOADER_MAINLINE_25Q2_VERSION 47u
using android::base::borrowed_fd;
using android::base::EndsWith;
using android::base::GetIntProperty;
@ -452,7 +448,7 @@ int readCodeSections(ElfObject& elfObj, vector<codeSection>& cs) {
}
static unsigned int sanitizeMapFlags(unsigned int map_flags) {
if (!isAtLeastKernelVersion(5, 10, 0)) map_flags &= ~BPF_F_MMAPABLE;
if (!isAtLeastKernelVersion(5, 10)) map_flags &= ~BPF_F_MMAPABLE;
return map_flags;
}
@ -464,7 +460,7 @@ static bool mapMatchesExpectations(const unique_fd& fd,
// This is because the primary failure mode we're trying to detect here
// is either a source code misconfiguration (which is likely kernel independent)
// or a newly introduced kernel feature/bug (which is unlikely to get backported to 4.9).
if (!isAtLeastKernelVersion(4, 14, 0)) return true;
if (!isAtLeastKernelVersion(4, 14)) return true;
// Assuming fd is a valid Bpf Map file descriptor then
// all the following should always succeed on a 4.14+ kernel.
@ -642,7 +638,7 @@ static int loadBtf(ElfObject &elfObj, struct btf *btf) {
if (ret) return ret;
}
if (!isAtLeastKernelVersion(5, 10, 0)) {
if (!isAtLeastKernelVersion(5, 10)) {
// Likely unnecessary on kernel 5.4 but untested.
sanitizeBtf(btf);
}
@ -778,7 +774,7 @@ static int pinMap(const borrowed_fd& fd, const struct bpf_map_def& mapDef) {
return -err;
}
if (isAtLeastKernelVersion(4, 14, 0)) {
if (isAtLeastKernelVersion(4, 14)) {
int mapId = bpfGetFdMapId(fd);
if (mapId == -1) {
const int err = errno;
@ -791,7 +787,7 @@ static int pinMap(const borrowed_fd& fd, const struct bpf_map_def& mapDef) {
}
static bool isMapTypeSupported(enum bpf_map_type type) {
if (type == BPF_MAP_TYPE_LPM_TRIE && !isAtLeastKernelVersion(4, 14, 0)) {
if (type == BPF_MAP_TYPE_LPM_TRIE && !isAtLeastKernelVersion(4, 14)) {
// On Linux Kernels older than 4.14 this map type doesn't exist - autoskip.
return false;
}
@ -799,7 +795,7 @@ static bool isMapTypeSupported(enum bpf_map_type type) {
}
static enum bpf_map_type sanitizeMapType(enum bpf_map_type type) {
if (type == BPF_MAP_TYPE_DEVMAP && !isAtLeastKernelVersion(4, 14, 0)) {
if (type == BPF_MAP_TYPE_DEVMAP && !isAtLeastKernelVersion(4, 14)) {
// On Linux Kernels older than 4.14 this map type doesn't exist, but it can kind
// of be approximated: ARRAY has the same userspace api, though it is not usable
// by the same ebpf programs. However, that's okay because the bpf_redirect_map()
@ -809,7 +805,7 @@ static enum bpf_map_type sanitizeMapType(enum bpf_map_type type) {
// Hence using an ARRAY instead of a DEVMAP simply makes life easier for userspace.
return BPF_MAP_TYPE_ARRAY;
}
if (type == BPF_MAP_TYPE_DEVMAP_HASH && !isAtLeastKernelVersion(5, 4, 0)) {
if (type == BPF_MAP_TYPE_DEVMAP_HASH && !isAtLeastKernelVersion(5, 4)) {
// On Linux Kernels older than 5.4 this map type doesn't exist, but it can kind
// of be approximated: HASH has the same userspace visible api.
// However it cannot be used by ebpf programs in the same way.
@ -823,13 +819,12 @@ static enum bpf_map_type sanitizeMapType(enum bpf_map_type type) {
return type;
}
static int createMaps(ElfObject& elfObj, vector<struct bpf_map_def>& md, vector<unique_fd>& mapFds,
const unsigned int bpfloader_ver) {
static int createMaps(ElfObject& elfObj, vector<struct bpf_map_def>& md, vector<unique_fd>& mapFds) {
int ret = 0;
vector<char> btfData;
struct btf *btf = NULL;
auto btfGuard = base::make_scope_guard([&btf] { if (btf) btf__free(btf); });
if (isAtLeastKernelVersion(4, 19, 0)) {
if (isAtLeastKernelVersion(4, 19)) {
// On Linux Kernels older than 4.18 BPF_BTF_LOAD command doesn't exist.
ret = elfObj.readSectionByName(".BTF", btfData);
if (ret) {
@ -847,14 +842,14 @@ static int createMaps(ElfObject& elfObj, vector<struct bpf_map_def>& md, vector<
}
for (unsigned i = 0; i < md.size(); i++) {
if (bpfloader_ver < md[i].bpfloader_min_ver) {
if (api_level_full < md[i].bpfloader_min_ver) {
ALOGD("skipping map %s which requires bpfloader min ver 0x%05x", md[i].name(),
md[i].bpfloader_min_ver);
mapFds.push_back(unique_fd());
continue;
}
if (bpfloader_ver >= md[i].bpfloader_max_ver) {
if (api_level_full >= md[i].bpfloader_max_ver) {
ALOGD("skipping map %s which requires bpfloader max ver 0x%05x", md[i].name(),
md[i].bpfloader_max_ver);
mapFds.push_back(unique_fd());
@ -906,7 +901,7 @@ static int createMaps(ElfObject& elfObj, vector<struct bpf_map_def>& md, vector<
.max_entries = max_entries,
.map_flags = sanitizeMapFlags(md[i].map_flags),
};
if (isAtLeastKernelVersion(4, 15, 0))
if (isAtLeastKernelVersion(4, 15))
strlcpy(req.map_name, md[i].name(), sizeof(req.map_name));
bool haveBtf = btf && isBtfSupported(type);
@ -1036,9 +1031,8 @@ static int pinProg(const borrowed_fd& fd, const struct bpf_prog_def& progDef) {
return 0;
}
static int validateProg(const borrowed_fd& fd, const char* const progPinLoc,
const unsigned int bpfloader_ver) {
if (!isAtLeastKernelVersion(4, 14, 0)) {
static int validateProg(const borrowed_fd& fd, const char* const progPinLoc) {
if (!isAtLeastKernelVersion(4, 14)) {
return 0;
}
int progId = bpfGetFdProgId(fd);
@ -1063,15 +1057,14 @@ static int validateProg(const borrowed_fd& fd, const char* const progPinLoc,
}
ALOGI("prog %s id %d len jit:%d xlat:%d", progPinLoc, progId, jitLen, xlatLen);
if (!jitLen && bpfloader_ver >= BPFLOADER_MAINLINE_25Q2_VERSION) {
if (!jitLen && api_level_full >= BPFLOADER_MAINLINE_25Q2_VERSION) {
ALOGE("Kernel eBPF JIT failure for %s", progPinLoc);
return -ENOTSUP;
}
return 0;
}
static int loadCodeSections(ElfObject& elfObj, vector<codeSection>& cs, const string& license,
const unsigned int bpfloader_ver) {
static int loadCodeSections(ElfObject& elfObj, vector<codeSection>& cs, const string& license) {
for (int i = 0; i < (int)cs.size(); i++) {
unique_fd& fd = cs[i].prog_fd;
int ret;
@ -1088,8 +1081,8 @@ static int loadCodeSections(ElfObject& elfObj, vector<codeSection>& cs, const st
if (kernelVer < cs[i].prog_def->min_kver) continue;
if (kernelVer >= cs[i].prog_def->max_kver) continue;
if (bpfloader_ver < cs[i].prog_def->bpfloader_min_ver) continue;
if (bpfloader_ver >= cs[i].prog_def->bpfloader_max_ver) continue;
if (api_level_full < cs[i].prog_def->bpfloader_min_ver) continue;
if (api_level_full >= cs[i].prog_def->bpfloader_max_ver) continue;
bool reuse = false;
if (access(cs[i].prog_def->pin_location, F_OK) == 0) {
@ -1110,7 +1103,7 @@ static int loadCodeSections(ElfObject& elfObj, vector<codeSection>& cs, const st
.log_buf = ptr_to_u64(log_buf),
.expected_attach_type = cs[i].prog_def->attach_type,
};
if (isAtLeastKernelVersion(4, 15, 0))
if (isAtLeastKernelVersion(4, 15))
strlcpy(req.prog_name, cs[i].prog_def->name(), sizeof(req.prog_name));
fd.reset(bpf(BPF_PROG_LOAD, req));
@ -1152,15 +1145,14 @@ static int loadCodeSections(ElfObject& elfObj, vector<codeSection>& cs, const st
ret = pinProg(fd, cs[i].prog_def.value());
if (ret) return ret;
}
ret = validateProg(fd, cs[i].prog_def->pin_location, bpfloader_ver);
ret = validateProg(fd, cs[i].prog_def->pin_location);
if (ret) return ret;
}
return 0;
}
static int prepareLoadMaps(const struct bpf_object* obj, const vector<struct bpf_map_def>& md,
const unsigned int bpfloader_ver) {
static int prepareLoadMaps(const struct bpf_object* obj, const vector<struct bpf_map_def>& md) {
for (unsigned i = 0; i < md.size(); i++) {
struct bpf_map* m = bpf_object__find_map_by_name(obj, md[i].name());
if (!m) {
@ -1168,9 +1160,9 @@ static int prepareLoadMaps(const struct bpf_object* obj, const vector<struct bpf
return -1;
}
if (bpfloader_ver < md[i].bpfloader_min_ver || bpfloader_ver >= md[i].bpfloader_max_ver) {
if (api_level_full < md[i].bpfloader_min_ver || api_level_full >= md[i].bpfloader_max_ver) {
ALOGD("skipping map %s: bpfloader 0x%05x is outside required range [0x%05x, 0x%05x)",
md[i].name(), bpfloader_ver,
md[i].name(), api_level_full,
md[i].bpfloader_min_ver, md[i].bpfloader_max_ver);
bpf_map__set_autocreate(m, false);
continue;
@ -1195,8 +1187,7 @@ static int prepareLoadMaps(const struct bpf_object* obj, const vector<struct bpf
return 0;
}
static int prepareLoadProgs(const struct bpf_object* obj, const vector<codeSection>& cs,
const unsigned int bpfloader_ver) {
static int prepareLoadProgs(const struct bpf_object* obj, const vector<codeSection>& cs) {
for (int i = 0; i < (int)cs.size(); i++) {
if (!cs[i].prog_def.has_value()) {
ALOGE("[%d] missing program definition! bad bpf.o build?", i);
@ -1218,11 +1209,11 @@ static int prepareLoadProgs(const struct bpf_object* obj, const vector<codeSecti
continue;
}
unsigned bpfMinVer = cs[i].prog_def->bpfloader_min_ver;
unsigned bpfMaxVer = cs[i].prog_def->bpfloader_max_ver;
if (bpfloader_ver < bpfMinVer || bpfloader_ver >= bpfMaxVer) {
int bpfMinVer = cs[i].prog_def->bpfloader_min_ver;
int bpfMaxVer = cs[i].prog_def->bpfloader_max_ver;
if (api_level_full < bpfMinVer || api_level_full >= bpfMaxVer) {
ALOGD("skipping prog %s: bpfloader 0x%05x is outside required range [0x%05x, 0x%05x)",
cs[i].prog_def->name(), bpfloader_ver, bpfMinVer, bpfMaxVer);
cs[i].prog_def->name(), api_level_full, bpfMinVer, bpfMaxVer);
bpf_program__set_autoload(prog, false);
continue;
}
@ -1261,7 +1252,7 @@ static int pinMaps(const struct bpf_object* obj, const vector<struct bpf_map_def
}
static int pinProgs(const struct bpf_object * obj,
const vector<codeSection>& cs, const unsigned int bpfloader_ver) {
const vector<codeSection>& cs) {
int ret;
for (int i = 0; i < (int)cs.size(); i++) {
@ -1283,13 +1274,13 @@ static int pinProgs(const struct bpf_object * obj,
int fd = bpf_program__fd(prog);
ret = pinProg(fd, cs[i].prog_def.value());
if (ret) return ret;
ret = validateProg(fd, cs[i].prog_def->pin_location, bpfloader_ver);
ret = validateProg(fd, cs[i].prog_def->pin_location);
if (ret) return ret;
}
return 0;
}
static int loadProgByLibbpf(const char* const elfPath, const unsigned int bpfloader_ver) {
static int loadProgByLibbpf(const char* const elfPath) {
ElfObject elfObj(elfPath);
vector<struct bpf_map_def> md;
vector<codeSection> cs;
@ -1305,30 +1296,30 @@ static int loadProgByLibbpf(const char* const elfPath, const unsigned int bpfloa
ret = elfObj.readSectionByName(".android_maps", md);
if (ret) return ret;
ret = prepareLoadMaps(obj, md, bpfloader_ver);
ret = prepareLoadMaps(obj, md);
if (ret) return ret;
ret = readCodeSections(elfObj, cs);
if (ret && ret != -ENOENT) return ret;
ret = prepareLoadProgs(obj, cs, bpfloader_ver);
ret = prepareLoadProgs(obj, cs);
if (ret) return ret;
ret = bpf_object__load(obj);
if (ret) return ret;
// On Linux Kernels older than 4.18 BPF_BTF_LOAD command doesn't exist.
if (isAtLeastKernelVersion(4, 19, 0) && bpf_object__btf_fd(obj) < 0) return -1;
if (isAtLeastKernelVersion(4, 19) && bpf_object__btf_fd(obj) < 0) return -1;
ret = pinMaps(obj, md);
if (ret) return ret;
ret = pinProgs(obj, cs, bpfloader_ver);
ret = pinProgs(obj, cs);
if (ret) return ret;
return 0;
}
int loadProg(const char* const elfPath, const unsigned int bpfloader_ver) {
int loadProg(const char* const elfPath) {
ElfObject elfObj(elfPath);
vector<char> license;
vector<codeSection> cs;
@ -1345,13 +1336,13 @@ int loadProg(const char* const elfPath, const unsigned int bpfloader_ver) {
elfPath, (char*)license.data());
}
ALOGD("BpfLoader ver 0x%05x processing ELF object %s", bpfloader_ver, elfPath);
ALOGD("Processing ELF object %s", elfPath);
ret = elfObj.readSectionByName(".android_maps", md);
if (ret == -2) ret = 0; // -2 means there were no maps to read
if (ret) return ret;
ret = createMaps(elfObj, md, mapFds, bpfloader_ver);
ret = createMaps(elfObj, md, mapFds);
if (ret) {
ALOGE("Failed to create maps: (ret=%d) in %s", ret, elfPath);
return ret;
@ -1369,7 +1360,7 @@ int loadProg(const char* const elfPath, const unsigned int bpfloader_ver) {
applyMapRelo(elfObj, md, mapFds, cs);
ret = loadCodeSections(elfObj, cs, string(license.data()), bpfloader_ver);
ret = loadCodeSections(elfObj, cs, string(license.data()));
if (ret) ALOGE("Failed to load programs, loadCodeSections ret=%d", ret);
return ret;
@ -1383,10 +1374,8 @@ static bool exists(const char* const path) {
abort(); // can only hit this if permissions (likely selinux) are screwed up
}
static bool loadObject(const unsigned int bpfloader_ver,
const char* const progPath, const bool useLibbpf = false) {
if (useLibbpf ? loadProgByLibbpf(progPath, bpfloader_ver) :
loadProg(progPath, bpfloader_ver)) {
static bool loadObject(const char* const progPath, const bool useLibbpf = false) {
if (useLibbpf ? loadProgByLibbpf(progPath) : loadProg(progPath)) {
ALOGE("Failed to load object: %s, libbpf: %d", progPath, useLibbpf);
return false;
}
@ -1397,14 +1386,14 @@ static bool loadObject(const unsigned int bpfloader_ver,
#define APEXROOT "/apex/com.android.tethering"
#define BPFROOT APEXROOT "/etc/bpf/mainline/"
static bool loadAllObjects(const unsigned int bpfloader_ver) {
bool libbpf = isAtLeast25Q3 || useLibBpf;
if (!loadObject(bpfloader_ver, BPFROOT "offload.o")) return false;
if (!loadObject(bpfloader_ver, BPFROOT "test.o", libbpf)) return false;
static bool loadAllObjects() {
bool libbpf = isAtLeast26Q1 || useLibBpf;
if (!loadObject(BPFROOT "offload.o")) return false;
if (!loadObject(BPFROOT "test.o", libbpf)) return false;
if (isAtLeastT) {
if (!loadObject(bpfloader_ver, BPFROOT "clatd.o", libbpf)) return false;
if (!loadObject(bpfloader_ver, BPFROOT "dscpPolicy.o", libbpf)) return false;
if (!loadObject(bpfloader_ver, BPFROOT "netd.o", libbpf)) return false;
if (!loadObject(BPFROOT "clatd.o", libbpf)) return false;
if (!loadObject(BPFROOT "dscpPolicy.o", libbpf)) return false;
if (!loadObject(BPFROOT "netd.o", libbpf)) return false;
}
return true;
}
@ -1590,28 +1579,15 @@ static int doLoad(char** argv, char * const envp[]) {
const bool runningAsRoot = !getuid(); // true iff U QPR3 or V+
const int first_api_level = GetIntProperty("ro.board.first_api_level", api_level);
const int first_api_level = GetIntProperty("ro.board.first_api_level", api_level_full / 100);
// last in U QPR2 beta1
const bool has_platform_bpfloader_rc = exists("/system/etc/init/bpfloader.rc");
// first in U QPR2 beta~2
const bool has_platform_netbpfload_rc = exists("/system/etc/init/netbpfload.rc");
// Version of Network BpfLoader depends on the Android OS version
unsigned int bpfloader_ver = BPFLOADER_MAINLINE_S_VERSION; // [42u]
if (isAtLeastT) ++bpfloader_ver; // [43] BPFLOADER_MAINLINE_T_VERSION
if (isAtLeastU) ++bpfloader_ver; // [44] BPFLOADER_MAINLINE_U_VERSION
if (runningAsRoot) ++bpfloader_ver; // [45] BPFLOADER_MAINLINE_U_QPR3_VERSION
if (isAtLeastV) ++bpfloader_ver; // [46] BPFLOADER_MAINLINE_V_VERSION
if (isAtLeast25Q2) ++bpfloader_ver; // [47] BPFLOADER_MAINLINE_25Q2_VERSION
if (isAtLeast25Q3) ++bpfloader_ver; // [48] BPFLOADER_MAINLINE_25Q3_VERSION
if (isAtLeast25Q4) ++bpfloader_ver; // [49] BPFLOADER_MAINLINE_25Q4_VERSION
if (isAtLeast26Q1) ++bpfloader_ver; // [50] BPFLOADER_MAINLINE_26Q1_VERSION
if (isAtLeast26Q2) ++bpfloader_ver; // [51] BPFLOADER_MAINLINE_26Q2_VERSION
ALOGI("NetBpfLoad v0.%u (%s) api:%d/%d kver:%07x (%s) libbpf: v%u.%u "
"uid:%d rc:%d%d",
bpfloader_ver, argv[0], android_get_device_api_level(), api_level,
ALOGI("NetBpfLoad (%s) api:%d/%d kver:%07x (%s) libbpf: v%u.%u uid:%d rc:%d%d",
argv[0], android_get_device_api_level(), api_level_full,
kernelVer, describeArch(), libbpf_major_version(),
libbpf_minor_version(), getuid(), has_platform_bpfloader_rc,
has_platform_netbpfload_rc);
@ -1630,41 +1606,41 @@ static int doLoad(char** argv, char * const envp[]) {
// both S and T require kernel 4.9 (and eBpf support)
// (this also guarantees 'kernelVer' isn't an invalid uninitialized 0)
if (!isAtLeastKernelVersion(4, 9, 0)) {
if (!isAtLeastKernelVersion(4, 9)) {
ALOGE("Android S & T require kernel 4.9.");
return 3;
}
// U bumps the kernel requirement up to 4.14
if (isAtLeastU && !isAtLeastKernelVersion(4, 14, 0)) {
if (isAtLeastU && !isAtLeastKernelVersion(4, 14)) {
ALOGE("Android U requires kernel 4.14.");
return 4;
}
// V bumps the kernel requirement up to 4.19
// see also: //system/netd/tests/kernel_test.cpp TestKernel419
if (isAtLeastV && !isAtLeastKernelVersion(4, 19, 0)) {
if (isAtLeastV && !isAtLeastKernelVersion(4, 19)) {
ALOGE("Android V requires kernel 4.19.");
return 5;
}
// 25Q2 bumps the kernel requirement up to 5.4
// see also: //system/netd/tests/kernel_test.cpp TestKernel54
if (isAtLeast25Q2 && !isAtLeastKernelVersion(5, 4, 0)) {
if (isAtLeast25Q2 && !isAtLeastKernelVersion(5, 4)) {
ALOGE("Android 25Q2 requires kernel 5.4.");
return 6;
}
// 25Q4 bumps the kernel requirement up to 5.10
// see also: //system/netd/tests/kernel_test.cpp TestKernel510
if (isAtLeast25Q4 && !isAtLeastKernelVersion(5, 10, 0)) {
if (isAtLeast25Q4 && !isAtLeastKernelVersion(5, 10)) {
ALOGE("Android 25Q4 requires kernel 5.10.");
return 7;
}
// Technically already required by U, but only enforce on V+
// see also: //system/netd/tests/kernel_test.cpp TestKernel64Bit
if (isAtLeastV && isKernel32Bit() && isAtLeastKernelVersion(5, 16, 0)) {
if (isAtLeastV && isKernel32Bit() && isAtLeastKernelVersion(5, 16)) {
ALOGE("Android V+ platform with 32 bit kernel version >= 5.16.0 is unsupported");
if (!isTV()) return 8;
}
@ -1675,7 +1651,7 @@ static int doLoad(char** argv, char * const envp[]) {
}
// 6.6 is highest version supported by Android V, so this is effectively W+ (sdk=36+)
if (isKernel32Bit() && isAtLeastKernelVersion(6, 7, 0)) {
if (isKernel32Bit() && isAtLeastKernelVersion(6, 7)) {
ALOGE("Android platform with 32 bit kernel version >= 6.7.0 is unsupported");
return 10;
}
@ -1740,7 +1716,7 @@ static int doLoad(char** argv, char * const envp[]) {
* Note, however, that TV and Wear devices will continue to support 32-bit userspace
* on ARM64.
*/
if (isUserspace32bit() && isAtLeastKernelVersion(6, 2, 0)) {
if (isUserspace32bit() && isAtLeastKernelVersion(6, 2)) {
// Stuff won't work reliably, but...
if (isArm() && (isTV() || isWear())) {
// exempt Arm TV or Wear devices (arm32 ABI is far less problematic than x86-32)
@ -1765,7 +1741,7 @@ static int doLoad(char** argv, char * const envp[]) {
// Since officially Android only supports LTS, 6.13+ really means 6.18+,
// and won't be supported before 2026, most likely Android 17 / 26Q2.
// 6.13+ (implying 26Q2+) requires 64-bit userspace.
if (isUserspace32bit() && isAtLeastKernelVersion(6, 13, 0)) {
if (isUserspace32bit() && isAtLeastKernelVersion(6, 13)) {
// due to previous check only reachable on Arm && (<=T kernel uprev || TV || Wear)
ALOGE("64-bit userspace required on 6.13+ kernels.");
return 14;
@ -1804,7 +1780,7 @@ static int doLoad(char** argv, char * const envp[]) {
// (this writeFile is known to fail on at least 4.19, but always defaults to 0 on
// pre-5.13, on 5.13+ it depends on CONFIG_BPF_UNPRIV_DEFAULT_OFF)
if (!writeFile("/proc/sys/kernel/unprivileged_bpf_disabled", "0\n") &&
isAtLeastKernelVersion(5, 13, 0)) return 23;
isAtLeastKernelVersion(5, 13)) return 23;
}
if (isAtLeastU) {
@ -1858,7 +1834,7 @@ static int doLoad(char** argv, char * const envp[]) {
ALOGE("bpfGetNextMapId(zero) returned %u (errno %d)", mapId, errno);
return 33;
}
} else if (isAtLeastKernelVersion(4, 14, 0)) { // implies S through U QPR2
} else if (isAtLeastKernelVersion(4, 14)) { // implies S through U QPR2
// bpfGetNext{Prog,Map}Id require 4.14+
// furthermore since we're not running as root, we're not the initial
// platform bpfloader, so there may already be some maps & programs.
@ -1883,11 +1859,11 @@ static int doLoad(char** argv, char * const envp[]) {
// and thus it should return 0 with errno == ENOENT.
ALOGE("bpfGetNextMapId(final %d) returned %d errno %d", mapId, next, errno);
if (next || errno != ENOENT) return 35;
if (isAtLeastT || isAtLeastKernelVersion(4, 20, 0)) return 36;
if (isAtLeastT || isAtLeastKernelVersion(4, 20)) return 36;
// implies Android S with 4.14 or 4.19 kernel
ALOGW("Detected kernel with invalid BPF UAPI - disabling mainline use of eBPF.");
// leave a flag that we're 'done'
if (!createDir("/sys/fs/bpf/netd_shared/mainline_done")) return 43; // same as below
if (!createDir("/sys/fs/bpf/netd_shared/mainline_done")) return 37;
return 0;
}
} else { // implies S/T with 4.9 kernel
@ -1895,27 +1871,20 @@ static int doLoad(char** argv, char * const envp[]) {
}
// Load all ELF objects, create programs and maps, and pin them
if (!loadAllObjects(bpfloader_ver)) {
if (!loadAllObjects()) {
ALOGE("=== CRITICAL FAILURE LOADING BPF PROGRAMS ===");
ALOGE("If this triggers reliably, you're probably missing kernel options or patches.");
ALOGE("If this triggers randomly, you might be hitting some memory allocation "
"problems or startup script race.");
ALOGE("--- DO NOT EXPECT SYSTEM TO BOOT SUCCESSFULLY ---");
sleep(20);
return 37;
return 38;
}
{
// Create a trivial bpf map: a two element array [int->int]
unique_fd map(createMap(BPF_MAP_TYPE_ARRAY, sizeof(int), sizeof(int), 2, 0));
int zero = 0;
int kernel_bugs = 0;
if (writeToMapEntry(map, &zero, &kernel_bugs, BPF_ANY)) {
ALOGE("Failure to write into index 0 of kernel bugs array.");
return 38;
}
int one = 1;
int value = 123;
if (writeToMapEntry(map, &one, &value, BPF_ANY)) {

View file

@ -82,12 +82,12 @@ static Status initPrograms(const char* cg2_path) {
if (!isAtLeastT) return Status("S- platform is unsupported");
// S requires eBPF support which was only added in 4.9, so this should be satisfied.
if (!isAtLeastKernelVersion(4, 9, 0)) {
if (!isAtLeastKernelVersion(4, 9)) {
return Status("kernel version < 4.9.0 is unsupported");
}
// U bumps the kernel requirement up to 4.14
if (isAtLeastU && !isAtLeastKernelVersion(4, 14, 0)) {
if (isAtLeastU && !isAtLeastKernelVersion(4, 14)) {
return Status("U+ platform with kernel version < 4.14.0 is unsupported");
}
@ -97,12 +97,12 @@ static Status initPrograms(const char* cg2_path) {
}
// V bumps the kernel requirement up to 4.19
if (isAtLeastV && !isAtLeastKernelVersion(4, 19, 0)) {
if (isAtLeastV && !isAtLeastKernelVersion(4, 19)) {
return Status("V+ platform with kernel version < 4.19.0 is unsupported");
}
// 25Q2 bumps the kernel requirement up to 5.4
if (isAtLeast25Q2 && !isAtLeastKernelVersion(5, 4, 0)) {
if (isAtLeast25Q2 && !isAtLeastKernelVersion(5, 4)) {
return Status("25Q2+ platform with kernel version < 5.4.0 is unsupported");
}
@ -121,12 +121,12 @@ static Status initPrograms(const char* cg2_path) {
// cgroup if the program is pinned properly.
// TODO: delete the if statement once all devices should support cgroup
// socket filter (ie. the minimum kernel version required is 4.14).
if (isAtLeastKernelVersion(4, 14, 0)) {
if (isAtLeastKernelVersion(4, 14)) {
RETURN_IF_NOT_OK(attachProgramToCgroup(CGROUP_INET_CREATE_PROG_PATH,
cg_fd, BPF_CGROUP_INET_SOCK_CREATE));
}
if (isAtLeastKernelVersion(5, 10, 0)) {
if (isAtLeastKernelVersion(5, 10)) {
RETURN_IF_NOT_OK(attachProgramToCgroup(CGROUP_INET_RELEASE_PROG_PATH,
cg_fd, BPF_CGROUP_INET_SOCK_RELEASE));
}
@ -134,7 +134,7 @@ static Status initPrograms(const char* cg2_path) {
if (isAtLeastV) {
// V requires 4.19+, so technically this 2nd 'if' is not required, but it
// doesn't hurt us to try to support AOSP forks that try to support older kernels.
if (isAtLeastKernelVersion(4, 19, 0)) {
if (isAtLeastKernelVersion(4, 19)) {
RETURN_IF_NOT_OK(attachProgramToCgroup(CGROUP_CONNECT4_PROG_PATH,
cg_fd, BPF_CGROUP_INET4_CONNECT));
RETURN_IF_NOT_OK(attachProgramToCgroup(CGROUP_CONNECT6_PROG_PATH,
@ -149,7 +149,7 @@ static Status initPrograms(const char* cg2_path) {
cg_fd, BPF_CGROUP_UDP6_SENDMSG));
}
if (isAtLeastKernelVersion(5, 4, 0)) {
if (isAtLeastKernelVersion(5, 4)) {
RETURN_IF_NOT_OK(attachProgramToCgroup(CGROUP_GETSOCKOPT_PROG_PATH,
cg_fd, BPF_CGROUP_GETSOCKOPT));
RETURN_IF_NOT_OK(attachProgramToCgroup(CGROUP_SETSOCKOPT_PROG_PATH,
@ -157,7 +157,7 @@ static Status initPrograms(const char* cg2_path) {
}
}
if (isAtLeastKernelVersion(4, 19, 0)) {
if (isAtLeastKernelVersion(4, 19)) {
RETURN_IF_NOT_OK(attachProgramToCgroup(CGROUP_BIND4_PROG_PATH,
cg_fd, BPF_CGROUP_INET4_BIND));
RETURN_IF_NOT_OK(attachProgramToCgroup(CGROUP_BIND6_PROG_PATH,
@ -172,14 +172,14 @@ static Status initPrograms(const char* cg2_path) {
if (queryProgram(cg_fd, BPF_CGROUP_INET6_BIND) <= 0) abort();
}
if (isAtLeastKernelVersion(5, 10, 0)) {
if (isAtLeastKernelVersion(5, 10)) {
if (queryProgram(cg_fd, BPF_CGROUP_INET_SOCK_RELEASE) <= 0) abort();
}
if (isAtLeastV) {
// V requires 4.19+, so technically this 2nd 'if' is not required, but it
// doesn't hurt us to try to support AOSP forks that try to support older kernels.
if (isAtLeastKernelVersion(4, 19, 0)) {
if (isAtLeastKernelVersion(4, 19)) {
if (queryProgram(cg_fd, BPF_CGROUP_INET4_CONNECT) <= 0) abort();
if (queryProgram(cg_fd, BPF_CGROUP_INET6_CONNECT) <= 0) abort();
if (queryProgram(cg_fd, BPF_CGROUP_UDP4_RECVMSG) <= 0) abort();
@ -188,7 +188,7 @@ static Status initPrograms(const char* cg2_path) {
if (queryProgram(cg_fd, BPF_CGROUP_UDP6_SENDMSG) <= 0) abort();
}
if (isAtLeastKernelVersion(5, 4, 0)) {
if (isAtLeastKernelVersion(5, 4)) {
if (queryProgram(cg_fd, BPF_CGROUP_GETSOCKOPT) <= 0) abort();
if (queryProgram(cg_fd, BPF_CGROUP_SETSOCKOPT) <= 0) abort();
}
@ -319,7 +319,7 @@ static void mapLockTest(void) {
Status BpfHandler::initMaps() {
// bpfLock() requires bpfGetFdMapId which is only available on 4.14+ kernels.
if (isAtLeastKernelVersion(4, 14, 0)) {
if (isAtLeastKernelVersion(4, 14)) {
mapLockTest();
}
@ -378,7 +378,7 @@ int BpfHandler::tagSocket(int sockFd, uint32_t tag, uid_t chargeUid, uid_t realU
// On 5.10+ the BPF_CGROUP_INET_SOCK_RELEASE hook takes care of cookie tag map cleanup
// during socket destruction. As such the socket destroy listener is superfluous.
if (!isAtLeastKernelVersion(5, 10, 0)) {
if (!isAtLeastKernelVersion(5, 10)) {
int socketProto;
socklen_t protoLen = sizeof(socketProto);
if (getsockopt(sockFd, SOL_SOCKET, SO_PROTOCOL, &socketProto, &protoLen)) {

View file

@ -191,7 +191,7 @@ TEST_F(BpfHandlerTest, TestTagSocketWithUnsupportedProtocol) {
int rawSocket = socket(AF_INET, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW);
EXPECT_LE(0, rawSocket);
EXPECT_NE(NONEXISTENT_COOKIE, getSocketCookie(rawSocket));
if (isAtLeastKernelVersion(5, 10, 0)) {
if (isAtLeastKernelVersion(5, 10)) {
EXPECT_EQ(0, mBh.tagSocket(rawSocket, TEST_TAG, TEST_UID, TEST_UID));
} else {
EXPECT_EQ(-EPROTONOSUPPORT, mBh.tagSocket(rawSocket, TEST_TAG, TEST_UID, TEST_UID));

View file

@ -6,37 +6,6 @@
#include "bpf_map_def.h"
// Android Mainline BpfLoader when running on Android S (sdk=31)
// Note: this value (and the following +1u's) are hardcoded in NetBpfLoad.cpp
#define BPFLOADER_MAINLINE_S_VERSION 42u
// Android Mainline BpfLoader when running on Android T (sdk=33)
#define BPFLOADER_MAINLINE_T_VERSION (BPFLOADER_MAINLINE_S_VERSION + 1u)
// Android Mainline BpfLoader when running on Android U (sdk=34)
#define BPFLOADER_MAINLINE_U_VERSION (BPFLOADER_MAINLINE_T_VERSION + 1u)
// Android Mainline BpfLoader when running on Android U QPR3
#define BPFLOADER_MAINLINE_U_QPR3_VERSION (BPFLOADER_MAINLINE_U_VERSION + 1u)
// Android Mainline BpfLoader when running on Android V (sdk=35)
#define BPFLOADER_MAINLINE_V_VERSION (BPFLOADER_MAINLINE_U_QPR3_VERSION + 1u)
// Android Mainline BpfLoader when running on Android 25Q2 (sdk=36 aka 36.0)
#define BPFLOADER_MAINLINE_25Q2_VERSION (BPFLOADER_MAINLINE_V_VERSION + 1u)
// Android Mainline BpfLoader when running on Android 25Q3 (sdk 36.0+)
#define BPFLOADER_MAINLINE_25Q3_VERSION (BPFLOADER_MAINLINE_25Q2_VERSION + 1u)
// Android Mainline BpfLoader when running on Android 25Q4 (sdk 36.1)
#define BPFLOADER_MAINLINE_25Q4_VERSION (BPFLOADER_MAINLINE_25Q3_VERSION + 1u)
// Android Mainline BpfLoader when running on Android 26Q1 (sdk 36.1+)
#define BPFLOADER_MAINLINE_26Q1_VERSION (BPFLOADER_MAINLINE_25Q4_VERSION + 1u)
// Android Mainline BpfLoader when running on Android 26Q2 (sdk 37.0)
#define BPFLOADER_MAINLINE_26Q2_VERSION (BPFLOADER_MAINLINE_26Q1_VERSION + 1u)
/* You should #define BPFLOADER_{MIN/MAX}_VER before #include "bpf_helpers.h"
* to change which bpfloaders will process the resulting .o file.
*/

View file

@ -138,8 +138,8 @@ struct bpf_map_def {
unsigned int gid; // gid_t
unsigned int mode; // mode_t
unsigned int bpfloader_min_ver;
unsigned int bpfloader_max_ver;
int bpfloader_min_ver;
int bpfloader_max_ver;
// kernelVer must be >= min_kver and < max_kver
unsigned int min_kver;
@ -181,8 +181,8 @@ struct bpf_prog_def {
char pad0[3]; // manually pad up to 4 byte alignment, may be used for extensions in the future
unsigned int bpfloader_min_ver;
unsigned int bpfloader_max_ver;
int bpfloader_min_ver;
int bpfloader_max_ver;
char create_location[BPF_DEF_CHAR_ARRAY_SIZE];
char pin_location[BPF_DEF_CHAR_ARRAY_SIZE];
@ -203,3 +203,13 @@ static_assert(std::is_standard_layout_v<struct bpf_prog_def>);
_Static_assert(sizeof(struct bpf_prog_def) == 40 + 2 * BPF_DEF_CHAR_ARRAY_SIZE, "wrong sizeof struct bpf_prog_def");
_Static_assert(__alignof__(struct bpf_prog_def) == 4, "__alignof__ struct bpf_prog_def != 4");
_Static_assert(_Alignof(struct bpf_prog_def) == 4, "_Alignof struct bpf_prog_def != 4");
// Android Mainline BpfLoader version when running on:
#define BPFLOADER_MAINLINE_S_VERSION 3100 // Android S (31)
#define BPFLOADER_MAINLINE_T_VERSION 3300 // Android T (33)
#define BPFLOADER_MAINLINE_U_VERSION 3400 // Android U (34)
#define BPFLOADER_MAINLINE_V_VERSION 3500 // Android V (35)
#define BPFLOADER_MAINLINE_25Q2_VERSION 3600 // Android 25Q2 (36.0)
#define BPFLOADER_MAINLINE_25Q4_VERSION 3610 // Android 25Q4 (36.1)
#define BPFLOADER_MAINLINE_26Q1_VERSION 3612 // Android 26Q1 (36.1+)
#define BPFLOADER_MAINLINE_26Q2_VERSION 3700 // Android 26Q2 (37.0)

View file

@ -32,7 +32,6 @@ using std::set;
using std::string;
using android::bpf::isAtLeastKernelVersion;
using android::bpf::isAtLeastR;
using android::bpf::isAtLeastS;
using android::bpf::isAtLeastT;
using android::bpf::isAtLeastU;
@ -203,7 +202,7 @@ TEST_F(BpfExistenceTest, TestPrograms) {
ASSERT_TRUE(isAtLeastS); // Q & R are no longer supported by mainline
// S requires Linux Kernel 4.9+ and thus requires eBPF support.
if (isAtLeastS) ASSERT_TRUE(isAtLeastKernelVersion(4, 9, 0));
if (isAtLeastS) ASSERT_TRUE(isAtLeastKernelVersion(4, 9));
// on S without a new enough DnsResolver apex, NetBpfLoad doesn't get triggered,
// and thus no mainline programs get loaded.
@ -215,28 +214,28 @@ TEST_F(BpfExistenceTest, TestPrograms) {
// T still only requires Linux Kernel 4.9+.
DO_EXPECT(isAtLeastT, MAINLINE_FOR_T_PLUS);
DO_EXPECT(isAtLeastT && isAtLeastKernelVersion(4, 14, 0), MAINLINE_FOR_T_4_14_PLUS);
DO_EXPECT(isAtLeastT && isAtLeastKernelVersion(4, 19, 0), MAINLINE_FOR_T_4_19_PLUS);
DO_EXPECT(isAtLeastT && isAtLeastKernelVersion(5, 10, 0), MAINLINE_FOR_T_5_10_PLUS);
DO_EXPECT(isAtLeastT && isAtLeastKernelVersion(5, 15, 0), MAINLINE_FOR_T_5_15_PLUS);
DO_EXPECT(isAtLeastT && isAtLeastKernelVersion(4, 14), MAINLINE_FOR_T_4_14_PLUS);
DO_EXPECT(isAtLeastT && isAtLeastKernelVersion(4, 19), MAINLINE_FOR_T_4_19_PLUS);
DO_EXPECT(isAtLeastT && isAtLeastKernelVersion(5, 10), MAINLINE_FOR_T_5_10_PLUS);
DO_EXPECT(isAtLeastT && isAtLeastKernelVersion(5, 15), MAINLINE_FOR_T_5_15_PLUS);
// U requires Linux Kernel 4.14+, but nothing (as yet) added or removed in U.
if (isAtLeastU) ASSERT_TRUE(isAtLeastKernelVersion(4, 14, 0));
if (isAtLeastU) ASSERT_TRUE(isAtLeastKernelVersion(4, 14));
DO_EXPECT(isAtLeastU, MAINLINE_FOR_U_PLUS);
DO_EXPECT(isAtLeastU && isAtLeastKernelVersion(5, 10, 0), MAINLINE_FOR_U_5_10_PLUS);
DO_EXPECT(isAtLeastU && isAtLeastKernelVersion(5, 10), MAINLINE_FOR_U_5_10_PLUS);
// V requires Linux Kernel 4.19+, but nothing (as yet) added or removed in V.
if (isAtLeastV) ASSERT_TRUE(isAtLeastKernelVersion(4, 19, 0));
if (isAtLeastV) ASSERT_TRUE(isAtLeastKernelVersion(4, 19));
DO_EXPECT(isAtLeastV, MAINLINE_FOR_V_PLUS);
DO_EXPECT(isAtLeastV && isAtLeastKernelVersion(5, 4, 0), MAINLINE_FOR_V_5_4_PLUS);
DO_EXPECT(isAtLeastV && isAtLeastKernelVersion(5, 4), MAINLINE_FOR_V_5_4_PLUS);
if (isAtLeast25Q2) ASSERT_TRUE(isAtLeastKernelVersion(5, 4, 0));
if (isAtLeast25Q2) ASSERT_TRUE(isAtLeastKernelVersion(5, 4));
DO_EXPECT(isAtLeast25Q2, MAINLINE_FOR_25Q2_PLUS);
if (isAtLeast25Q4) ASSERT_TRUE(isAtLeastKernelVersion(5, 10, 0));
if (isAtLeast25Q4) ASSERT_TRUE(isAtLeastKernelVersion(5, 10));
DO_EXPECT(isAtLeast26Q2, MAINLINE_FOR_26Q2_PLUS);
DO_EXPECT(isAtLeast26Q2 && isAtLeastKernelVersion(6, 1, 0), MAINLINE_FOR_26Q2_6_1_PLUS);
DO_EXPECT(isAtLeast26Q2 && isAtLeastKernelVersion(6, 1), MAINLINE_FOR_26Q2_6_1_PLUS);
for (const auto& file : mustExist) {
EXPECT_EQ(0, access(file.c_str(), R_OK)) << file << " does not exist";

View file

@ -19,6 +19,8 @@ package android.net;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
import static com.android.net.module.util.BitUtils.appendStringRepresentationOfBitMaskToStringBuilder;
import static com.android.net.module.util.BitUtils.describeDifferences;
import static com.android.net.module.util.FrameworkConnectivityStatsLog.CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED;
import static com.android.net.module.util.FrameworkConnectivityStatsLog.CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_SPECIFIER_REDACTION_REFLECTION_ERROR;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
@ -37,6 +39,7 @@ import android.os.Process;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
import android.util.LruCache;
import android.util.Range;
import com.android.internal.annotations.VisibleForTesting;
@ -44,15 +47,19 @@ import com.android.modules.utils.build.SdkLevel;
import com.android.net.flags.Flags;
import com.android.net.module.util.BitUtils;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.FrameworkConnectivityStatsLog;
import com.android.net.module.util.NetworkCapabilitiesUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.StringJoiner;
@ -169,6 +176,15 @@ public final class NetworkCapabilities implements Parcelable {
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final long REDACT_FOR_NETWORK_SETTINGS = 1 << 2;
/**
* Redact any fields that need {@link android.Manifest.permission#THREAD_NETWORK_PRIVILEGED}
* permission since the receiving app does not hold this permission.
*
* @see android.Manifest.permission#THREAD_NETWORK_PRIVILEGED
* @hide for only {@code ThreadNetworkSpecifier} which is in the same module
*/
public static final long REDACT_FOR_THREAD_NETWORK_PRIVILEGED = 1 << 3;
/**
* Redact all fields in this object that require any relevant permission.
* @hide
@ -182,6 +198,7 @@ public final class NetworkCapabilities implements Parcelable {
REDACT_FOR_ACCESS_FINE_LOCATION,
REDACT_FOR_LOCAL_MAC_ADDRESS,
REDACT_FOR_NETWORK_SETTINGS,
REDACT_FOR_THREAD_NETWORK_PRIVILEGED,
REDACT_ALL
})
@Retention(RetentionPolicy.SOURCE)
@ -335,6 +352,11 @@ public final class NetworkCapabilities implements Parcelable {
if (mTransportInfo != null) {
mTransportInfo = nc.mTransportInfo.makeCopy(redactions);
}
if (mNetworkSpecifier != null) {
mNetworkSpecifier =
(NetworkSpecifier) RedactionHelper.getSpecifierRedactResult(
mNetworkSpecifier, redactions);
}
}
/**
@ -3005,13 +3027,14 @@ public final class NetworkCapabilities implements Parcelable {
* @hide
*/
public @RedactionType long getApplicableRedactions() {
// Currently, there are no fields redacted in NetworkCapabilities itself, so we just
// passthrough the redactions required by the embedded TransportInfo. If this changes
// in the future, modify this method.
if (mTransportInfo == null) {
return NetworkCapabilities.REDACT_NONE;
long redactions = REDACT_NONE;
if (mTransportInfo != null) {
redactions |= mTransportInfo.getApplicableRedactions();
}
return mTransportInfo.getApplicableRedactions();
if (mNetworkSpecifier != null) {
redactions |= RedactionHelper.getSpecifierApplicableRedactions(mNetworkSpecifier);
}
return redactions;
}
private NetworkCapabilities removeDefaultCapabilites() {
@ -3509,4 +3532,148 @@ public final class NetworkCapabilities implements Parcelable {
return new NetworkCapabilities(mCaps);
}
}
/**
* Helper class for providing access to {@link NetworkSpecifier} redaction methods via
* reflection.
*
* Reflection is necessary because NetworkSpecifier is platform code. So it's possible for a
* mainline update to define a getApplicableRedactions() or redact(long) method in a subclass
* of NetworkSpecifier (e.g., EthernetNetworkSpecifier), and then to run that mainline module
* on an Android release where the NetworkSpecifier base class does not have those methods.
* On such a device, ConnectivityService must still call those methods if they exist, otherwise
* it would fail to redact information that needs to be redacted. Another way to do this would
* be for ConnectivityService to just call the method and catch NoSuchMethodError, but this
* code runs fairly often and constructing exceptions is expensive.
*
* @hide
*/
public static final class RedactionHelper {
// LruCache is thread-safe so that no locks are used to project it this class
private static final LruCache<Class<? extends NetworkSpecifier>, Optional<Method>>
sGetApplicableRedactionsMethodCache = new LruCache<>(100);
private static final LruCache<Class<? extends NetworkSpecifier>, Optional<Method>>
sRedactLongMethodCache = new LruCache<>(100);
private RedactionHelper() {}
/**
* Returns the {@link Method} of the {@link NetworkSpecifier} class for the given method
* name, or {@code null} if the method doesn't exist.
* <p>
* This is typically used for {@link NetworkSpecifier#getApplicableRedactions()} and {@link
* NetworkSpecifier#redact(long)} to work with new NetworkSpecifier subclasses added in new
* mainline version but deployed to older platforms which don't have those two
* NetworkSpecifier methods.
* <p>
* See {@link #createWithSensitiveInfoSanitizedIfNecessaryWhenParceled} and {@link
* #ensureSufficientPermissionsForRequest} for how this method should be used.
*/
@Nullable
private static Method getNetworkSpecifierMethod(
Class<? extends NetworkSpecifier> clazz, String name, Class<?> returnType,
Class<?>... parameterTypes) {
// Use {@code getMethods()} rather than {@code getDeclaredMethods()} so that it can
// return the inherited NetworkSpecifier methods for those existing subclasses which
// don't override {@link NetworkSpecifier#getApplicableRedactions()} and {@link
// NetworkSpecifier#redact(long)}.
// Note also {@code getMethods()} returns only public methods, which is necessary for
// code to call the method later.
for (Method method : clazz.getMethods()) {
if (name.equals(method.getName())
&& returnType.equals(method.getReturnType())
&& Arrays.equals(parameterTypes, method.getParameterTypes())) {
return method;
}
}
return null;
}
/**
* Returns the {@link NetworkSpecifier#getApplicableRedactions()} method or {@code null} if
* it doesn't exist for the given {@link NetworkSpecifier} subclass.
*/
@Nullable
private static Method getApplicableRedactionsMethod(
Class<? extends NetworkSpecifier> clazz) {
// If this method is called concurrently from two threads, both may see cache missed
// and the reflection branch in method will be executed for twice. But this is okay
// because getApplicableRedactions() itself has no side-effect (and this is why we can
// create caches for it).
Optional<Method> cache = sGetApplicableRedactionsMethodCache.get(clazz);
if (cache != null) {
return cache.orElse(null);
}
Method method = getNetworkSpecifierMethod(
clazz, "getApplicableRedactions", long.class);
sGetApplicableRedactionsMethodCache.put(clazz, Optional.ofNullable(method));
return method;
}
/**
* Returns the value of {@link NetworkSpecifier#getApplicableRedactions()} or
* {@link REDACT_NONE} if the platform doesn't support
* {@link NetworkSpecifier#getApplicableRedactions()}.
*/
public static long getSpecifierApplicableRedactions(NetworkSpecifier specifier) {
final Method getApplicableRedactions =
getApplicableRedactionsMethod(specifier.getClass());
if (getApplicableRedactions == null) {
return REDACT_NONE;
}
try {
return (long) getApplicableRedactions.invoke(specifier);
} catch (IllegalAccessException | InvocationTargetException
| IllegalArgumentException e) {
FrameworkConnectivityStatsLog.write(CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED,
CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_SPECIFIER_REDACTION_REFLECTION_ERROR);
Log.wtf(TAG, "Un expected reflection error on specifier " + specifier, e);
return REDACT_ALL;
}
}
/**
* Returns the {@link NetworkSpecifier#redact(long)} method or {@code null} if it doesn't
* exist for the given {@link NetworkSpecifier} subclass.
*/
@Nullable
private static Method getRedactLongMethod(Class<? extends NetworkSpecifier> clazz) {
// If this method is called concurrently from two threads, both may see cache missed
// and the reflection branch in method will be executed for twice. But this is okay
// because redact(long) itself has no side-effect (and this is why we can create caches
// for it).
Optional<Method> cache = sRedactLongMethodCache.get(clazz);
if (cache != null) {
return cache.orElse(null);
}
Method method = getNetworkSpecifierMethod(
clazz, "redact", NetworkSpecifier.class, long.class);
sRedactLongMethodCache.put(clazz, Optional.ofNullable(method));
return method;
}
/**
* Returns the result of {@link NetworkSpecifier#redact(long)} or {@code specifier} itself
* if the platform doesn't support {@link NetworkSpecifier#redact(long)}.
*/
@Nullable
public static NetworkSpecifier getSpecifierRedactResult(
NetworkSpecifier specifier, long redactions) {
final Method redactLong = getRedactLongMethod(specifier.getClass());
if (redactLong == null) {
return specifier;
}
try {
return (NetworkSpecifier) redactLong.invoke(specifier, redactions);
} catch (IllegalAccessException | InvocationTargetException
| IllegalArgumentException e) {
FrameworkConnectivityStatsLog.write(CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED,
CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_SPECIFIER_REDACTION_REFLECTION_ERROR);
Log.wtf(TAG, "Un expected reflection error on specifier " + specifier, e);
return null;
}
}
}
}

View file

@ -50,6 +50,9 @@ public interface TransportInfo {
* </ul>
* </p>
*
* <p> Subclasses should silently ignore redactions which are not included in {@link
* #getApplicableRedactions()}.
*
* @param redactions bitmask of redactions that needs to be performed on this instance.
* @return Copy of this instance with the necessary redactions.
* @hide

Some files were not shown because too many files have changed in this diff Show more