Merge "Test recoverable Ikev2 errors" into tm-qpr-dev
diff --git a/bpf_progs/Android.bp b/bpf_progs/Android.bp
index 4fc678f..9e516bf 100644
--- a/bpf_progs/Android.bp
+++ b/bpf_progs/Android.bp
@@ -25,8 +25,14 @@
name: "bpf_connectivity_headers",
vendor_available: false,
host_supported: false,
- header_libs: ["bpf_headers"],
- export_header_lib_headers: ["bpf_headers"],
+ header_libs: [
+ "bpf_headers",
+ "netd_mainline_headers",
+ ],
+ export_header_lib_headers: [
+ "bpf_headers",
+ "netd_mainline_headers",
+ ],
export_include_dirs: ["."],
cflags: [
"-Wall",
@@ -37,11 +43,8 @@
apex_available: [
"//apex_available:platform",
"com.android.tethering",
- ],
+ ],
visibility: [
- // TODO: remove it when NetworkStatsService is moved into the mainline module and no more
- // calls to JNI in libservices.core.
- "//frameworks/base/services/core/jni",
"//packages/modules/Connectivity/netd",
"//packages/modules/Connectivity/service",
"//packages/modules/Connectivity/service/native/libs/libclat",
@@ -50,7 +53,6 @@
"//packages/modules/Connectivity/tests/native",
"//packages/modules/Connectivity/service-t/native/libs/libnetworkstats",
"//packages/modules/Connectivity/tests/unit/jni",
- "//system/netd/server",
"//system/netd/tests",
],
}
@@ -103,21 +105,17 @@
"-Wall",
"-Werror",
],
- include_dirs: [
- "frameworks/libs/net/common/netd/libnetdutils/include",
- ],
sub_dir: "net_shared",
}
bpf {
+ // WARNING: Android T's non-updatable netd depends on 'netd' string for xt_bpf programs it loads
name: "netd.o",
srcs: ["netd.c"],
cflags: [
"-Wall",
"-Werror",
],
- include_dirs: [
- "frameworks/libs/net/common/netd/libnetdutils/include",
- ],
+ // WARNING: Android T's non-updatable netd depends on 'netd_shared' string for xt_bpf programs
sub_dir: "netd_shared",
}
diff --git a/bpf_progs/block.c b/bpf_progs/block.c
index 601b932..f2a3e62 100644
--- a/bpf_progs/block.c
+++ b/bpf_progs/block.c
@@ -19,8 +19,8 @@
#include <netinet/in.h>
#include <stdint.h>
-// The resulting .o needs to load on the Android T bpfloader v0.12+
-#define BPFLOADER_MIN_VER 12u
+// The resulting .o needs to load on the Android T beta 3 bpfloader
+#define BPFLOADER_MIN_VER BPFLOADER_T_BETA3_VERSION
#include "bpf_helpers.h"
diff --git a/bpf_progs/bpf_shared.h b/bpf_progs/bpf_shared.h
index 706dd1d..fd449a3 100644
--- a/bpf_progs/bpf_shared.h
+++ b/bpf_progs/bpf_shared.h
@@ -21,6 +21,11 @@
#include <linux/in.h>
#include <linux/in6.h>
+#ifdef __cplusplus
+#include <string_view>
+#include "XtBpfProgLocations.h"
+#endif
+
// This header file is shared by eBPF kernel programs (C) and netd (C++) and
// some of the maps are also accessed directly from Java mainline module code.
//
@@ -98,14 +103,33 @@
static const int CONFIGURATION_MAP_SIZE = 2;
static const int UID_OWNER_MAP_SIZE = 2000;
+#ifdef __cplusplus
+
#define BPF_NETD_PATH "/sys/fs/bpf/netd_shared/"
#define BPF_EGRESS_PROG_PATH BPF_NETD_PATH "prog_netd_cgroupskb_egress_stats"
#define BPF_INGRESS_PROG_PATH BPF_NETD_PATH "prog_netd_cgroupskb_ingress_stats"
-#define XT_BPF_INGRESS_PROG_PATH BPF_NETD_PATH "prog_netd_skfilter_ingress_xtbpf"
-#define XT_BPF_EGRESS_PROG_PATH BPF_NETD_PATH "prog_netd_skfilter_egress_xtbpf"
-#define XT_BPF_ALLOWLIST_PROG_PATH BPF_NETD_PATH "prog_netd_skfilter_allowlist_xtbpf"
-#define XT_BPF_DENYLIST_PROG_PATH BPF_NETD_PATH "prog_netd_skfilter_denylist_xtbpf"
+
+#define ASSERT_STRING_EQUAL(s1, s2) \
+ static_assert(std::string_view(s1) == std::string_view(s2), "mismatch vs Android T netd")
+
+/* -=-=-=-=- WARNING -=-=-=-=-
+ *
+ * These 4 xt_bpf program paths are actually defined by:
+ * //system/netd/include/mainline/XtBpfProgLocations.h
+ * which is intentionally a non-automerged location.
+ *
+ * They are *UNCHANGEABLE* due to being hard coded in Android T's netd binary
+ * as such we have compile time asserts that things match.
+ * (which will be validated during build on mainline-prod branch against old system/netd)
+ *
+ * If you break this, netd on T will fail to start with your tethering mainline module.
+ */
+ASSERT_STRING_EQUAL(XT_BPF_INGRESS_PROG_PATH, BPF_NETD_PATH "prog_netd_skfilter_ingress_xtbpf");
+ASSERT_STRING_EQUAL(XT_BPF_EGRESS_PROG_PATH, BPF_NETD_PATH "prog_netd_skfilter_egress_xtbpf");
+ASSERT_STRING_EQUAL(XT_BPF_ALLOWLIST_PROG_PATH, BPF_NETD_PATH "prog_netd_skfilter_allowlist_xtbpf");
+ASSERT_STRING_EQUAL(XT_BPF_DENYLIST_PROG_PATH, BPF_NETD_PATH "prog_netd_skfilter_denylist_xtbpf");
+
#define CGROUP_SOCKET_PROG_PATH BPF_NETD_PATH "prog_netd_cgroupsock_inet_create"
#define TC_BPF_INGRESS_ACCOUNT_PROG_NAME "prog_netd_schedact_ingress_account"
@@ -122,6 +146,8 @@
#define UID_OWNER_MAP_PATH BPF_NETD_PATH "map_netd_uid_owner_map"
#define UID_PERMISSION_MAP_PATH BPF_NETD_PATH "map_netd_uid_permission_map"
+#endif // __cplusplus
+
enum UidOwnerMatchType {
NO_MATCH = 0,
HAPPY_BOX_MATCH = (1 << 0),
@@ -164,19 +190,9 @@
STRUCT_SIZE(UidOwnerValue, 2 * 4); // 8
// Entry in the configuration map that stores which UID rules are enabled.
-#define UID_RULES_CONFIGURATION_KEY 1
+#define UID_RULES_CONFIGURATION_KEY 0
// Entry in the configuration map that stores which stats map is currently in use.
-#define CURRENT_STATS_MAP_CONFIGURATION_KEY 2
-
-#define BPF_CLATD_PATH "/sys/fs/bpf/net_shared/"
-
-#define CLAT_INGRESS6_PROG_RAWIP_NAME "prog_clatd_schedcls_ingress6_clat_rawip"
-#define CLAT_INGRESS6_PROG_ETHER_NAME "prog_clatd_schedcls_ingress6_clat_ether"
-
-#define CLAT_INGRESS6_PROG_RAWIP_PATH BPF_CLATD_PATH CLAT_INGRESS6_PROG_RAWIP_NAME
-#define CLAT_INGRESS6_PROG_ETHER_PATH BPF_CLATD_PATH CLAT_INGRESS6_PROG_ETHER_NAME
-
-#define CLAT_INGRESS6_MAP_PATH BPF_CLATD_PATH "map_clatd_clat_ingress6_map"
+#define CURRENT_STATS_MAP_CONFIGURATION_KEY 1
typedef struct {
uint32_t iif; // The input interface index
@@ -191,14 +207,6 @@
} ClatIngress6Value;
STRUCT_SIZE(ClatIngress6Value, 4 + 4); // 8
-#define CLAT_EGRESS4_PROG_RAWIP_NAME "prog_clatd_schedcls_egress4_clat_rawip"
-#define CLAT_EGRESS4_PROG_ETHER_NAME "prog_clatd_schedcls_egress4_clat_ether"
-
-#define CLAT_EGRESS4_PROG_RAWIP_PATH BPF_CLATD_PATH CLAT_EGRESS4_PROG_RAWIP_NAME
-#define CLAT_EGRESS4_PROG_ETHER_PATH BPF_CLATD_PATH CLAT_EGRESS4_PROG_ETHER_NAME
-
-#define CLAT_EGRESS4_MAP_PATH BPF_CLATD_PATH "map_clatd_clat_egress4_map"
-
typedef struct {
uint32_t iif; // The input interface index
struct in_addr local4; // The source IPv4 address
diff --git a/bpf_progs/clat_mark.h b/bpf_progs/clat_mark.h
new file mode 100644
index 0000000..874d6ae
--- /dev/null
+++ b/bpf_progs/clat_mark.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+/* -=-=-=-=-= WARNING -=-=-=-=-=-
+ *
+ * DO *NOT* *EVER* CHANGE THIS CONSTANT
+ *
+ * This is aidl::android::net::INetd::CLAT_MARK but we can't use that from
+ * pure C code (ie. the eBPF clat program).
+ *
+ * It must match the iptables rules setup by netd on Android T.
+ *
+ * This mark value is used by the eBPF clatd program to mark ingress non-offloaded clat
+ * packets for later dropping in ip6tables bw_raw_PREROUTING.
+ * They need to be dropped *after* the clat daemon (via receive on an AF_PACKET socket)
+ * sees them and thus cannot be dropped from the bpf program itself.
+ */
+static const uint32_t CLAT_MARK = 0xDEADC1A7;
diff --git a/bpf_progs/clatd.c b/bpf_progs/clatd.c
index 87795f5..66e9616 100644
--- a/bpf_progs/clatd.c
+++ b/bpf_progs/clatd.c
@@ -30,22 +30,17 @@
#define __kernel_udphdr udphdr
#include <linux/udp.h>
-// The resulting .o needs to load on the Android T bpfloader v0.12+
-#define BPFLOADER_MIN_VER 12u
+// The resulting .o needs to load on the Android T beta 3 bpfloader
+#define BPFLOADER_MIN_VER BPFLOADER_T_BETA3_VERSION
#include "bpf_helpers.h"
#include "bpf_net_helpers.h"
#include "bpf_shared.h"
+#include "clat_mark.h"
// From kernel:include/net/ip.h
#define IP_DF 0x4000 // Flag: "Don't Fragment"
-// Used for iptables drops ingress clat packet. Beware of clat mark change may break the device
-// which is using the old clat mark in netd platform code. The reason is that the clat mark is a
-// mainline constant since T+ but netd iptable rules (ex: bandwidth control, firewall, and so on)
-// are set in stone.
-#define CLAT_MARK 0xdeadc1a7
-
DEFINE_BPF_MAP_GRW(clat_ingress6_map, HASH, ClatIngress6Key, ClatIngress6Value, 16, AID_SYSTEM)
static inline __always_inline int nat64(struct __sk_buff* skb, bool is_ethernet) {
diff --git a/bpf_progs/dscp_policy.c b/bpf_progs/dscp_policy.c
index 7211f2b..538a9e4 100644
--- a/bpf_progs/dscp_policy.c
+++ b/bpf_progs/dscp_policy.c
@@ -27,8 +27,8 @@
#include <netinet/udp.h>
#include <string.h>
-// The resulting .o needs to load on the Android T bpfloader v0.12+
-#define BPFLOADER_MIN_VER 12u
+// The resulting .o needs to load on the Android T beta 3 bpfloader
+#define BPFLOADER_MIN_VER BPFLOADER_T_BETA3_VERSION
#include "bpf_helpers.h"
#include "dscp_policy.h"
diff --git a/bpf_progs/netd.c b/bpf_progs/netd.c
index d754616..24b3fed 100644
--- a/bpf_progs/netd.c
+++ b/bpf_progs/netd.c
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-// The resulting .o needs to load on the Android T Beta 3 bpfloader v0.13+
-#define BPFLOADER_MIN_VER 13u
+// The resulting .o needs to load on the Android T Beta 3 bpfloader
+#define BPFLOADER_MIN_VER BPFLOADER_T_BETA3_VERSION
#include <bpf_helpers.h>
#include <linux/bpf.h>
@@ -28,7 +28,6 @@
#include <linux/ipv6.h>
#include <linux/pkt_cls.h>
#include <linux/tcp.h>
-#include <netdutils/UidConstants.h>
#include <stdbool.h>
#include <stdint.h>
#include "bpf_net_helpers.h"
@@ -52,28 +51,57 @@
#define TCP_FLAG_OFF 13
#define RST_OFFSET 2
-DEFINE_BPF_MAP_GRW(cookie_tag_map, HASH, uint64_t, UidTagValue, COOKIE_UID_MAP_SIZE,
- AID_NET_BW_ACCT)
-DEFINE_BPF_MAP_GRW(uid_counterset_map, HASH, uint32_t, uint8_t, UID_COUNTERSET_MAP_SIZE,
- AID_NET_BW_ACCT)
-DEFINE_BPF_MAP_GRW(app_uid_stats_map, HASH, uint32_t, StatsValue, APP_STATS_MAP_SIZE,
- AID_NET_BW_ACCT)
-DEFINE_BPF_MAP_GRW(stats_map_A, HASH, StatsKey, StatsValue, STATS_MAP_SIZE, AID_NET_BW_ACCT)
-DEFINE_BPF_MAP_GRW(stats_map_B, HASH, StatsKey, StatsValue, STATS_MAP_SIZE, AID_NET_BW_ACCT)
-DEFINE_BPF_MAP_GRW(iface_stats_map, HASH, uint32_t, StatsValue, IFACE_STATS_MAP_SIZE,
- AID_NET_BW_ACCT)
-DEFINE_BPF_MAP_GRW(configuration_map, HASH, uint32_t, uint32_t, CONFIGURATION_MAP_SIZE,
- AID_NET_BW_ACCT)
-DEFINE_BPF_MAP_GRW(uid_owner_map, HASH, uint32_t, UidOwnerValue, UID_OWNER_MAP_SIZE,
- AID_NET_BW_ACCT)
-DEFINE_BPF_MAP_GRW(uid_permission_map, HASH, uint32_t, uint8_t, UID_OWNER_MAP_SIZE, AID_NET_BW_ACCT)
+// For maps netd does not need to access
+#define DEFINE_BPF_MAP_NO_NETD(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries) \
+ DEFINE_BPF_MAP_EXT(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries, \
+ AID_ROOT, AID_NET_BW_ACCT, 0060, "fs_bpf_net_shared", "", false)
+
+// For maps netd only needs read only access to
+#define DEFINE_BPF_MAP_RO_NETD(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries) \
+ DEFINE_BPF_MAP_EXT(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries, \
+ AID_ROOT, AID_NET_BW_ACCT, 0460, "fs_bpf_netd_readonly", "", false)
+
+// For maps netd needs to be able to read and write
+#define DEFINE_BPF_MAP_RW_NETD(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries) \
+ DEFINE_BPF_MAP_UGM(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries, \
+ AID_ROOT, AID_NET_BW_ACCT, 0660)
+
+// Bpf map arrays on creation are preinitialized to 0 and do not support deletion of a key,
+// see: kernel/bpf/arraymap.c array_map_delete_elem() returns -EINVAL (from both syscall and ebpf)
+// Additionally on newer kernels the bpf jit can optimize out the lookups.
+// only valid indexes are [0..CONFIGURATION_MAP_SIZE-1]
+DEFINE_BPF_MAP_RO_NETD(configuration_map, ARRAY, uint32_t, uint32_t, CONFIGURATION_MAP_SIZE)
+
+DEFINE_BPF_MAP_RW_NETD(cookie_tag_map, HASH, uint64_t, UidTagValue, COOKIE_UID_MAP_SIZE)
+DEFINE_BPF_MAP_NO_NETD(uid_counterset_map, HASH, uint32_t, uint8_t, UID_COUNTERSET_MAP_SIZE)
+DEFINE_BPF_MAP_NO_NETD(app_uid_stats_map, HASH, uint32_t, StatsValue, APP_STATS_MAP_SIZE)
+DEFINE_BPF_MAP_RW_NETD(stats_map_A, HASH, StatsKey, StatsValue, STATS_MAP_SIZE)
+DEFINE_BPF_MAP_RO_NETD(stats_map_B, HASH, StatsKey, StatsValue, STATS_MAP_SIZE)
+DEFINE_BPF_MAP_NO_NETD(iface_stats_map, HASH, uint32_t, StatsValue, IFACE_STATS_MAP_SIZE)
+DEFINE_BPF_MAP_NO_NETD(uid_owner_map, HASH, uint32_t, UidOwnerValue, UID_OWNER_MAP_SIZE)
+DEFINE_BPF_MAP_RW_NETD(uid_permission_map, HASH, uint32_t, uint8_t, UID_OWNER_MAP_SIZE)
/* never actually used from ebpf */
-DEFINE_BPF_MAP_GRW(iface_index_name_map, HASH, uint32_t, IfaceValue, IFACE_INDEX_NAME_MAP_SIZE,
- AID_NET_BW_ACCT)
+DEFINE_BPF_MAP_NO_NETD(iface_index_name_map, HASH, uint32_t, IfaceValue, IFACE_INDEX_NAME_MAP_SIZE)
+
+// iptables xt_bpf programs need to be usable by both netd and netutils_wrappers
+#define DEFINE_XTBPF_PROG(SECTION_NAME, prog_uid, prog_gid, the_prog) \
+ DEFINE_BPF_PROG(SECTION_NAME, prog_uid, prog_gid, the_prog)
+
+// programs that need to be usable by netd, but not by netutils_wrappers
+#define DEFINE_NETD_BPF_PROG(SECTION_NAME, prog_uid, prog_gid, the_prog) \
+ DEFINE_BPF_PROG_EXT(SECTION_NAME, prog_uid, prog_gid, the_prog, \
+ KVER_NONE, KVER_INF, false, "fs_bpf_netd_readonly", "")
+
+// programs that only need to be usable by the system server
+#define DEFINE_SYS_BPF_PROG(SECTION_NAME, prog_uid, prog_gid, the_prog) \
+ DEFINE_BPF_PROG_EXT(SECTION_NAME, prog_uid, prog_gid, the_prog, \
+ KVER_NONE, KVER_INF, false, "fs_bpf_net_shared", "")
static __always_inline int is_system_uid(uint32_t uid) {
- return (uid <= MAX_SYSTEM_UID) && (uid >= MIN_SYSTEM_UID);
+ // MIN_SYSTEM_UID is AID_ROOT == 0, so uint32_t is *always* >= 0
+ // MAX_SYSTEM_UID is AID_NOBODY == 9999, while AID_APP_START == 10000
+ return (uid < AID_APP_START);
}
/*
@@ -316,17 +344,18 @@
return match;
}
-DEFINE_BPF_PROG("cgroupskb/ingress/stats", AID_ROOT, AID_SYSTEM, bpf_cgroup_ingress)
+DEFINE_NETD_BPF_PROG("cgroupskb/ingress/stats", AID_ROOT, AID_SYSTEM, bpf_cgroup_ingress)
(struct __sk_buff* skb) {
return bpf_traffic_account(skb, BPF_INGRESS);
}
-DEFINE_BPF_PROG("cgroupskb/egress/stats", AID_ROOT, AID_SYSTEM, bpf_cgroup_egress)
+DEFINE_NETD_BPF_PROG("cgroupskb/egress/stats", AID_ROOT, AID_SYSTEM, bpf_cgroup_egress)
(struct __sk_buff* skb) {
return bpf_traffic_account(skb, BPF_EGRESS);
}
-DEFINE_BPF_PROG("skfilter/egress/xtbpf", AID_ROOT, AID_NET_ADMIN, xt_bpf_egress_prog)
+// WARNING: Android T's non-updatable netd depends on the name of this program.
+DEFINE_XTBPF_PROG("skfilter/egress/xtbpf", AID_ROOT, AID_NET_ADMIN, xt_bpf_egress_prog)
(struct __sk_buff* skb) {
// Clat daemon does not generate new traffic, all its traffic is accounted for already
// on the v4-* interfaces (except for the 20 (or 28) extra bytes of IPv6 vs IPv4 overhead,
@@ -345,7 +374,8 @@
return BPF_MATCH;
}
-DEFINE_BPF_PROG("skfilter/ingress/xtbpf", AID_ROOT, AID_NET_ADMIN, xt_bpf_ingress_prog)
+// WARNING: Android T's non-updatable netd depends on the name of this program.
+DEFINE_XTBPF_PROG("skfilter/ingress/xtbpf", AID_ROOT, AID_NET_ADMIN, xt_bpf_ingress_prog)
(struct __sk_buff* skb) {
// Clat daemon traffic is not accounted by virtue of iptables raw prerouting drop rule
// (in clat_raw_PREROUTING chain), which triggers before this (in bw_raw_PREROUTING chain).
@@ -357,7 +387,8 @@
return BPF_MATCH;
}
-DEFINE_BPF_PROG("schedact/ingress/account", AID_ROOT, AID_NET_ADMIN, tc_bpf_ingress_account_prog)
+DEFINE_SYS_BPF_PROG("schedact/ingress/account", AID_ROOT, AID_NET_ADMIN,
+ tc_bpf_ingress_account_prog)
(struct __sk_buff* skb) {
if (is_received_skb(skb)) {
// Account for ingress traffic before tc drops it.
@@ -367,7 +398,8 @@
return TC_ACT_UNSPEC;
}
-DEFINE_BPF_PROG("skfilter/allowlist/xtbpf", AID_ROOT, AID_NET_ADMIN, xt_bpf_allowlist_prog)
+// WARNING: Android T's non-updatable netd depends on the name of this program.
+DEFINE_XTBPF_PROG("skfilter/allowlist/xtbpf", AID_ROOT, AID_NET_ADMIN, xt_bpf_allowlist_prog)
(struct __sk_buff* skb) {
uint32_t sock_uid = bpf_get_socket_uid(skb);
if (is_system_uid(sock_uid)) return BPF_MATCH;
@@ -384,7 +416,8 @@
return BPF_NOMATCH;
}
-DEFINE_BPF_PROG("skfilter/denylist/xtbpf", AID_ROOT, AID_NET_ADMIN, xt_bpf_denylist_prog)
+// WARNING: Android T's non-updatable netd depends on the name of this program.
+DEFINE_XTBPF_PROG("skfilter/denylist/xtbpf", AID_ROOT, AID_NET_ADMIN, xt_bpf_denylist_prog)
(struct __sk_buff* skb) {
uint32_t sock_uid = bpf_get_socket_uid(skb);
UidOwnerValue* denylistMatch = bpf_uid_owner_map_lookup_elem(&sock_uid);
@@ -392,8 +425,8 @@
return BPF_NOMATCH;
}
-DEFINE_BPF_PROG_KVER("cgroupsock/inet/create", AID_ROOT, AID_ROOT, inet_socket_create,
- KVER(4, 14, 0))
+DEFINE_BPF_PROG_EXT("cgroupsock/inet/create", AID_ROOT, AID_ROOT, inet_socket_create,
+ KVER(4, 14, 0), KVER_INF, false, "fs_bpf_netd_readonly", "")
(struct bpf_sock* sk) {
uint64_t gid_uid = bpf_get_current_uid_gid();
/*
@@ -402,7 +435,7 @@
* user at install time so we only check the appId part of a request uid at
* run time. See UserHandle#isSameApp for detail.
*/
- uint32_t appId = (gid_uid & 0xffffffff) % PER_USER_RANGE;
+ uint32_t appId = (gid_uid & 0xffffffff) % AID_USER_OFFSET; // == PER_USER_RANGE == 100000
uint8_t* permissions = bpf_uid_permission_map_lookup_elem(&appId);
if (!permissions) {
// UID not in map. Default to just INTERNET permission.
diff --git a/bpf_progs/offload.c b/bpf_progs/offload.c
index 896bc09..2ec0792 100644
--- a/bpf_progs/offload.c
+++ b/bpf_progs/offload.c
@@ -24,8 +24,8 @@
#define __kernel_udphdr udphdr
#include <linux/udp.h>
-// The resulting .o needs to load on the Android S bpfloader v0.2
-#define BPFLOADER_MIN_VER 2u
+// The resulting .o needs to load on the Android S bpfloader
+#define BPFLOADER_MIN_VER BPFLOADER_S_VERSION
#include "bpf_helpers.h"
#include "bpf_net_helpers.h"
diff --git a/bpf_progs/test.c b/bpf_progs/test.c
index c9c73f1..f2fcc8c 100644
--- a/bpf_progs/test.c
+++ b/bpf_progs/test.c
@@ -18,8 +18,8 @@
#include <linux/in.h>
#include <linux/ip.h>
-// The resulting .o needs to load on the Android S bpfloader v0.2
-#define BPFLOADER_MIN_VER 2u
+// The resulting .o needs to load on the Android S bpfloader
+#define BPFLOADER_MIN_VER BPFLOADER_S_VERSION
#include "bpf_helpers.h"
#include "bpf_net_helpers.h"
diff --git a/netd/BpfHandler.cpp b/netd/BpfHandler.cpp
index f3dfb57..6ae26c3 100644
--- a/netd/BpfHandler.cpp
+++ b/netd/BpfHandler.cpp
@@ -110,8 +110,6 @@
RETURN_IF_NOT_OK(mStatsMapA.init(STATS_MAP_A_PATH));
RETURN_IF_NOT_OK(mStatsMapB.init(STATS_MAP_B_PATH));
RETURN_IF_NOT_OK(mConfigurationMap.init(CONFIGURATION_MAP_PATH));
- RETURN_IF_NOT_OK(mConfigurationMap.writeValue(CURRENT_STATS_MAP_CONFIGURATION_KEY, SELECT_MAP_A,
- BPF_ANY));
RETURN_IF_NOT_OK(mUidPermissionMap.init(UID_PERMISSION_MAP_PATH));
return netdutils::status::ok;
@@ -207,6 +205,7 @@
BpfMap<StatsKey, StatsValue>& currentMap =
(configuration.value() == SELECT_MAP_A) ? mStatsMapA : mStatsMapB;
+ // HACK: mStatsMapB becomes RW BpfMap here, but countUidStatsEntries doesn't modify so it works
base::Result<void> res = currentMap.iterate(countUidStatsEntries);
if (!res.ok()) {
ALOGE("Failed to count the stats entry in map %d: %s", currentMap.getMap().get(),
diff --git a/netd/BpfHandler.h b/netd/BpfHandler.h
index 05b9ebc..5ee04d1 100644
--- a/netd/BpfHandler.h
+++ b/netd/BpfHandler.h
@@ -23,6 +23,7 @@
#include "bpf_shared.h"
using android::bpf::BpfMap;
+using android::bpf::BpfMapRO;
namespace android {
namespace net {
@@ -61,8 +62,8 @@
BpfMap<uint64_t, UidTagValue> mCookieTagMap;
BpfMap<StatsKey, StatsValue> mStatsMapA;
- BpfMap<StatsKey, StatsValue> mStatsMapB;
- BpfMap<uint32_t, uint32_t> mConfigurationMap;
+ BpfMapRO<StatsKey, StatsValue> mStatsMapB;
+ BpfMapRO<uint32_t, uint32_t> mConfigurationMap;
BpfMap<uint32_t, uint8_t> mUidPermissionMap;
std::mutex mMutex;
diff --git a/netd/BpfHandlerTest.cpp b/netd/BpfHandlerTest.cpp
index 1bd222d..a031dbb 100644
--- a/netd/BpfHandlerTest.cpp
+++ b/netd/BpfHandlerTest.cpp
@@ -49,7 +49,7 @@
BpfHandler mBh;
BpfMap<uint64_t, UidTagValue> mFakeCookieTagMap;
BpfMap<StatsKey, StatsValue> mFakeStatsMapA;
- BpfMap<uint32_t, uint32_t> mFakeConfigurationMap;
+ BpfMapRO<uint32_t, uint32_t> mFakeConfigurationMap;
BpfMap<uint32_t, uint8_t> mFakeUidPermissionMap;
void SetUp() {
@@ -62,7 +62,7 @@
mFakeStatsMapA.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE);
ASSERT_VALID(mFakeStatsMapA);
- mFakeConfigurationMap.resetMap(BPF_MAP_TYPE_HASH, 1);
+ mFakeConfigurationMap.resetMap(BPF_MAP_TYPE_ARRAY, CONFIGURATION_MAP_SIZE);
ASSERT_VALID(mFakeConfigurationMap);
mFakeUidPermissionMap.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, 0);
@@ -75,8 +75,8 @@
mBh.mConfigurationMap = mFakeConfigurationMap;
ASSERT_VALID(mBh.mConfigurationMap);
// Always write to stats map A by default.
- ASSERT_RESULT_OK(mBh.mConfigurationMap.writeValue(CURRENT_STATS_MAP_CONFIGURATION_KEY,
- SELECT_MAP_A, BPF_ANY));
+ static_assert(SELECT_MAP_A == 0, "bpf map arrays are zero-initialized");
+
mBh.mUidPermissionMap = mFakeUidPermissionMap;
ASSERT_VALID(mBh.mUidPermissionMap);
}
diff --git a/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java b/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
index 79802fb..c4ea9ae 100644
--- a/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
+++ b/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
@@ -488,8 +488,6 @@
if (null != capabilities) {
setCapabilities(capabilities);
}
- // Send an abort callback if a request is filed before the previous one has completed.
- maybeSendNetworkManagementCallbackForAbort();
// TODO: Update this logic to only do a restart if required. Although a restart may
// be required due to the capabilities or ipConfiguration values, not all
// capabilities changes require a restart.
@@ -651,6 +649,8 @@
mIpClientCallback.awaitIpClientShutdown();
mIpClient = null;
}
+ // Send an abort callback if an updateInterface request was in progress.
+ maybeSendNetworkManagementCallbackForAbort();
mIpClientCallback = null;
if (mNetworkAgent != null) {
@@ -662,7 +662,6 @@
public void destroy() {
mNetworkProvider.unregisterNetworkOffer(mNetworkOfferCallback);
- maybeSendNetworkManagementCallbackForAbort();
stop();
mRequests.clear();
}
diff --git a/service-t/src/com/android/server/net/NetworkStatsRecorder.java b/service-t/src/com/android/server/net/NetworkStatsRecorder.java
index d73e342..768f3eb 100644
--- a/service-t/src/com/android/server/net/NetworkStatsRecorder.java
+++ b/service-t/src/com/android/server/net/NetworkStatsRecorder.java
@@ -79,6 +79,7 @@
private final long mBucketDuration;
private final boolean mOnlyTags;
+ private final boolean mWipeOnError;
private long mPersistThresholdBytes = 2 * MB_IN_BYTES;
private NetworkStats mLastSnapshot;
@@ -103,6 +104,7 @@
// slack to avoid overflow
mBucketDuration = YEAR_IN_MILLIS;
mOnlyTags = false;
+ mWipeOnError = true;
mPending = null;
mSinceBoot = new NetworkStatsCollection(mBucketDuration);
@@ -114,7 +116,8 @@
* Persisted recorder.
*/
public NetworkStatsRecorder(FileRotator rotator, NonMonotonicObserver<String> observer,
- DropBoxManager dropBox, String cookie, long bucketDuration, boolean onlyTags) {
+ DropBoxManager dropBox, String cookie, long bucketDuration, boolean onlyTags,
+ boolean wipeOnError) {
mRotator = Objects.requireNonNull(rotator, "missing FileRotator");
mObserver = Objects.requireNonNull(observer, "missing NonMonotonicObserver");
mDropBox = Objects.requireNonNull(dropBox, "missing DropBoxManager");
@@ -122,6 +125,7 @@
mBucketDuration = bucketDuration;
mOnlyTags = onlyTags;
+ mWipeOnError = wipeOnError;
mPending = new NetworkStatsCollection(bucketDuration);
mSinceBoot = new NetworkStatsCollection(bucketDuration);
@@ -593,7 +597,9 @@
}
mDropBox.addData(TAG_NETSTATS_DUMP, os.toByteArray(), 0);
}
-
- mRotator.deleteAll();
+ // Delete all files if this recorder is set wipe on error.
+ if (mWipeOnError) {
+ mRotator.deleteAll();
+ }
}
}
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index 42a108f..4f0f341 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -76,8 +76,10 @@
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.database.ContentObserver;
import android.net.ConnectivityManager;
+import android.net.ConnectivityResources;
import android.net.DataUsageRequest;
import android.net.INetd;
import android.net.INetworkStatsService;
@@ -140,6 +142,7 @@
import android.util.SparseIntArray;
import android.util.proto.ProtoOutputStream;
+import com.android.connectivity.resources.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FileRotator;
@@ -765,6 +768,11 @@
return null;
}
}
+
+ /** Gets whether the build is userdebug. */
+ public boolean isDebuggable() {
+ return Build.isDebuggable();
+ }
}
/**
@@ -792,11 +800,14 @@
mSystemReady = true;
// create data recorders along with historical rotators
- mDevRecorder = buildRecorder(PREFIX_DEV, mSettings.getDevConfig(), false, mStatsDir);
- mXtRecorder = buildRecorder(PREFIX_XT, mSettings.getXtConfig(), false, mStatsDir);
- mUidRecorder = buildRecorder(PREFIX_UID, mSettings.getUidConfig(), false, mStatsDir);
+ mDevRecorder = buildRecorder(PREFIX_DEV, mSettings.getDevConfig(), false, mStatsDir,
+ true /* wipeOnError */);
+ mXtRecorder = buildRecorder(PREFIX_XT, mSettings.getXtConfig(), false, mStatsDir,
+ true /* wipeOnError */);
+ mUidRecorder = buildRecorder(PREFIX_UID, mSettings.getUidConfig(), false, mStatsDir,
+ true /* wipeOnError */);
mUidTagRecorder = buildRecorder(PREFIX_UID_TAG, mSettings.getUidTagConfig(), true,
- mStatsDir);
+ mStatsDir, true /* wipeOnError */);
updatePersistThresholdsLocked();
@@ -861,12 +872,13 @@
private NetworkStatsRecorder buildRecorder(
String prefix, NetworkStatsSettings.Config config, boolean includeTags,
- File baseDir) {
+ File baseDir, boolean wipeOnError) {
final DropBoxManager dropBox = (DropBoxManager) mContext.getSystemService(
Context.DROPBOX_SERVICE);
return new NetworkStatsRecorder(new FileRotator(
baseDir, prefix, config.rotateAgeMillis, config.deleteAgeMillis),
- mNonMonotonicObserver, dropBox, prefix, config.bucketDuration, includeTags);
+ mNonMonotonicObserver, dropBox, prefix, config.bucketDuration, includeTags,
+ wipeOnError);
}
@GuardedBy("mStatsLock")
@@ -927,18 +939,27 @@
final int targetAttempts = mDeps.getImportLegacyTargetAttempts();
final int attempts;
final int fallbacks;
+ final boolean runComparison;
try {
attempts = mImportLegacyAttemptsCounter.get();
+ // Fallbacks counter would be set to non-zero value to indicate the migration was
+ // not successful.
fallbacks = mImportLegacyFallbacksCounter.get();
+ runComparison = shouldRunComparison();
} catch (IOException e) {
Log.wtf(TAG, "Failed to read counters, skip.", e);
return;
}
- // If fallbacks is not zero, proceed with reading only to give signals from dogfooders.
- // TODO(b/233752318): Remove fallbacks counter check before T formal release.
- if (attempts >= targetAttempts && fallbacks == 0) return;
- final boolean dryRunImportOnly = (attempts >= targetAttempts);
+ // If the target number of attempts are reached, don't import any data.
+ // However, if comparison is requested, still read the legacy data and compare
+ // it to the importer output. This allows OEMs to debug issues with the
+ // importer code and to collect signals from the field.
+ final boolean dryRunImportOnly =
+ fallbacks != 0 && runComparison && (attempts >= targetAttempts);
+ // Return if target attempts are reached and there is no need to dry run.
+ if (attempts >= targetAttempts && !dryRunImportOnly) return;
+
if (dryRunImportOnly) {
Log.i(TAG, "Starting import : only perform read");
} else {
@@ -951,69 +972,59 @@
};
// Legacy directories will be created by recorders if they do not exist
- final File legacyBaseDir = mDeps.getLegacyStatsDir();
- final NetworkStatsRecorder[] legacyRecorders = new NetworkStatsRecorder[]{
- buildRecorder(PREFIX_DEV, mSettings.getDevConfig(), false, legacyBaseDir),
- buildRecorder(PREFIX_XT, mSettings.getXtConfig(), false, legacyBaseDir),
- buildRecorder(PREFIX_UID, mSettings.getUidConfig(), false, legacyBaseDir),
- buildRecorder(PREFIX_UID_TAG, mSettings.getUidTagConfig(), true, legacyBaseDir)
- };
+ final NetworkStatsRecorder[] legacyRecorders;
+ if (runComparison) {
+ final File legacyBaseDir = mDeps.getLegacyStatsDir();
+ // Set wipeOnError flag false so the recorder won't damage persistent data if reads
+ // failed and calling deleteAll.
+ legacyRecorders = new NetworkStatsRecorder[]{
+ buildRecorder(PREFIX_DEV, mSettings.getDevConfig(), false, legacyBaseDir,
+ false /* wipeOnError */),
+ buildRecorder(PREFIX_XT, mSettings.getXtConfig(), false, legacyBaseDir,
+ false /* wipeOnError */),
+ buildRecorder(PREFIX_UID, mSettings.getUidConfig(), false, legacyBaseDir,
+ false /* wipeOnError */),
+ buildRecorder(PREFIX_UID_TAG, mSettings.getUidTagConfig(), true, legacyBaseDir,
+ false /* wipeOnError */)};
+ } else {
+ legacyRecorders = null;
+ }
long migrationEndTime = Long.MIN_VALUE;
- boolean endedWithFallback = false;
try {
// First, read all legacy collections. This is OEM code and it can throw. Don't
// commit any data to disk until all are read.
for (int i = 0; i < migrations.length; i++) {
- String errMsg = null;
- Throwable exception = null;
final MigrationInfo migration = migrations[i];
- // Read the collection from platform code, and using fallback method if throws.
+ // Read the collection from platform code, and set fallbacks counter if throws
+ // for better debugging.
try {
migration.collection = readPlatformCollectionForRecorder(migration.recorder);
} catch (Throwable e) {
- errMsg = "Failed to read stats from platform";
- exception = e;
- }
-
- // Also read the collection with legacy method
- final NetworkStatsRecorder legacyRecorder = legacyRecorders[i];
-
- final NetworkStatsCollection legacyStats;
- try {
- legacyStats = legacyRecorder.getOrLoadCompleteLocked();
- } catch (Throwable e) {
- Log.wtf(TAG, "Failed to read stats with legacy method for recorder " + i, e);
- if (exception != null) {
- throw exception;
+ if (dryRunImportOnly) {
+ Log.wtf(TAG, "Platform data read failed. ", e);
+ return;
} else {
- // Use newer stats, since that's all that is available
- continue;
+ // Data is not imported successfully, set fallbacks counter to non-zero
+ // value to trigger dry run every later boot when the runComparison is
+ // true, in order to make it easier to debug issues.
+ tryIncrementLegacyFallbacksCounter();
+ // Re-throw for error handling. This will increase attempts counter.
+ throw e;
}
}
- if (errMsg == null) {
- try {
- errMsg = compareStats(migration.collection, legacyStats);
- } catch (Throwable e) {
- errMsg = "Failed to compare migrated stats with all stats";
- exception = e;
+ if (runComparison) {
+ final boolean success =
+ compareImportedToLegacyStats(migration, legacyRecorders[i]);
+ if (!success && !dryRunImportOnly) {
+ tryIncrementLegacyFallbacksCounter();
}
}
-
- if (errMsg != null) {
- Log.wtf(TAG, "NetworkStats import for migration " + i
- + " returned invalid data: " + errMsg, exception);
- // Fall back to legacy stats for this boot. The stats for old data will be
- // re-imported again on next boot until they succeed the import. This is fine
- // since every import clears the previous stats for the imported timespan.
- migration.collection = legacyStats;
- endedWithFallback = true;
- }
}
- // For cases where the fallbacks is not zero but target attempts counts reached,
+ // For cases where the fallbacks are not zero but target attempts counts reached,
// only perform reads above and return here.
if (dryRunImportOnly) return;
@@ -1079,22 +1090,78 @@
// Success ! No need to import again next time.
try {
mImportLegacyAttemptsCounter.set(targetAttempts);
- if (endedWithFallback) {
- Log.wtf(TAG, "Imported platform collections with legacy fallback");
- final int fallbacksCount = mImportLegacyFallbacksCounter.get();
- mImportLegacyFallbacksCounter.set(fallbacksCount + 1);
- } else {
- Log.i(TAG, "Successfully imported platform collections");
- // The successes counter is only for debugging. Hence, the synchronization
- // between successes counter and attempts counter are not very critical.
- final int successCount = mImportLegacySuccessesCounter.get();
- mImportLegacySuccessesCounter.set(successCount + 1);
- }
+ Log.i(TAG, "Successfully imported platform collections");
+ // The successes counter is only for debugging. Hence, the synchronization
+ // between successes counter and attempts counter are not very critical.
+ final int successCount = mImportLegacySuccessesCounter.get();
+ mImportLegacySuccessesCounter.set(successCount + 1);
} catch (IOException e) {
Log.wtf(TAG, "Succeed but failed to update counters.", e);
}
}
+ void tryIncrementLegacyFallbacksCounter() {
+ try {
+ final int fallbacks = mImportLegacyFallbacksCounter.get();
+ mImportLegacyFallbacksCounter.set(fallbacks + 1);
+ } catch (IOException e) {
+ Log.wtf(TAG, "Failed to update fallback counter.", e);
+ }
+ }
+
+ @VisibleForTesting
+ boolean shouldRunComparison() {
+ final ConnectivityResources resources = new ConnectivityResources(mContext);
+ // 0 if id not found.
+ Boolean overlayValue = null;
+ try {
+ switch (resources.get().getInteger(R.integer.config_netstats_validate_import)) {
+ case 1:
+ overlayValue = Boolean.TRUE;
+ break;
+ case 0:
+ overlayValue = Boolean.FALSE;
+ break;
+ }
+ } catch (Resources.NotFoundException e) {
+ // Overlay value is not defined.
+ }
+ return overlayValue != null ? overlayValue : mDeps.isDebuggable();
+ }
+
+ /**
+ * Compare imported data with the data returned by legacy recorders.
+ *
+ * @return true if the data matches, false if the data does not match or throw with exceptions.
+ */
+ private boolean compareImportedToLegacyStats(@NonNull MigrationInfo migration,
+ @NonNull NetworkStatsRecorder legacyRecorder) {
+ final NetworkStatsCollection legacyStats;
+ try {
+ legacyStats = legacyRecorder.getOrLoadCompleteLocked();
+ } catch (Throwable e) {
+ Log.wtf(TAG, "Failed to read stats with legacy method for recorder "
+ + legacyRecorder.getCookie(), e);
+ // Cannot read data from legacy method, skip comparison.
+ return false;
+ }
+
+ // The result of comparison is only for logging.
+ try {
+ final String error = compareStats(migration.collection, legacyStats);
+ if (error != null) {
+ Log.wtf(TAG, "Unexpected comparison result for recorder "
+ + legacyRecorder.getCookie() + ": " + error);
+ return false;
+ }
+ } catch (Throwable e) {
+ Log.wtf(TAG, "Failed to compare migrated stats with legacy stats for recorder "
+ + legacyRecorder.getCookie(), e);
+ return false;
+ }
+ return true;
+ }
+
private static String str(NetworkStatsCollection.Key key) {
StringBuilder sb = new StringBuilder()
.append(key.ident.toString())
diff --git a/service/ServiceConnectivityResources/res/values-or/strings.xml b/service/ServiceConnectivityResources/res/values-or/strings.xml
index 8b85884..49a773a 100644
--- a/service/ServiceConnectivityResources/res/values-or/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-or/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"ସିଷ୍ଟମର ସଂଯୋଗ ସମ୍ବନ୍ଧିତ ରିସୋର୍ସଗୁଡ଼ିକ"</string>
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"ସିଷ୍ଟମ କନେକ୍ଟିଭିଟୀ ରିସୋର୍ସ"</string>
<string name="wifi_available_sign_in" msgid="8041178343789805553">"ୱାଇ-ଫାଇ ନେଟୱର୍କରେ ସାଇନ୍-ଇନ୍ କରନ୍ତୁ"</string>
<string name="network_available_sign_in" msgid="2622520134876355561">"ନେଟ୍ୱର୍କରେ ସାଇନ୍ ଇନ୍ କରନ୍ତୁ"</string>
<!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
diff --git a/service/ServiceConnectivityResources/res/values-sq/strings.xml b/service/ServiceConnectivityResources/res/values-sq/strings.xml
index 385c75c..85bd84f 100644
--- a/service/ServiceConnectivityResources/res/values-sq/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-sq/strings.xml
@@ -35,7 +35,7 @@
<string-array name="network_switch_type_name">
<item msgid="3004933964374161223">"të dhënat celulare"</item>
<item msgid="5624324321165953608">"Wi-Fi"</item>
- <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="5667906231066981731">"Bluetooth-i"</item>
<item msgid="346574747471703768">"Eternet"</item>
<item msgid="5734728378097476003">"VPN"</item>
</string-array>
diff --git a/service/ServiceConnectivityResources/res/values/config.xml b/service/ServiceConnectivityResources/res/values/config.xml
index 81782f9..bff6953 100644
--- a/service/ServiceConnectivityResources/res/values/config.xml
+++ b/service/ServiceConnectivityResources/res/values/config.xml
@@ -179,4 +179,13 @@
Only supported up to S. On T+, the Wi-Fi code should use unregisterAfterReplacement in order
to ensure that apps see the network disconnect and reconnect. -->
<integer translatable="false" name="config_validationFailureAfterRoamIgnoreTimeMillis">-1</integer>
+
+ <!-- Whether the network stats service should run compare on the result of
+ {@link NetworkStatsDataMigrationUtils#readPlatformCollection} and the result
+ of reading from legacy recorders. Possible values are:
+ 0 = never compare,
+ 1 = always compare,
+ 2 = compare on debuggable builds (default value)
+ -->
+ <integer translatable="false" name="config_netstats_validate_import">2</integer>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values/overlayable.xml b/service/ServiceConnectivityResources/res/values/overlayable.xml
index b92dd08..3389d63 100644
--- a/service/ServiceConnectivityResources/res/values/overlayable.xml
+++ b/service/ServiceConnectivityResources/res/values/overlayable.xml
@@ -41,6 +41,7 @@
<item type="array" name="config_ethernet_interfaces"/>
<item type="string" name="config_ethernet_iface_regex"/>
<item type="integer" name="config_validationFailureAfterRoamIgnoreTimeMillis" />
+ <item type="integer" name="config_netstats_validate_import" />
</policy>
</overlayable>
</resources>
diff --git a/service/jarjar-rules.txt b/service/jarjar-rules.txt
index c7223fc..4013d2e 100644
--- a/service/jarjar-rules.txt
+++ b/service/jarjar-rules.txt
@@ -118,6 +118,7 @@
rule androidx.core.** com.android.server.nearby.@0
rule androidx.versionedparcelable.** com.android.server.nearby.@0
rule com.google.common.** com.android.server.nearby.@0
+rule android.support.v4.** com.android.server.nearby.@0
# Remaining are connectivity sources in com.android.server and com.android.server.connectivity:
# TODO: move to a subpackage of com.android.connectivity (such as com.android.connectivity.server)
diff --git a/service/native/TrafficControllerTest.cpp b/service/native/TrafficControllerTest.cpp
index c920398..9e53f11 100644
--- a/service/native/TrafficControllerTest.cpp
+++ b/service/native/TrafficControllerTest.cpp
@@ -83,7 +83,7 @@
mFakeStatsMapA.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE);
ASSERT_VALID(mFakeStatsMapA);
- mFakeConfigurationMap.resetMap(BPF_MAP_TYPE_HASH, 1);
+ mFakeConfigurationMap.resetMap(BPF_MAP_TYPE_ARRAY, CONFIGURATION_MAP_SIZE);
ASSERT_VALID(mFakeConfigurationMap);
mFakeUidOwnerMap.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE);
@@ -101,8 +101,8 @@
ASSERT_VALID(mTc.mConfigurationMap);
// Always write to stats map A by default.
- ASSERT_RESULT_OK(mTc.mConfigurationMap.writeValue(CURRENT_STATS_MAP_CONFIGURATION_KEY,
- SELECT_MAP_A, BPF_ANY));
+ static_assert(SELECT_MAP_A == 0);
+
mTc.mUidOwnerMap = mFakeUidOwnerMap;
ASSERT_VALID(mTc.mUidOwnerMap);
mTc.mUidPermissionMap = mFakeUidPermissionMap;
diff --git a/service/native/include/Common.h b/service/native/include/Common.h
index 2427aa9..c9653ad 100644
--- a/service/native/include/Common.h
+++ b/service/native/include/Common.h
@@ -17,9 +17,12 @@
#pragma once
// TODO: deduplicate with the constants in NetdConstants.h.
#include <aidl/android/net/INetd.h>
+#include "clat_mark.h"
using aidl::android::net::INetd;
+static_assert(INetd::CLAT_MARK == CLAT_MARK, "must be 0xDEADC1A7");
+
enum FirewallRule { ALLOW = INetd::FIREWALL_RULE_ALLOW, DENY = INetd::FIREWALL_RULE_DENY };
// ALLOWLIST means the firewall denies all by default, uids must be explicitly ALLOWed
diff --git a/tests/common/java/android/net/LinkPropertiesTest.java b/tests/common/java/android/net/LinkPropertiesTest.java
index 581ee22..9ed2bb3 100644
--- a/tests/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/common/java/android/net/LinkPropertiesTest.java
@@ -20,7 +20,6 @@
import static android.net.RouteInfo.RTN_UNICAST;
import static android.net.RouteInfo.RTN_UNREACHABLE;
-import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
import static com.android.testutils.ParcelUtils.assertParcelingIsLossless;
import static com.android.testutils.ParcelUtils.parcelingRoundTrip;
@@ -53,6 +52,7 @@
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import java.net.Inet4Address;
@@ -68,11 +68,13 @@
@SmallTest
@ConnectivityModuleTest
public class LinkPropertiesTest {
+ // Use a RuleChain to explicitly specify the order of rules. DevSdkIgnoreRule must run before
+ // PlatformCompatChange rule, because otherwise tests with that should be skipped when targeting
+ // target SDK 33 will still attempt to override compat changes (which on user builds will crash)
+ // before being skipped.
@Rule
- public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
-
- @Rule
- public final PlatformCompatChangeRule compatChangeRule = new PlatformCompatChangeRule();
+ public final RuleChain chain = RuleChain.outerRule(
+ new DevSdkIgnoreRule()).around(new PlatformCompatChangeRule());
private static final InetAddress ADDRV4 = address("75.208.6.1");
private static final InetAddress ADDRV6 = address("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
@@ -1262,7 +1264,8 @@
assertFalse(lp.hasIpv4UnreachableDefaultRoute());
}
- @Test @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ @CtsNetTestCasesMaxTargetSdk31(reason = "Compat change cannot be overridden when targeting T+")
@EnableCompatChanges({LinkProperties.EXCLUDED_ROUTES})
public void testHasExcludeRoute() {
LinkProperties lp = new LinkProperties();
@@ -1274,7 +1277,8 @@
assertTrue(lp.hasExcludeRoute());
}
- @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ @CtsNetTestCasesMaxTargetSdk31(reason = "Compat change cannot be overridden when targeting T+")
@EnableCompatChanges({LinkProperties.EXCLUDED_ROUTES})
public void testRouteAddWithSameKey() throws Exception {
LinkProperties lp = new LinkProperties();
@@ -1291,7 +1295,8 @@
assertEquals(2, lp.getRoutes().size());
}
- @Test @IgnoreUpTo(SC_V2)
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ @CtsNetTestCasesMaxTargetSdk31(reason = "Compat change cannot be overridden when targeting T+")
@EnableCompatChanges({LinkProperties.EXCLUDED_ROUTES})
public void testExcludedRoutesEnabled() {
final LinkProperties lp = new LinkProperties();
@@ -1307,8 +1312,8 @@
assertEquals(3, lp.getRoutes().size());
}
- @Test @IgnoreUpTo(SC_V2)
- @CtsNetTestCasesMaxTargetSdk31(reason = "Compat change cannot be overridden on T or above")
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ @CtsNetTestCasesMaxTargetSdk31(reason = "Compat change cannot be overridden when targeting T+")
@DisableCompatChanges({LinkProperties.EXCLUDED_ROUTES})
public void testExcludedRoutesDisabled() {
final LinkProperties lp = new LinkProperties();
diff --git a/tests/common/java/android/net/netstats/NetworkStatsHistoryTest.kt b/tests/common/java/android/net/netstats/NetworkStatsHistoryTest.kt
index 9343ea1..a6c9f3c 100644
--- a/tests/common/java/android/net/netstats/NetworkStatsHistoryTest.kt
+++ b/tests/common/java/android/net/netstats/NetworkStatsHistoryTest.kt
@@ -22,7 +22,6 @@
import com.android.testutils.ConnectivityModuleTest
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.SC_V2
-import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -37,7 +36,6 @@
@JvmField
val ignoreRule = DevSdkIgnoreRule(ignoreClassUpTo = SC_V2)
- @Ignore
@Test
fun testBuilder() {
val entry1 = NetworkStatsHistory.Entry(10, 30, 40, 4, 50, 5, 60)
@@ -63,7 +61,6 @@
statsMultiple.assertEntriesEqual(entry3, entry1, entry2)
}
- @Ignore
@Test
fun testBuilderSortAndDeduplicate() {
val entry1 = NetworkStatsHistory.Entry(10, 30, 40, 4, 50, 5, 60)
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideLinkPropertiesGatingTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideLinkPropertiesGatingTests.java
index b65fb6b..9a1fa42 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideLinkPropertiesGatingTests.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideLinkPropertiesGatingTests.java
@@ -22,6 +22,9 @@
/**
* Tests for the {@link android.net.LinkProperties#EXCLUDED_ROUTES} compatibility change.
+ *
+ * TODO: see if we can delete this cumbersome host test by moving the coverage to CtsNetTestCases
+ * and CtsNetTestCasesMaxTargetSdk31.
*/
public class HostsideLinkPropertiesGatingTests extends CompatChangeGatingTestCase {
private static final String TEST_APK = "CtsHostsideNetworkTestsApp3.apk";
@@ -45,8 +48,19 @@
runDeviceCompatTest("testExcludedRoutesChangeDisabled");
}
- public void testExcludedRoutesChangeDisabledByOverride() throws Exception {
+ public void testExcludedRoutesChangeDisabledByOverrideOnDebugBuild() throws Exception {
+ // Must install APK even when skipping test, because tearDown expects uninstall to succeed.
installPackage(TEST_APK, true);
+
+ // This test uses an app with a target SDK where the compat change is on by default.
+ // Because user builds do not allow overriding compat changes, only run this test on debug
+ // builds. This seems better than deleting this test and not running it anywhere because we
+ // could in the future run this test on userdebug builds in presubmit.
+ //
+ // We cannot use assumeXyz here because CompatChangeGatingTestCase ultimately inherits from
+ // junit.framework.TestCase, which does not understand assumption failures.
+ if ("user".equals(getDevice().getProperty("ro.build.type"))) return;
+
runDeviceCompatTestWithChangeDisabled("testExcludedRoutesChangeDisabled");
}
diff --git a/tests/cts/net/native/src/BpfCompatTest.cpp b/tests/cts/net/native/src/BpfCompatTest.cpp
index 97ecb9e..e52533b 100644
--- a/tests/cts/net/native/src/BpfCompatTest.cpp
+++ b/tests/cts/net/native/src/BpfCompatTest.cpp
@@ -31,8 +31,13 @@
std::ifstream elfFile(elfPath, std::ios::in | std::ios::binary);
ASSERT_TRUE(elfFile.is_open());
- EXPECT_EQ(48, readSectionUint("size_of_bpf_map_def", elfFile, 0));
- EXPECT_EQ(28, readSectionUint("size_of_bpf_prog_def", elfFile, 0));
+ if (android::modules::sdklevel::IsAtLeastT()) {
+ EXPECT_EQ(116, readSectionUint("size_of_bpf_map_def", elfFile, 0));
+ EXPECT_EQ(92, readSectionUint("size_of_bpf_prog_def", elfFile, 0));
+ } else {
+ EXPECT_EQ(48, readSectionUint("size_of_bpf_map_def", elfFile, 0));
+ EXPECT_EQ(28, readSectionUint("size_of_bpf_prog_def", elfFile, 0));
+ }
}
TEST(BpfTest, bpfStructSizeTestPreT) {
diff --git a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
index 69ec189..64cc97d 100644
--- a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
@@ -65,7 +65,6 @@
import org.junit.Assert.assertTrue
import org.junit.Assume.assumeTrue
import org.junit.Before
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import java.net.ServerSocket
@@ -464,7 +463,7 @@
}
}
- @Test @Ignore // TODO(b/234099453): re-enable when the prebuilt module is updated
+ @Test
fun testNsdManager_DiscoverWithNetworkRequest() {
// This test requires shims supporting T+ APIs (discovering on network request)
assumeTrue(TestUtils.shouldTestTApis())
@@ -532,7 +531,7 @@
}
}
- @Test @Ignore // TODO(b/234099453): re-enable when the prebuilt module is updated
+ @Test
fun testNsdManager_DiscoverWithNetworkRequest_NoMatchingNetwork() {
// This test requires shims supporting T+ APIs (discovering on network request)
assumeTrue(TestUtils.shouldTestTApis())
diff --git a/tests/cts/net/src/android/net/cts/RateLimitTest.java b/tests/cts/net/src/android/net/cts/RateLimitTest.java
index 423f213..28cec1a 100644
--- a/tests/cts/net/src/android/net/cts/RateLimitTest.java
+++ b/tests/cts/net/src/android/net/cts/RateLimitTest.java
@@ -304,7 +304,7 @@
// If this value is too low, this test might become flaky because of the burst value that
// allows to send at a higher data rate for a short period of time. The faster the data rate
// and the longer the test, the less this test will be affected.
- final long dataLimitInBytesPerSecond = 1_000_000; // 1MB/s
+ final long dataLimitInBytesPerSecond = 2_000_000; // 2MB/s
long resultInBytesPerSecond = runIngressDataRateMeasurement(Duration.ofSeconds(1));
assertGreaterThan("Failed initial test with rate limit disabled", resultInBytesPerSecond,
dataLimitInBytesPerSecond);
@@ -315,9 +315,9 @@
waitForTcPoliceFilterInstalled(Duration.ofSeconds(1));
resultInBytesPerSecond = runIngressDataRateMeasurement(Duration.ofSeconds(10));
- // Add 1% tolerance to reduce test flakiness. Burst size is constant at 128KiB.
+ // Add 10% tolerance to reduce test flakiness. Burst size is constant at 128KiB.
assertLessThan("Failed test with rate limit enabled", resultInBytesPerSecond,
- (long) (dataLimitInBytesPerSecond * 1.01));
+ (long) (dataLimitInBytesPerSecond * 1.1));
ConnectivitySettingsManager.setIngressRateLimitInBytesPerSecond(mContext, -1);
diff --git a/tests/unit/java/android/net/NetworkStatsRecorderTest.java b/tests/unit/java/android/net/NetworkStatsRecorderTest.java
new file mode 100644
index 0000000..fad11a3
--- /dev/null
+++ b/tests/unit/java/android/net/NetworkStatsRecorderTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *i
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net;
+
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+
+import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.net.NetworkStats;
+import android.os.DropBoxManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.util.FileRotator;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.IOException;
+
+@RunWith(DevSdkIgnoreRunner.class)
+@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(SC_V2)
+public final class NetworkStatsRecorderTest {
+ private static final String TAG = NetworkStatsRecorderTest.class.getSimpleName();
+
+ private static final String TEST_PREFIX = "test";
+
+ @Mock private DropBoxManager mDropBox;
+ @Mock private NetworkStats.NonMonotonicObserver mObserver;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ private NetworkStatsRecorder buildRecorder(FileRotator rotator, boolean wipeOnError) {
+ return new NetworkStatsRecorder(rotator, mObserver, mDropBox, TEST_PREFIX,
+ HOUR_IN_MILLIS, false /* includeTags */, wipeOnError);
+ }
+
+ @Test
+ public void testWipeOnError() throws Exception {
+ final FileRotator rotator = mock(FileRotator.class);
+ final NetworkStatsRecorder wipeOnErrorRecorder = buildRecorder(rotator, true);
+
+ // Assuming that the rotator gets an exception happened when read data.
+ doThrow(new IOException()).when(rotator).readMatching(any(), anyLong(), anyLong());
+ wipeOnErrorRecorder.getOrLoadPartialLocked(Long.MIN_VALUE, Long.MAX_VALUE);
+ // Verify that the files will be deleted.
+ verify(rotator, times(1)).deleteAll();
+ reset(rotator);
+
+ final NetworkStatsRecorder noWipeOnErrorRecorder = buildRecorder(rotator, false);
+ doThrow(new IOException()).when(rotator).readMatching(any(), anyLong(), anyLong());
+ noWipeOnErrorRecorder.getOrLoadPartialLocked(Long.MIN_VALUE, Long.MAX_VALUE);
+ // Verify that the rotator won't delete files.
+ verify(rotator, never()).deleteAll();
+ }
+}
diff --git a/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java b/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java
index 829b824..892e140 100644
--- a/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java
+++ b/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java
@@ -42,9 +42,7 @@
@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
-// TODO(b/234099453): re-enable once a newer prebuilt is available
-// @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.CUR_DEVELOPMENT)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class NsdServiceInfoTest {
public final static InetAddress LOCALHOST;
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index d3cfb76..ed9e930 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -83,9 +83,7 @@
// - test NSD_ON ENABLE/DISABLED listening
@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
-// TODO(b/234099453): re-enable once a newer prebuilt is available
-// @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.CUR_DEVELOPMENT)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class NsdServiceTest {
static final int PROTOCOL = NsdManager.PROTOCOL_DNS_SD;
diff --git a/tests/unit/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java b/tests/unit/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java
index 8e43253..9a8a72f 100644
--- a/tests/unit/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java
+++ b/tests/unit/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java
@@ -93,6 +93,8 @@
private Handler mHandler;
private EthernetNetworkFactory mNetFactory = null;
private IpClientCallbacks mIpClientCallbacks;
+ private NetworkOfferCallback mNetworkOfferCallback;
+ private NetworkRequest mRequestToKeepNetworkUp;
@Mock private Context mContext;
@Mock private Resources mResources;
@Mock private EthernetNetworkFactory.Dependencies mDeps;
@@ -245,7 +247,9 @@
ArgumentCaptor<NetworkOfferCallback> captor = ArgumentCaptor.forClass(
NetworkOfferCallback.class);
verify(mNetworkProvider).registerNetworkOffer(any(), any(), any(), captor.capture());
- captor.getValue().onNetworkNeeded(createDefaultRequest());
+ mRequestToKeepNetworkUp = createDefaultRequest();
+ mNetworkOfferCallback = captor.getValue();
+ mNetworkOfferCallback.onNetworkNeeded(mRequestToKeepNetworkUp);
verifyStart(ipConfig);
clearInvocations(mDeps);
@@ -649,6 +653,14 @@
@DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
@Ignore("TODO: temporarily ignore tests until prebuilts are updated")
@Test
+ public void testUpdateInterfaceAbortsOnNetworkUneededRemovesAllRequests() throws Exception {
+ initEthernetNetworkFactory();
+ verifyNetworkManagementCallIsAbortedWhenInterrupted(
+ TEST_IFACE,
+ () -> mNetworkOfferCallback.onNetworkUnneeded(mRequestToKeepNetworkUp));
+ }
+
+ @Test
public void testUpdateInterfaceCallsListenerCorrectlyOnConcurrentRequests() throws Exception {
initEthernetNetworkFactory();
final NetworkCapabilities capabilities = createDefaultFilterCaps();
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
index b1d44ea..e03b4fe 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -95,13 +95,16 @@
import android.app.AlarmManager;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Resources;
import android.database.ContentObserver;
+import android.net.ConnectivityResources;
import android.net.DataUsageRequest;
import android.net.INetd;
import android.net.INetworkStatsSession;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
+import android.net.NetworkIdentity;
import android.net.NetworkStateSnapshot;
import android.net.NetworkStats;
import android.net.NetworkStatsCollection;
@@ -128,6 +131,7 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.android.connectivity.resources.R;
import com.android.internal.util.FileRotator;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.net.module.util.IBpfMap;
@@ -154,6 +158,7 @@
import java.time.temporal.ChronoUnit;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -247,6 +252,8 @@
private @Mock PersistentInt mImportLegacyAttemptsCounter;
private @Mock PersistentInt mImportLegacySuccessesCounter;
private @Mock PersistentInt mImportLegacyFallbacksCounter;
+ private @Mock Resources mResources;
+ private Boolean mIsDebuggable;
private class MockContext extends BroadcastInterceptingContext {
private final Context mBaseContext;
@@ -307,6 +314,12 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+
+ // Setup mock resources.
+ final Context mockResContext = mock(Context.class);
+ doReturn(mResources).when(mockResContext).getResources();
+ ConnectivityResources.setResourcesContextForTest(mockResContext);
+
final Context context = InstrumentationRegistry.getContext();
mServiceContext = new MockContext(context);
when(mLocationPermissionChecker.checkCallersLocationPermission(
@@ -462,6 +475,11 @@
public IBpfMap<UidStatsMapKey, StatsMapValue> getAppUidStatsMap() {
return mAppUidStatsMap;
}
+
+ @Override
+ public boolean isDebuggable() {
+ return mIsDebuggable == Boolean.TRUE;
+ }
};
}
@@ -1149,7 +1167,7 @@
// already documented publicly, refer to {@link NetworkStatsManager#queryDetails}.
}
- @Test @Ignore // TODO(b/234099453): re-enable when the prebuilt module is updated
+ @Test
public void testUidStatsForTransport() throws Exception {
// pretend that network comes online
expectDefaultSettings();
@@ -1898,19 +1916,127 @@
// will decrease the retry counter by 1.
}
+ @Test
+ public void testDataMigration_differentFromFallback() throws Exception {
+ assertStatsFilesExist(false);
+ expectDefaultSettings();
+
+ NetworkStateSnapshot[] states = new NetworkStateSnapshot[]{buildWifiState()};
+
+ mService.notifyNetworkStatus(NETWORKS_WIFI, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
+
+ // modify some number on wifi, and trigger poll event
+ incrementCurrentTime(HOUR_IN_MILLIS);
+ expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
+ .insertEntry(TEST_IFACE, 1024L, 8L, 2048L, 16L));
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
+ .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 0L));
+ forcePollAndWaitForIdle();
+ // Simulate shutdown to force persisting data
+ mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SHUTDOWN));
+ assertStatsFilesExist(true);
+
+ // Move the files to the legacy directory to simulate an import from old data
+ for (File f : mStatsDir.listFiles()) {
+ Files.move(f.toPath(), mLegacyStatsDir.toPath().resolve(f.getName()));
+ }
+ assertStatsFilesExist(false);
+
+ // Prepare some unexpected data.
+ final NetworkIdentity testWifiIdent = new NetworkIdentity.Builder().setType(TYPE_WIFI)
+ .setWifiNetworkKey(TEST_WIFI_NETWORK_KEY).build();
+ final NetworkStatsCollection.Key unexpectedUidAllkey = new NetworkStatsCollection.Key(
+ Set.of(testWifiIdent), UID_ALL, SET_DEFAULT, 0);
+ final NetworkStatsCollection.Key unexpectedUidBluekey = new NetworkStatsCollection.Key(
+ Set.of(testWifiIdent), UID_BLUE, SET_DEFAULT, 0);
+ final NetworkStatsHistory unexpectedHistory = new NetworkStatsHistory
+ .Builder(965L /* bucketDuration */, 1)
+ .addEntry(new NetworkStatsHistory.Entry(TEST_START, 3L, 55L, 4L, 31L, 10L, 5L))
+ .build();
+
+ // Simulate the platform stats collection somehow is different from what is read from
+ // the fallback method. The service should read them as is. This usually happens when an
+ // OEM has changed the implementation of NetworkStatsDataMigrationUtils inside the platform.
+ final NetworkStatsCollection summaryCollection =
+ getLegacyCollection(PREFIX_XT, false /* includeTags */);
+ summaryCollection.recordHistory(unexpectedUidAllkey, unexpectedHistory);
+ final NetworkStatsCollection uidCollection =
+ getLegacyCollection(PREFIX_UID, false /* includeTags */);
+ uidCollection.recordHistory(unexpectedUidBluekey, unexpectedHistory);
+ mPlatformNetworkStatsCollection.put(PREFIX_DEV, summaryCollection);
+ mPlatformNetworkStatsCollection.put(PREFIX_XT, summaryCollection);
+ mPlatformNetworkStatsCollection.put(PREFIX_UID, uidCollection);
+ mPlatformNetworkStatsCollection.put(PREFIX_UID_TAG,
+ getLegacyCollection(PREFIX_UID_TAG, true /* includeTags */));
+
+ // Mock zero usage and boot through serviceReady(), verify there is no imported data.
+ expectDefaultSettings();
+ expectNetworkStatsUidDetail(buildEmptyStats());
+ expectSystemReady();
+ mService.systemReady();
+ assertStatsFilesExist(false);
+
+ // Set the flag and reboot, verify the imported data is not there until next boot.
+ mStoreFilesInApexData = true;
+ mImportLegacyTargetAttempts = 3;
+ mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SHUTDOWN));
+ assertStatsFilesExist(false);
+
+ // Boot through systemReady() again.
+ expectDefaultSettings();
+ expectNetworkStatsUidDetail(buildEmptyStats());
+ expectSystemReady();
+ mService.systemReady();
+
+ // Verify the result read from public API matches the result returned from the importer.
+ assertNetworkTotal(sTemplateWifi, 1024L + 55L, 8L + 4L, 2048L + 31L, 16L + 10L, 0 + 5);
+ assertUidTotal(sTemplateWifi, UID_BLUE,
+ 128L + 55L, 1L + 4L, 128L + 31L, 1L + 10L, 0 + 5);
+ assertStatsFilesExist(true);
+ verify(mImportLegacyAttemptsCounter).set(3);
+ verify(mImportLegacySuccessesCounter).set(1);
+ }
+
+ @Test
+ public void testShouldRunComparison() {
+ for (Boolean isDebuggable : Set.of(Boolean.TRUE, Boolean.FALSE)) {
+ mIsDebuggable = isDebuggable;
+ // Verify return false regardless of the device is debuggable.
+ doReturn(0).when(mResources)
+ .getInteger(R.integer.config_netstats_validate_import);
+ assertShouldRunComparison(false, isDebuggable);
+ // Verify return true regardless of the device is debuggable.
+ doReturn(1).when(mResources)
+ .getInteger(R.integer.config_netstats_validate_import);
+ assertShouldRunComparison(true, isDebuggable);
+ // Verify return true iff the device is debuggable.
+ for (int testValue : Set.of(-1, 2)) {
+ doReturn(testValue).when(mResources)
+ .getInteger(R.integer.config_netstats_validate_import);
+ assertShouldRunComparison(isDebuggable, isDebuggable);
+ }
+ }
+ }
+
+ private void assertShouldRunComparison(boolean expected, boolean isDebuggable) {
+ assertEquals("shouldRunComparison (debuggable=" + isDebuggable + "): ",
+ expected, mService.shouldRunComparison());
+ }
+
private NetworkStatsRecorder makeTestRecorder(File directory, String prefix, Config config,
- boolean includeTags) {
+ boolean includeTags, boolean wipeOnError) {
final NetworkStats.NonMonotonicObserver observer =
mock(NetworkStats.NonMonotonicObserver.class);
final DropBoxManager dropBox = mock(DropBoxManager.class);
return new NetworkStatsRecorder(new FileRotator(
directory, prefix, config.rotateAgeMillis, config.deleteAgeMillis),
- observer, dropBox, prefix, config.bucketDuration, includeTags);
+ observer, dropBox, prefix, config.bucketDuration, includeTags, wipeOnError);
}
private NetworkStatsCollection getLegacyCollection(String prefix, boolean includeTags) {
final NetworkStatsRecorder recorder = makeTestRecorder(mLegacyStatsDir, prefix,
- mSettings.getDevConfig(), includeTags);
+ mSettings.getDevConfig(), includeTags, false);
return recorder.getOrLoadCompleteLocked();
}