summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHsin-Yi Chen <hsinyichen@google.com>2020-03-02 17:40:19 +0800
committerHsin-Yi Chen <hsinyichen@google.com>2020-03-09 18:16:52 +0800
commit2d354379a0ed78bf0a777bde65025557df077377 (patch)
tree10c0a6fa97d3c840080084324ac401664c9bb6e4
parente2e918fc67eb1984be7decce7115d871caddf13b (diff)
downloadvndk-2d354379a0ed78bf0a777bde65025557df077377.tar.gz
Add vts_vndk_open_libraries_test
This commit copies VtsVndkOpenLibrariesTest.py to vts_vndk_open_libraries_test.py, removes the dependency on VTS framework, and adds a python_test_host module for the new file. Bug: 147454897 Test: ANDROID_SERIAL=1234 \ LD_LIBRARY_PATH=$ANDROID_HOST_OUT/lib64 \ $ANDROID_HOST_OUT/nativetest64/vts_vndk_open_libraries_test/vts_vndk_open_libraries_test \ --verbose Change-Id: I0f35d8322045cedc0f1e43b3fb8eb7367c8e39b1 Merged-In: I0f35d8322045cedc0f1e43b3fb8eb7367c8e39b1 (cherry picked from commit 1d1c640b24f9ec493e3094197f3e4cf79a0ddf79)
-rw-r--r--Android.bp13
-rw-r--r--open_libraries/vts_vndk_open_libraries_test.py162
-rw-r--r--open_libraries/vts_vndk_open_libraries_test.xml23
-rw-r--r--utils.py17
4 files changed, 206 insertions, 9 deletions
diff --git a/Android.bp b/Android.bp
index 9eae205..176da18 100644
--- a/Android.bp
+++ b/Android.bp
@@ -75,3 +75,16 @@ python_test_host {
],
test_config: "files/vts_vndk_files_test.xml",
}
+
+python_test_host {
+ name: "vts_vndk_open_libraries_test",
+ defaults: ["vts_vndk_default"],
+ main: "open_libraries/vts_vndk_open_libraries_test.py",
+ srcs: [
+ "open_libraries/vts_vndk_open_libraries_test.py",
+ ],
+ test_suites: [
+ "vts-core",
+ ],
+ test_config: "open_libraries/vts_vndk_open_libraries_test.xml",
+}
diff --git a/open_libraries/vts_vndk_open_libraries_test.py b/open_libraries/vts_vndk_open_libraries_test.py
new file mode 100644
index 0000000..aac1b66
--- /dev/null
+++ b/open_libraries/vts_vndk_open_libraries_test.py
@@ -0,0 +1,162 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2020 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.
+#
+
+import logging
+import os
+import posixpath as target_path_module
+import re
+import unittest
+
+from vts.testcases.vndk import utils
+from vts.testcases.vndk.golden import vndk_data
+from vts.utils.python.vndk import vndk_utils
+
+
+class VtsVndkOpenLibrariesTest(unittest.TestCase):
+ """A test module to verify libraries opened by running processes.
+
+ Attributes:
+ _dut: The AndroidDevice under test.
+ """
+
+ def setUp(self):
+ """Initializes attributes."""
+ serial_number = os.environ.get("ANDROID_SERIAL")
+ self.assertTrue(serial_number, "$ANDROID_SERIAL is empty.")
+ self._dut = utils.AndroidDevice(serial_number)
+
+ def _ListProcessCommands(self, cmd_filter):
+ """Finds current processes whose commands match the filter.
+
+ Args:
+ cmd_filter: A function that takes a binary file path as argument
+ and returns whether the path matches the condition.
+
+ Returns:
+ A dict of {pid: command} where pid and command are strings.
+ """
+ ps_cmd = ["ps", "-Aw", "-o", "PID,COMMAND"]
+ out, err, return_code = self._dut.Execute(*ps_cmd)
+ if err.strip():
+ logging.info("`%s` stderr: %s", " ".join(ps_cmd), err)
+ self.assertEqual(return_code, 0)
+
+ lines = out.split("\n")
+ pid_end = lines[0].index("PID") + len("PID")
+ cmd_begin = lines[0].index("COMMAND", pid_end)
+ cmds = {}
+ for line in lines[1:]:
+ cmd = line[cmd_begin:]
+ if not cmd_filter(cmd):
+ continue
+ pid = line[:pid_end].lstrip()
+ cmds[pid] = cmd
+ return cmds
+
+ def _ListOpenFiles(self, pids, file_filter):
+ """Finds open files whose names match the filter.
+
+ Args:
+ pids: A collection of strings, the PIDs to list open files.
+ file_filter: A function that takes a file path as argument and
+ returns whether the path matches the condition.
+
+ Returns:
+ A dict of {pid: [file, ...]} where pid and file are strings.
+ """
+ lsof_cmd = ["lsof", "-p", ",".join(pids)]
+ out, err, return_code = self._dut.Execute(*lsof_cmd)
+ if err.strip():
+ logging.info("`%s` stderr: %s", " ".join(lsof), err)
+ self.assertEqual(return_code, 0)
+ # The first line consists of the column names:
+ # COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
+ # PID is right-justified. NAME is left-justified.
+ lines = out.split("\n")
+ pid_end = lines[0].index("PID") + len("PID")
+ name_begin = lines[0].index("NAME")
+ files = {}
+ for line in lines[1:]:
+ if not line.strip():
+ continue
+ # On Android, COMMAND may exceed the column and causes the right
+ # columns to be misaligned. This program looks for digits in the
+ # PID column or on the right of the column.
+ try:
+ match_pid = next(match for match in
+ re.finditer(r"\s(\d+)\s", line) if
+ match.end(1) >= pid_end)
+ except StopIteration:
+ self.fail("Cannot parse PID from lsof output: " + line)
+ offset = match_pid.end(1) - pid_end
+ self.assertEqual(line[name_begin + offset - 1], " ",
+ "Cannot parse NAME from lsof output: " + line)
+ name = line[name_begin + offset:]
+ if not file_filter(name):
+ continue
+ pid = match_pid.group(1)
+ if pid in files:
+ files[pid].append(name)
+ else:
+ files[pid] = [name]
+ return files
+
+ def testVendorProcessOpenLibraries(self):
+ """Checks if vendor processes load shared libraries on system."""
+ if not vndk_utils.IsVndkRuntimeEnforced(self._dut):
+ logging.info("Skip the test as VNDK runtime is not enforced on "
+ "the device.")
+ return
+ vndk_lists = vndk_data.LoadVndkLibraryListsFromResources(
+ self._dut.GetVndkVersion(),
+ vndk_data.LL_NDK,
+ vndk_data.LL_NDK_PRIVATE,
+ vndk_data.VNDK,
+ vndk_data.VNDK_PRIVATE,
+ vndk_data.VNDK_SP,
+ vndk_data.VNDK_SP_PRIVATE)
+ self.assertTrue(vndk_lists, "Cannot load VNDK library lists.")
+ allowed_libs = set().union(*vndk_lists)
+ logging.debug("Allowed system libraries: %s", allowed_libs)
+
+ self.assertTrue(self._dut.IsRoot(),
+ "Must be root to find all libraries in use.")
+ cmds = self._ListProcessCommands(lambda x: (x.startswith("/odm/") or
+ x.startswith("/vendor/")))
+
+ def _IsDisallowedSystemLib(lib_path):
+ return ((lib_path.startswith("/system/") or
+ lib_path.startswith("/apex/")) and
+ lib_path.endswith(".so") and
+ target_path_module.basename(lib_path) not in allowed_libs)
+
+ deps = self._ListOpenFiles(cmds.keys(), _IsDisallowedSystemLib)
+ if deps:
+ error_lines = ["%s %s %s" % (pid, cmds[pid], libs)
+ for pid, libs in deps.items()]
+ logging.error("pid command libraries\n%s", "\n".join(error_lines))
+
+ assert_lines = ["pid command libraries"] + error_lines[:20]
+ if len(deps) > 20:
+ assert_lines.append("...")
+ assert_lines.append("Number of vendor processes using system "
+ "libraries: " + str(len(deps)))
+ self.fail("\n".join(assert_lines))
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/open_libraries/vts_vndk_open_libraries_test.xml b/open_libraries/vts_vndk_open_libraries_test.xml
new file mode 100644
index 0000000..664f60e
--- /dev/null
+++ b/open_libraries/vts_vndk_open_libraries_test.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+<configuration description="Config for vts_vndk_open_libraries_test">
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+ <test class="com.android.tradefed.testtype.python.PythonBinaryHostTest">
+ <option name="par-file-name" value="vts_vndk_open_libraries_test" />
+ <option name="test-timeout" value="2m" />
+ </test>
+</configuration>
+
diff --git a/utils.py b/utils.py
index 0592bfa..38efdce 100644
--- a/utils.py
+++ b/utils.py
@@ -32,7 +32,7 @@ class AndroidDevice(object):
subprocess.check_call(cmd, shell=False, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- def _ExecuteCommand(self, *args):
+ def Execute(self, *args):
"""Executes a command.
Args:
@@ -66,7 +66,7 @@ class AndroidDevice(object):
Raises:
IOError if the command fails.
"""
- out, err, return_code = self._ExecuteCommand("getprop", name)
+ out, err, return_code = self.Execute("getprop", name)
if err.strip() or return_code != 0:
raise IOError("`getprop %s` stdout: %s\nstderr: %s" %
(name, out, err))
@@ -143,14 +143,14 @@ class AndroidDevice(object):
def IsRoot(self):
"""Returns whether adb has root privilege on the device."""
- out, err, return_code = self._ExecuteCommand("id")
+ out, err, return_code = self.Execute("id")
if err.strip() or return_code != 0:
raise IOError("`id` stdout: %s\nstderr: %s \n" % (out, err))
return "uid=0(root)" in out.strip()
def _Test(self, *args):
"""Tests file types and status."""
- out, err, return_code = self._ExecuteCommand("test", *args)
+ out, err, return_code = self.Execute("test", *args)
if out.strip() or err.strip():
raise IOError("`test` args: %s\nstdout: %s\nstderr: %s" %
(args, out, err))
@@ -166,8 +166,7 @@ class AndroidDevice(object):
def _Stat(self, fmt, path):
"""Executes stat command."""
- out, err, return_code = self._ExecuteCommand("stat", "--format", fmt,
- path)
+ out, err, return_code = self.Execute("stat", "--format", fmt, path)
if return_code != 0 or err.strip():
raise IOError("`stat --format %s %s` stdout: %s\nstderr: %s" %
(fmt, path, out, err))
@@ -194,9 +193,9 @@ class AndroidDevice(object):
"""
if '"' in name_pattern or "'" in name_pattern:
raise ValueError("File name pattern contains quotes.")
- out, err, return_code = self._ExecuteCommand("find", path, "-name",
- "'" + name_pattern + "'",
- *options)
+ out, err, return_code = self.Execute("find", path, "-name",
+ "'" + name_pattern + "'",
+ *options)
if return_code != 0 or err.strip():
raise IOError("`find %s -name '%s' %s` stdout: %s\nstderr: %s" %
(path, name_pattern, " ".join(options), out, err))