Snap for 8564071 from 35f68506d29b8031ac98bdaf89fd6a22c83f9192 to mainline-adbd-release
Change-Id: I1e24e8e61d7758ae89479513bb647ea6c63a9de2
diff --git a/Android.bp b/Android.bp
index 7306d44..d16b05d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -34,21 +34,6 @@
"libdmabufinfo",
],
target: {
- android: {
- static_libs: [
- "libbpf_android",
- ],
- },
- apex: {
- exclude_static_libs: [
- "libbpf_android",
- ],
- },
- vendor: {
- exclude_static_libs: [
- "libbpf_android",
- ],
- },
darwin: {
enabled: false,
},
@@ -67,6 +52,7 @@
"libdmabufinfo/include",
],
export_shared_lib_headers: ["libbase"],
+ header_libs: ["bpf_headers"],
srcs: [
"pageacct.cpp",
"procmeminfo.cpp",
diff --git a/OWNERS b/OWNERS
index b15bb48..5a7a0bd 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1 +1,2 @@
+# Bug component: 391836
[email protected]
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 32750b0..8dae108 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -3,5 +3,10 @@
{
"name": "libmeminfo_test"
}
+ ],
+ "hwasan-postsubmit": [
+ {
+ "name": "libmeminfo_test"
+ }
]
}
diff --git a/include/meminfo/sysmeminfo.h b/include/meminfo/sysmeminfo.h
index 175a0df..a17f8ef 100644
--- a/include/meminfo/sysmeminfo.h
+++ b/include/meminfo/sysmeminfo.h
@@ -24,6 +24,7 @@
#include <map>
#include <string>
#include <string_view>
+#include <unordered_map>
namespace android {
namespace meminfo {
@@ -121,6 +122,12 @@
bool ReadDmabufHeapPoolsSizeKb(uint64_t* size,
const std::string& path = "/sys/kernel/dma_heap/total_pools_kb");
+// Read per-process GPU memory usage. Returns a map of pid -> GPU Mem in kilobytes.
+bool ReadPerProcessGpuMem(std::unordered_map<uint32_t, uint64_t>* out);
+
+// Read GPU usage of the specified process in kb.
+bool ReadProcessGpuUsageKb(uint32_t pid, uint32_t gpu_id, uint64_t* size);
+
// Read GPU total usage size in kb
bool ReadGpuTotalUsageKb(uint64_t* size);
diff --git a/libdmabufinfo/dmabufinfo.cpp b/libdmabufinfo/dmabufinfo.cpp
index 422fc6e..cb639c1 100644
--- a/libdmabufinfo/dmabufinfo.cpp
+++ b/libdmabufinfo/dmabufinfo.cpp
@@ -302,5 +302,36 @@
return true;
}
+bool ReadDmaBufs(std::vector<DmaBuffer>* bufs) {
+ bufs->clear();
+
+ std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir("/proc"), closedir);
+ if (!dir) {
+ LOG(ERROR) << "Failed to open /proc directory";
+ bufs->clear();
+ return false;
+ }
+
+ struct dirent* dent;
+ while ((dent = readdir(dir.get()))) {
+ if (dent->d_type != DT_DIR) continue;
+
+ int pid = atoi(dent->d_name);
+ if (pid == 0) {
+ continue;
+ }
+
+ if (!ReadDmaBufFdRefs(pid, bufs)) {
+ LOG(ERROR) << "Failed to read dmabuf fd references for pid " << pid;
+ }
+
+ if (!ReadDmaBufMapRefs(pid, bufs)) {
+ LOG(ERROR) << "Failed to read dmabuf map references for pid " << pid;
+ }
+ }
+
+ return true;
+}
+
} // namespace dmabufinfo
} // namespace android
diff --git a/libdmabufinfo/include/dmabufinfo/dmabufinfo.h b/libdmabufinfo/include/dmabufinfo/dmabufinfo.h
index 93736df..bb4aff1 100644
--- a/libdmabufinfo/include/dmabufinfo/dmabufinfo.h
+++ b/libdmabufinfo/include/dmabufinfo/dmabufinfo.h
@@ -127,5 +127,10 @@
bool ReadDmaBufPss(int pid, uint64_t* pss, const std::string& procfs_path = "/proc",
const std::string& dmabuf_sysfs_path = "/sys/kernel/dmabuf/buffers");
+// Writes DmaBuffer info into an existing vector (which will be cleared first.)
+// Will include all DmaBuffers, whether thay are retained or mapped.
+// Returns true on success, otherwise false.
+bool ReadDmaBufs(std::vector<DmaBuffer>* bufs);
+
} // namespace dmabufinfo
} // namespace android
diff --git a/libdmabufinfo/tools/dmabuf_dump.cpp b/libdmabufinfo/tools/dmabuf_dump.cpp
index 57204f6..65ebd08 100644
--- a/libdmabufinfo/tools/dmabuf_dump.cpp
+++ b/libdmabufinfo/tools/dmabuf_dump.cpp
@@ -179,45 +179,6 @@
total_size / 1024, kernel_rss / 1024, total_rss / 1024, total_pss / 1024);
}
-static bool ReadDmaBufs(std::vector<DmaBuffer>* bufs) {
- bufs->clear();
-
- if (!ReadDmaBufInfo(bufs)) {
- printf("debugfs entry for dmabuf not available, using /proc/<pid>/fdinfo instead\n");
- }
-
- std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir("/proc"), closedir);
- if (!dir) {
- fprintf(stderr, "Failed to open /proc directory\n");
- bufs->clear();
- return false;
- }
-
- struct dirent* dent;
- while ((dent = readdir(dir.get()))) {
- if (dent->d_type != DT_DIR) continue;
-
- int pid = atoi(dent->d_name);
- if (pid == 0) {
- continue;
- }
-
- if (!ReadDmaBufFdRefs(pid, bufs)) {
- fprintf(stderr, "Failed to read dmabuf fd references for pid %d\n", pid);
- bufs->clear();
- return false;
- }
-
- if (!ReadDmaBufMapRefs(pid, bufs)) {
- fprintf(stderr, "Failed to read dmabuf map references for pid %d\n", pid);
- bufs->clear();
- return false;
- }
- }
-
- return true;
-}
-
static void DumpDmabufSysfsStats() {
android::dmabufinfo::DmabufSysfsStats stats;
@@ -300,7 +261,10 @@
exit(EXIT_FAILURE);
}
} else {
- if (!ReadDmaBufs(&bufs)) exit(EXIT_FAILURE);
+ if (!ReadDmaBufs(&bufs)) {
+ fprintf(stderr, "Failed to ReadDmaBufs, check logcat for info\n");
+ exit(EXIT_FAILURE);
+ }
}
// Show the old dmabuf table, inode x process
diff --git a/libmeminfo_test.cpp b/libmeminfo_test.cpp
index c41e4ff..20159bf 100644
--- a/libmeminfo_test.cpp
+++ b/libmeminfo_test.cpp
@@ -967,7 +967,7 @@
TEST(SysMemInfo, TestReadGpuTotalUsageKb) {
uint64_t size;
- if (android::base::GetIntProperty("ro.product.first_api_level", 0) < __ANDROID_API_S__) {
+ if (android::base::GetIntProperty("ro.vendor.api_level", 0) < __ANDROID_API_S__) {
GTEST_SKIP();
}
diff --git a/procmeminfo.cpp b/procmeminfo.cpp
index af64f86..13a17da 100644
--- a/procmeminfo.cpp
+++ b/procmeminfo.cpp
@@ -68,55 +68,58 @@
// Returns true if the line was valid smaps stats line false otherwise.
static bool parse_smaps_field(const char* line, MemUsage* stats) {
- char field[64];
- int len;
- if (sscanf(line, "%63s %n", field, &len) == 1 && *field && field[strlen(field) - 1] == ':') {
- const char* c = line + len;
- switch (field[0]) {
+ const char *end = line;
+
+ // https://lore.kernel.org/patchwork/patch/1088579/ introduced tabs. Handle this case as well.
+ while (*end && !isspace(*end)) end++;
+ if (*end && end > line && *(end - 1) == ':') {
+ const char* c = end;
+ while (isspace(*c)) c++;
+ switch (line[0]) {
case 'P':
- if (strncmp(field, "Pss:", 4) == 0) {
+ if (strncmp(line, "Pss:", 4) == 0) {
stats->pss = strtoull(c, nullptr, 10);
- } else if (strncmp(field, "Private_Clean:", 14) == 0) {
+ } else if (strncmp(line, "Private_Clean:", 14) == 0) {
uint64_t prcl = strtoull(c, nullptr, 10);
stats->private_clean = prcl;
stats->uss += prcl;
- } else if (strncmp(field, "Private_Dirty:", 14) == 0) {
+ } else if (strncmp(line, "Private_Dirty:", 14) == 0) {
uint64_t prdi = strtoull(c, nullptr, 10);
stats->private_dirty = prdi;
stats->uss += prdi;
- } else if (strncmp(field, "Private_Hugetlb:", 16) == 0) {
+ } else if (strncmp(line, "Private_Hugetlb:", 16) == 0) {
stats->private_hugetlb = strtoull(c, nullptr, 10);
}
break;
case 'S':
- if (strncmp(field, "Size:", 5) == 0) {
+ if (strncmp(line, "Size:", 5) == 0) {
stats->vss = strtoull(c, nullptr, 10);
- } else if (strncmp(field, "Shared_Clean:", 13) == 0) {
+ } else if (strncmp(line, "Shared_Clean:", 13) == 0) {
stats->shared_clean = strtoull(c, nullptr, 10);
- } else if (strncmp(field, "Shared_Dirty:", 13) == 0) {
+ } else if (strncmp(line, "Shared_Dirty:", 13) == 0) {
stats->shared_dirty = strtoull(c, nullptr, 10);
- } else if (strncmp(field, "Swap:", 5) == 0) {
+ } else if (strncmp(line, "Swap:", 5) == 0) {
stats->swap = strtoull(c, nullptr, 10);
- } else if (strncmp(field, "SwapPss:", 8) == 0) {
+ } else if (strncmp(line, "SwapPss:", 8) == 0) {
stats->swap_pss = strtoull(c, nullptr, 10);
- } else if (strncmp(field, "ShmemPmdMapped:", 15) == 0) {
+ } else if (strncmp(line, "ShmemPmdMapped:", 15) == 0) {
stats->shmem_pmd_mapped = strtoull(c, nullptr, 10);
- } else if (strncmp(field, "Shared_Hugetlb:", 15) == 0) {
+ } else if (strncmp(line, "Shared_Hugetlb:", 15) == 0) {
stats->shared_hugetlb = strtoull(c, nullptr, 10);
}
break;
case 'R':
- if (strncmp(field, "Rss:", 4) == 0) {
+ if (strncmp(line, "Rss:", 4) == 0) {
stats->rss = strtoull(c, nullptr, 10);
}
break;
case 'A':
- if (strncmp(field, "AnonHugePages:", 14) == 0) {
+ if (strncmp(line, "AnonHugePages:", 14) == 0) {
stats->anon_huge_pages = strtoull(c, nullptr, 10);
}
break;
case 'F':
- if (strncmp(field, "FilePmdMapped:", 14) == 0) {
+ if (strncmp(line, "FilePmdMapped:", 14) == 0) {
stats->file_pmd_mapped = strtoull(c, nullptr, 10);
}
break;
@@ -383,10 +386,6 @@
size_t num_in_page_cache = 0;
size_t num_leftover_pages = num_pages;
for (size_t cur_page = first_page; cur_page < first_page + num_pages; ++cur_page) {
- if (!get_wss) {
- vma.usage.vss += pagesz;
- }
-
// Cache page map data.
if (cur_page_cache_index == num_in_page_cache) {
static constexpr size_t kMaxPages = 2048;
@@ -479,6 +478,9 @@
vma.usage.shared_clean += is_dirty ? 0 : pagesz;
}
}
+ if (!get_wss) {
+ vma.usage.vss += pagesz * num_pages;
+ }
return true;
}
diff --git a/sysmeminfo.cpp b/sysmeminfo.cpp
index 2dabea8..b7a9c70 100644
--- a/sysmeminfo.cpp
+++ b/sysmeminfo.cpp
@@ -320,11 +320,9 @@
return true;
}
-bool ReadGpuTotalUsageKb(uint64_t* size) {
+bool ReadPerProcessGpuMem([[maybe_unused]] std::unordered_map<uint32_t, uint64_t>* out) {
#if defined(__ANDROID__) && !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
- static constexpr const char kBpfGpuMemTotalMap[] =
- "/sys/fs/bpf/map_gpu_mem_gpu_mem_total_map";
- static constexpr uint64_t kBpfKeyGpuTotalUsage = 0;
+ static constexpr const char kBpfGpuMemTotalMap[] = "/sys/fs/bpf/map_gpu_mem_gpu_mem_total_map";
// Use the read-only wrapper BpfMapRO to properly retrieve the read-only map.
auto map = bpf::BpfMapRO<uint64_t, uint64_t>(kBpfGpuMemTotalMap);
@@ -333,14 +331,73 @@
return false;
}
- auto res = map.readValue(kBpfKeyGpuTotalUsage);
- if (!res.ok()) {
+ if (!out) {
+ LOG(ERROR) << "ReadPerProcessGpuMem: out param is null";
+ return false;
+ }
+ out->clear();
+
+ auto map_key = map.getFirstKey();
+ if (!map_key.ok()) {
+ return true;
+ }
+
+ do {
+ uint64_t key = map_key.value();
+ uint32_t pid = key; // BPF Key [32-bits GPU ID | 32-bits PID]
+
+ auto gpu_mem = map.readValue(key);
+ if (!gpu_mem.ok()) {
+ LOG(ERROR) << "Invalid file format: " << kBpfGpuMemTotalMap;
+ return false;
+ }
+
+ const auto& iter = out->find(pid);
+ if (iter == out->end()) {
+ out->insert({pid, gpu_mem.value() / 1024});
+ } else {
+ iter->second += gpu_mem.value() / 1024;
+ }
+
+ map_key = map.getNextKey(key);
+ } while (map_key.ok());
+
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool ReadProcessGpuUsageKb([[maybe_unused]] uint32_t pid, [[maybe_unused]] uint32_t gpu_id,
+ uint64_t* size) {
+#if defined(__ANDROID__) && !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
+ static constexpr const char kBpfGpuMemTotalMap[] = "/sys/fs/bpf/map_gpu_mem_gpu_mem_total_map";
+
+ uint64_t gpu_mem;
+
+ // BPF Key [32-bits GPU ID | 32-bits PID]
+ uint64_t kBpfKeyGpuUsage = ((uint64_t)gpu_id << 32) | pid;
+
+ // Use the read-only wrapper BpfMapRO to properly retrieve the read-only map.
+ auto map = bpf::BpfMapRO<uint64_t, uint64_t>(kBpfGpuMemTotalMap);
+ if (!map.isValid()) {
+ LOG(ERROR) << "Can't open file: " << kBpfGpuMemTotalMap;
+ return false;
+ }
+
+ auto res = map.readValue(kBpfKeyGpuUsage);
+
+ if (res.ok()) {
+ gpu_mem = res.value();
+ } else if (res.error().code() == ENOENT) {
+ gpu_mem = 0;
+ } else {
LOG(ERROR) << "Invalid file format: " << kBpfGpuMemTotalMap;
return false;
}
if (size) {
- *size = res.value() / 1024;
+ *size = gpu_mem / 1024;
}
return true;
#else
@@ -351,5 +408,13 @@
#endif
}
+bool ReadGpuTotalUsageKb(uint64_t* size) {
+ // gpu_mem_total tracepoint defines PID 0 as global total
+ // GPU ID 0 suffices for current android devices.
+ // This will need to check all GPU IDs in future if more than
+ // one is GPU device is present on the device.
+ return ReadProcessGpuUsageKb(0, 0, size);
+}
+
} // namespace meminfo
} // namespace android
diff --git a/tools/Android.bp b/tools/Android.bp
index 8cb98fc..d336434 100644
--- a/tools/Android.bp
+++ b/tools/Android.bp
@@ -55,6 +55,7 @@
shared_libs: [
"libbase",
"libmeminfo",
+ "libprocinfo",
],
}
diff --git a/tools/procrank.cpp b/tools/procrank.cpp
index e7c1af9..e4f247a 100644
--- a/tools/procrank.cpp
+++ b/tools/procrank.cpp
@@ -25,6 +25,7 @@
#include <linux/oom.h>
#include <meminfo/procmeminfo.h>
#include <meminfo/sysmeminfo.h>
+#include <procinfo/process.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
@@ -32,7 +33,9 @@
#include <iostream>
#include <memory>
+#include <set>
#include <sstream>
+#include <unordered_map>
#include <vector>
using ::android::meminfo::MemUsage;
@@ -142,7 +145,7 @@
uint64_t total_zswap = 0;
[[noreturn]] static void usage(int exit_status) {
- std::cerr << "Usage: " << getprogname() << " [ -W ] [ -v | -r | -p | -u | -s | -h ]"
+ std::cerr << "Usage: " << getprogname() << " [ -W ] [ -v | -r | -p | -u | -s | -h ] [-d PID]"
<< std::endl
<< " -v Sort by VSS." << std::endl
<< " -r Sort by RSS." << std::endl
@@ -155,14 +158,15 @@
<< " -C Only show non-cached (ram/swap backed) pages" << std::endl
<< " -k Only show pages collapsed by KSM" << std::endl
<< " -w Display statistics for working set only." << std::endl
- << " -W Reset working set of all processes." << std::endl
+ << " -W Reset working set of processes." << std::endl
<< " -o Show and sort by oom score against lowmemorykiller thresholds."
<< std::endl
+ << " -d Filter to descendants of specified process (can be repeated)" << std::endl
<< " -h Display this help screen." << std::endl;
exit(exit_status);
}
-static bool read_all_pids(std::vector<pid_t>* pids, std::function<bool(pid_t pid)> for_each_pid) {
+static bool read_all_pids(std::set<pid_t>* pids) {
pids->clear();
std::unique_ptr<DIR, int (*)(DIR*)> procdir(opendir("/proc"), closedir);
if (!procdir) return false;
@@ -171,8 +175,7 @@
pid_t pid;
while ((dir = readdir(procdir.get()))) {
if (!::android::base::ParseInt(dir->d_name, &pid)) continue;
- if (!for_each_pid(pid)) return false;
- pids->emplace_back(pid);
+ pids->insert(pid);
}
return true;
@@ -375,8 +378,9 @@
uint64_t pgflags = 0;
uint64_t pgflags_mask = 0;
+ std::vector<pid_t> descendant_filter;
int opt;
- while ((opt = getopt(argc, argv, "cChkoprRsuvwW")) != -1) {
+ while ((opt = getopt(argc, argv, "cCd:hkoprRsuvwW")) != -1) {
switch (opt) {
case 'c':
pgflags = 0;
@@ -386,6 +390,15 @@
pgflags = (1 << KPF_SWAPBACKED);
pgflags_mask = (1 << KPF_SWAPBACKED);
break;
+ case 'd': {
+ pid_t p;
+ if (!android::base::ParseInt(optarg, &p)) {
+ std::cerr << "Failed to parse pid '" << optarg << "'" << std::endl;
+ usage(EXIT_FAILURE);
+ }
+ descendant_filter.push_back(p);
+ break;
+ }
case 'h':
usage(EXIT_SUCCESS);
case 'k':
@@ -425,13 +438,58 @@
}
}
- std::vector<pid_t> pids;
+ std::set<pid_t> pids;
std::vector<ProcessRecord> procs;
+ if (!read_all_pids(&pids)) {
+ std::cerr << "Failed to read pids" << std::endl;
+ exit(EXIT_FAILURE);
+ }
+
+ if (descendant_filter.size()) {
+ // Map from parent pid to all of its children.
+ std::unordered_map<pid_t, std::vector<pid_t>> pid_tree;
+
+ for (pid_t pid : pids) {
+ android::procinfo::ProcessInfo info;
+ std::string error;
+ if (!android::procinfo::GetProcessInfo(pid, &info, &error)) {
+ std::cerr << "warning: failed to get process info for: " << pid << ": " << error
+ << std::endl;
+ continue;
+ }
+
+ pid_tree[info.ppid].push_back(pid);
+ }
+
+ std::set<pid_t> final_pids;
+ std::vector<pid_t>& frontier = descendant_filter;
+
+ // Do a breadth-first walk of the process tree, starting from the pids we were given.
+ while (!frontier.empty()) {
+ pid_t pid = frontier.back();
+ frontier.pop_back();
+
+ // It's possible for the pid we're looking at to already be in our list if one of the
+ // passed in processes descends from another, or if the same pid is passed twice.
+ auto [it, inserted] = final_pids.insert(pid);
+ if (inserted) {
+ auto it = pid_tree.find(pid);
+ if (it != pid_tree.end()) {
+ // Add all of the children of |pid| to the list of nodes to visit.
+ frontier.insert(frontier.end(), it->second.begin(), it->second.end());
+ }
+ }
+ }
+
+ pids = std::move(final_pids);
+ }
+
if (reset_wss) {
- if (!read_all_pids(&pids,
- [&](pid_t pid) -> bool { return ProcMemInfo::ResetWorkingSet(pid); })) {
- std::cerr << "Failed to reset working set of all processes" << std::endl;
- exit(EXIT_FAILURE);
+ for (pid_t pid : pids) {
+ if (!ProcMemInfo::ResetWorkingSet(pid)) {
+ std::cerr << "Failed to reset working set of all processes" << std::endl;
+ exit(EXIT_FAILURE);
+ }
}
// we are done, all other options passed to procrank are ignored in the presence of '-W'
return 0;
@@ -456,42 +514,35 @@
}
}
- auto mark_swap_usage = [&](pid_t pid) -> bool {
+ // Mark each swap offset used by the process as we find them for calculating proportional
+ // swap usage later.
+ for (pid_t pid : pids) {
ProcessRecord proc(pid, show_wss, pgflags, pgflags_mask);
if (!proc.valid()) {
// Check to see if the process is still around, skip the process if the proc
// directory is inaccessible. It was most likely killed while creating the process
// record
std::string procdir = ::android::base::StringPrintf("/proc/%d", pid);
- if (access(procdir.c_str(), F_OK | R_OK)) return true;
+ if (access(procdir.c_str(), F_OK | R_OK)) continue;
// Warn if we failed to gather process stats even while it is still alive.
// Return success here, so we continue to print stats for other processes.
std::cerr << "warning: failed to create process record for: " << pid << std::endl;
- return true;
+ continue;
}
// Skip processes with no memory mappings
uint64_t vss = show_wss ? proc.Wss().vss : proc.Usage().vss;
- if (vss == 0) return true;
+ if (vss == 0) continue;
// collect swap_offset counts from all processes in 1st pass
- if (!show_wss && has_swap &&
- !count_swap_offsets(proc, swap_offset_array)) {
+ if (!show_wss && has_swap && !count_swap_offsets(proc, swap_offset_array)) {
std::cerr << "Failed to count swap offsets for process: " << pid << std::endl;
- return false;
+ std::cerr << "Failed to read all pids from the system" << std::endl;
+ exit(EXIT_FAILURE);
}
procs.emplace_back(std::move(proc));
- return true;
- };
-
- // Get a list of all pids currently running in the system in 1st pass through all processes.
- // Mark each swap offset used by the process as we find them for calculating proportional
- // swap usage later.
- if (!read_all_pids(&pids, mark_swap_usage)) {
- std::cerr << "Failed to read all pids from the system" << std::endl;
- exit(EXIT_FAILURE);
}
std::stringstream ss;
diff --git a/tools/showmap.cpp b/tools/showmap.cpp
index 36739b1..452e895 100644
--- a/tools/showmap.cpp
+++ b/tools/showmap.cpp
@@ -16,10 +16,10 @@
#include <getopt.h>
#include <inttypes.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
-#include <sys/signal.h>
#include <sys/types.h>
#include <unistd.h>
diff --git a/vts/Android.bp b/vts/Android.bp
index 0f514b5..ec7f58a 100644
--- a/vts/Android.bp
+++ b/vts/Android.bp
@@ -34,5 +34,7 @@
defaults: ["vts_meminfo_defaults"],
test_suites: ["vts"],
require_root: true,
- test_min_api_level: 29,
+ test_options: {
+ min_shipping_api_level: 29,
+ },
}
diff --git a/vts/vts_meminfo_test.cpp b/vts/vts_meminfo_test.cpp
index a9b2c26..cec46d5 100644
--- a/vts/vts_meminfo_test.cpp
+++ b/vts/vts_meminfo_test.cpp
@@ -94,7 +94,7 @@
TEST(SysMemInfo, TestGpuTotalUsageKb) {
uint64_t size;
- if (android::base::GetIntProperty("ro.product.first_api_level", 0) < __ANDROID_API_S__) {
+ if (android::base::GetIntProperty("ro.vendor.api_level", 0) < __ANDROID_API_S__) {
GTEST_SKIP();
}