summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2020-02-20 02:50:17 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2020-02-20 02:50:17 +0000
commit661fa1f6f4d74b85266e84a77627d94f57fb08db (patch)
tree34636d6fd53f4ef7297f1023cb0b2cf248018ff3
parentd50c3b1fb275a7b94462d1de60f7aa3f0abf9c5e (diff)
parent2cf102850813907fbb7d5cace81e2e0b0eb5189d (diff)
downloadvndk-661fa1f6f4d74b85266e84a77627d94f57fb08db.tar.gz
Add vts_vndk_dependency_test am: f4f34abd42 am: 3e4430fd2b am: 2cf1028508
Change-Id: I85307674a8038299e0e5ec86d837e118fcd9c46e
-rw-r--r--Android.bp60
-rw-r--r--dependency/VtsVndkDependencyTest.py2
-rw-r--r--dependency/vts_vndk_dependency_test.py441
-rw-r--r--golden/vndk_data.py81
-rw-r--r--utils.py165
5 files changed, 733 insertions, 16 deletions
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..18a2b91
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,60 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// 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.
+
+python_library {
+ name: "vts_vndk_utils",
+ pkg_path: "vts/testcases/vndk",
+ srcs: [
+ "utils.py",
+ "golden/vndk_data.py",
+ ],
+ data: [
+ ":vndk_lib_lists",
+ ":vndk_lib_extra_lists"
+ ],
+ version: {
+ py2: {
+ enabled: true,
+ },
+ py3: {
+ enabled: true,
+ },
+ },
+}
+
+python_defaults {
+ name: "vts_vndk_default",
+ libs: [
+ "vndk_utils",
+ "vts_vndk_utils",
+ ],
+ version: {
+ py2: {
+ enabled: false,
+ },
+ py3: {
+ enabled: true,
+ embedded_launcher: true,
+ }
+ }
+}
+
+python_test {
+ name: "vts_vndk_dependency_test",
+ defaults: ["vts_vndk_default"],
+ main: "dependency/vts_vndk_dependency_test.py",
+ srcs: [
+ "dependency/vts_vndk_dependency_test.py",
+ ]
+}
diff --git a/dependency/VtsVndkDependencyTest.py b/dependency/VtsVndkDependencyTest.py
index 49ac575..f99e670 100644
--- a/dependency/VtsVndkDependencyTest.py
+++ b/dependency/VtsVndkDependencyTest.py
@@ -15,6 +15,8 @@
# limitations under the License.
#
+# TODO(b/147454897): Keep the test logic in sync with vts_vndk_dependency.py
+# until this file is removed.
import collections
import logging
import os
diff --git a/dependency/vts_vndk_dependency_test.py b/dependency/vts_vndk_dependency_test.py
new file mode 100644
index 0000000..6541a80
--- /dev/null
+++ b/dependency/vts_vndk_dependency_test.py
@@ -0,0 +1,441 @@
+#!/usr/bin/env python
+#
+# 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.
+#
+
+# TODO(b/147454897): Keep the test logic in sync with VtsVndkDependency.py
+# until it is removed.
+import collections
+import logging
+import os
+import re
+import unittest
+
+from vts.testcases.vndk import utils
+from vts.testcases.vndk.golden import vndk_data
+from vts.utils.python.library import elf_parser
+from vts.utils.python.vndk import vndk_utils
+
+
+class VtsVndkDependencyTest(unittest.TestCase):
+ """A test case to verify vendor library dependency.
+
+ Attributes:
+ _dut: The AndroidDevice under test.
+ _ll_ndk: Set of strings. The names of low-level NDK libraries in
+ /system/lib[64].
+ _sp_hal: List of patterns. The names of the same-process HAL libraries
+ expected to be in /vendor/lib[64].
+ _vndk: Set of strings. The names of VNDK-core libraries.
+ _vndk_sp: Set of strings. The names of VNDK-SP libraries.
+ _SP_HAL_LINK_PATHS: Format strings of same-process HAL's link paths.
+ _VENDOR_LINK_PATHS: Format strings of vendor processes' link paths.
+ """
+ _TARGET_DIR_SEP = "/"
+ _TARGET_ODM_DIR = "/odm"
+ _TARGET_VENDOR_DIR = "/vendor"
+
+ _SP_HAL_LINK_PATHS = [
+ "/odm/{LIB}/egl", "/odm/{LIB}/hw", "/odm/{LIB}",
+ "/vendor/{LIB}/egl", "/vendor/{LIB}/hw", "/vendor/{LIB}"
+ ]
+ _VENDOR_LINK_PATHS = [
+ "/odm/{LIB}/hw", "/odm/{LIB}/egl", "/odm/{LIB}",
+ "/vendor/{LIB}/hw", "/vendor/{LIB}/egl", "/vendor/{LIB}"
+ ]
+ _DEFAULT_PROGRAM_INTERPRETERS = [
+ "/system/bin/linker", "/system/bin/linker64"
+ ]
+
+ class ElfObject(object):
+ """Contains dependencies of an ELF file on target device.
+
+ Attributes:
+ target_path: String. The path to the ELF file on target.
+ name: String. File name of the ELF.
+ target_dir: String. The directory containing the ELF file on
+ target.
+ bitness: Integer. Bitness of the ELF.
+ deps: List of strings. The names of the depended libraries.
+ runpaths: List of strings. The library search paths.
+ """
+
+ def __init__(self, target_path, bitness, deps, runpaths):
+ self.target_path = target_path
+ self.name = os.path.basename(target_path)
+ self.target_dir = os.path.dirname(target_path)
+ self.bitness = bitness
+ self.deps = deps
+ # Format runpaths
+ self.runpaths = []
+ lib_dir_name = "lib64" if bitness == 64 else "lib"
+ for runpath in runpaths:
+ path = runpath.replace("${LIB}", lib_dir_name)
+ path = path.replace("$LIB", lib_dir_name)
+ path = path.replace("${ORIGIN}", self.target_dir)
+ path = path.replace("$ORIGIN", self.target_dir)
+ self.runpaths.append(path)
+
+ def setUp(self):
+ """Initializes device, temporary directory, and VNDK lists."""
+ self._dut = utils.AndroidDevice()
+ self.assertTrue(self._dut.IsRoot(), "This test requires adb root.")
+
+ vndk_lists = vndk_data.LoadVndkLibraryListsFromResources(
+ self._dut.GetVndkVersion(),
+ vndk_data.SP_HAL,
+ vndk_data.LL_NDK,
+ vndk_data.VNDK,
+ vndk_data.VNDK_SP)
+ self.assertTrue(vndk_lists, "Cannot load VNDK library lists.")
+
+ sp_hal_strings = vndk_lists[0]
+ self._sp_hal = [re.compile(x) for x in sp_hal_strings]
+ (self._ll_ndk, self._vndk, self._vndk_sp) = vndk_lists[1:]
+
+ logging.debug("LL_NDK: %s", self._ll_ndk)
+ logging.debug("SP_HAL: %s", sp_hal_strings)
+ logging.debug("VNDK: %s", self._vndk)
+ logging.debug("VNDK_SP: %s", self._vndk_sp)
+
+ def _IsElfObjectForAp(self, elf, target_path, abi_list):
+ """Checks whether an ELF object is for application processor.
+
+ Args:
+ elf: The object of elf_parser.ElfParser.
+ target_path: The path to the ELF file on target.
+ abi_list: A list of strings, the ABIs of the application processor.
+
+ Returns:
+ A boolean, whether the ELF object is for application processor.
+ """
+ if not any(elf.MatchCpuAbi(x) for x in abi_list):
+ logging.debug("%s does not match the ABI", target_path)
+ return False
+
+ # b/115567177 Skip an ELF file if it meets the following 3 conditions:
+ # The ELF type is executable.
+ if not elf.IsExecutable():
+ return True
+
+ # It requires special program interpreter.
+ interp = elf.GetProgramInterpreter()
+ if not interp or interp in self._DEFAULT_PROGRAM_INTERPRETERS:
+ return True
+
+ # It does not have execute permission in the file system.
+ if self._dut.IsExecutable(target_path):
+ return True
+
+ return False
+
+ def _IsElfObjectBuiltForAndroid(self, elf, target_path):
+ """Checks whether an ELF object is built for Android.
+
+ Some ELF objects in vendor partition require special program
+ interpreters. Such executable files have .interp sections, but shared
+ libraries don't. As there is no reliable way to identify those
+ libraries. This method checks .note.android.ident section which is
+ created by Android build system.
+
+ Args:
+ elf: The object of elf_parser.ElfParser.
+ target_path: The path to the ELF file on target.
+
+ Returns:
+ A boolean, whether the ELF object is built for Android.
+ """
+ # b/133399940 Skip an ELF file if it does not have .note.android.ident
+ # section and meets one of the following conditions:
+ if elf.HasAndroidIdent():
+ return True
+
+ # It's in the specific directory and is a shared library.
+ if (target_path.startswith("/vendor/arib/lib/") and
+ ".so" in target_path and
+ elf.IsSharedObject()):
+ return False
+
+ # It's in the specific directory, requires special program interpreter,
+ # and is executable.
+ if target_path.startswith("/vendor/arib/bin/"):
+ interp = elf.GetProgramInterpreter()
+ if interp and interp not in self._DEFAULT_PROGRAM_INTERPRETERS:
+ if elf.IsExecutable() or self._dut.IsExecutable(target_path):
+ return False
+
+ return True
+
+ @staticmethod
+ def _IterateFiles(host_dir):
+ """Iterates files in a host directory.
+
+ Args:
+ host_dir: The host directory.
+
+ Yields:
+ The file paths under the directory.
+ """
+ for root_dir, dir_names, file_names in os.walk(host_dir):
+ for file_name in file_names:
+ yield os.path.join(root_dir, file_name)
+
+ def _LoadElfObjects(self, target_dir, abi_list, elf_error_handler):
+ """Scans a host directory recursively and loads all ELF files in it.
+
+ Args:
+ target_dir: The host directory to scan.
+ abi_list: A list of strings, the ABIs of the ELF files to load.
+ elf_error_handler: A function that takes 2 arguments
+ (path, exception). It is called when
+ the parser fails to read an ELF file.
+
+ Returns:
+ List of ElfObject.
+ """
+ objs = []
+ for target_path in self._IterateFiles(target_dir):
+ try:
+ elf = elf_parser.ElfParser(target_path)
+ except elf_parser.ElfError:
+ logging.debug("%s is not an ELF file", target_path)
+ continue
+ try:
+ if not self._IsElfObjectForAp(elf, target_path, abi_list):
+ logging.info("%s is not for application processor",
+ target_path)
+ continue
+ if not self._IsElfObjectBuiltForAndroid(elf, target_path):
+ logging.warning("%s is not built for Android, which is no "
+ "longer exempted.", target_path)
+
+ deps, runpaths = elf.ListDependencies()
+ except elf_parser.ElfError as e:
+ elf_error_handler(target_path, e)
+ continue
+ finally:
+ elf.Close()
+
+ logging.info("%s depends on: %s", target_path, ", ".join(deps))
+ if runpaths:
+ logging.info("%s has runpaths: %s",
+ target_path, ":".join(runpaths))
+ objs.append(self.ElfObject(target_path, elf.bitness, deps,
+ runpaths))
+ return objs
+
+ def _FindLibsInLinkPaths(self, bitness, link_paths, objs):
+ """Finds libraries in link paths.
+
+ Args:
+ bitness: 32 or 64, the bitness of the returned libraries.
+ link_paths: List of strings, the default link paths.
+ objs: List of ElfObject, the libraries/executables to be filtered
+ by bitness and path.
+
+ Returns:
+ A defaultdict, {dir: {name: obj}} where obj is an ElfObject, dir
+ is obj.target_dir, and name is obj.name.
+ """
+ namespace = collections.defaultdict(dict)
+ for obj in objs:
+ if (obj.bitness == bitness and
+ any(obj.target_path.startswith(link_path +
+ self._TARGET_DIR_SEP)
+ for link_path in link_paths)):
+ namespace[obj.target_dir][obj.name] = obj
+ return namespace
+
+ def _DfsDependencies(self, lib, searched, namespace, link_paths):
+ """Depth-first-search for library dependencies.
+
+ Args:
+ lib: ElfObject, the library to search for dependencies.
+ searched: The set of searched libraries.
+ namespace: Defaultdict, {dir: {name: obj}} containing all
+ searchable libraries.
+ link_paths: List of strings, the default link paths.
+ """
+ if lib in searched:
+ return
+ searched.add(lib)
+ for dep_name in lib.deps:
+ for link_path in lib.runpaths + link_paths:
+ if dep_name in namespace[link_path]:
+ self._DfsDependencies(namespace[link_path][dep_name],
+ searched, namespace, link_paths)
+ break
+
+ def _FindDisallowedDependencies(self, objs, namespace, link_paths,
+ *vndk_lists):
+ """Tests if libraries/executables have disallowed dependencies.
+
+ Args:
+ objs: Collection of ElfObject, the libraries/executables under
+ test.
+ namespace: Defaultdict, {dir: {name: obj}} containing all libraries
+ in the linker namespace.
+ link_paths: List of strings, the default link paths.
+ vndk_lists: Collections of library names in VNDK, VNDK-SP, etc.
+
+ Returns:
+ List of tuples (path, disallowed_dependencies).
+ """
+ dep_errors = []
+ for obj in objs:
+ disallowed_libs = []
+ for dep_name in obj.deps:
+ if any((dep_name in vndk_list) for vndk_list in vndk_lists):
+ continue
+ if any((dep_name in namespace[link_path]) for link_path in
+ obj.runpaths + link_paths):
+ continue
+ disallowed_libs.append(dep_name)
+
+ if disallowed_libs:
+ dep_errors.append((obj.target_path, disallowed_libs))
+ return dep_errors
+
+ def _TestElfDependency(self, bitness, objs):
+ """Tests vendor libraries/executables and SP-HAL dependencies.
+
+ Args:
+ bitness: 32 or 64, the bitness of the vendor libraries.
+ objs: List of ElfObject. The libraries/executables in odm and
+ vendor partitions.
+
+ Returns:
+ List of tuples (path, disallowed_dependencies).
+ """
+ vndk_sp_ext_dirs = vndk_utils.GetVndkSpExtDirectories(bitness)
+
+ vendor_link_paths = [vndk_utils.FormatVndkPath(x, bitness) for
+ x in self._VENDOR_LINK_PATHS]
+ vendor_namespace = self._FindLibsInLinkPaths(bitness,
+ vendor_link_paths, objs)
+ # Exclude VNDK and VNDK-SP extensions from vendor libraries.
+ for vndk_ext_dir in (vndk_utils.GetVndkExtDirectories(bitness) +
+ vndk_utils.GetVndkSpExtDirectories(bitness)):
+ vendor_namespace.pop(vndk_ext_dir, None)
+ logging.info("%d-bit odm, vendor, and SP-HAL libraries:", bitness)
+ for dir_path, libs in vendor_namespace.items():
+ logging.info("%s: %s", dir_path, ",".join(libs.keys()))
+
+ sp_hal_link_paths = [vndk_utils.FormatVndkPath(x, bitness) for
+ x in self._SP_HAL_LINK_PATHS]
+ sp_hal_namespace = self._FindLibsInLinkPaths(bitness,
+ sp_hal_link_paths, objs)
+
+ # Find same-process HAL and dependencies
+ sp_hal_libs = set()
+ for link_path in sp_hal_link_paths:
+ for obj in sp_hal_namespace[link_path].values():
+ if any(x.match(obj.target_path) for x in self._sp_hal):
+ self._DfsDependencies(obj, sp_hal_libs, sp_hal_namespace,
+ sp_hal_link_paths)
+ logging.info("%d-bit SP-HAL libraries: %s",
+ bitness, ", ".join(x.name for x in sp_hal_libs))
+
+ # Find VNDK-SP extension libraries and their dependencies.
+ vndk_sp_ext_libs = set(obj for obj in objs if
+ obj.bitness == bitness and
+ obj.target_dir in vndk_sp_ext_dirs)
+ vndk_sp_ext_deps = set()
+ for lib in vndk_sp_ext_libs:
+ self._DfsDependencies(lib, vndk_sp_ext_deps, sp_hal_namespace,
+ sp_hal_link_paths)
+ logging.info("%d-bit VNDK-SP extension libraries and dependencies: %s",
+ bitness, ", ".join(x.name for x in vndk_sp_ext_deps))
+
+ # A vendor library/executable is allowed to depend on
+ # LL-NDK
+ # VNDK
+ # VNDK-SP
+ # Other libraries in vendor link paths
+ vendor_objs = {obj for obj in objs if
+ obj.bitness == bitness and
+ obj not in sp_hal_libs and
+ obj not in vndk_sp_ext_deps}
+ dep_errors = self._FindDisallowedDependencies(
+ vendor_objs, vendor_namespace, vendor_link_paths,
+ self._ll_ndk, self._vndk, self._vndk_sp)
+
+ # A VNDK-SP extension library/dependency is allowed to depend on
+ # LL-NDK
+ # VNDK-SP
+ # Libraries in vendor link paths
+ # Other VNDK-SP extension libraries, which is a subset of VNDK-SP
+ #
+ # However, it is not allowed to indirectly depend on VNDK. i.e., the
+ # depended vendor libraries must not depend on VNDK.
+ #
+ # vndk_sp_ext_deps and sp_hal_libs may overlap. Their dependency
+ # restrictions are the same.
+ dep_errors.extend(self._FindDisallowedDependencies(
+ vndk_sp_ext_deps - sp_hal_libs, vendor_namespace,
+ vendor_link_paths, self._ll_ndk, self._vndk_sp))
+
+ if not vndk_utils.IsVndkRuntimeEnforced(self._dut):
+ logging.warning("Ignore dependency errors: %s", dep_errors)
+ dep_errors = []
+
+ # A same-process HAL library is allowed to depend on
+ # LL-NDK
+ # VNDK-SP
+ # Other same-process HAL libraries and dependencies
+ dep_errors.extend(self._FindDisallowedDependencies(
+ sp_hal_libs, sp_hal_namespace, sp_hal_link_paths,
+ self._ll_ndk, self._vndk_sp))
+ return dep_errors
+
+ def testElfDependency(self):
+ """Tests vendor libraries/executables and SP-HAL dependencies."""
+ read_errors = []
+ abi_list = self._dut.GetCpuAbiList()
+ objs = []
+ for target_dir in (self._TARGET_ODM_DIR, self._TARGET_VENDOR_DIR):
+ if self._dut.IsDirectory(target_dir):
+ objs.extend(self._LoadElfObjects(
+ target_dir, abi_list,
+ lambda p, e: read_errors.append((p, str(e)))))
+
+ dep_errors = self._TestElfDependency(32, objs)
+ if self._dut.GetCpuAbiList(64):
+ dep_errors.extend(self._TestElfDependency(64, objs))
+
+ assert_lines = []
+ if read_errors:
+ error_lines = ["%s: %s" % (x[0], x[1]) for x in read_errors]
+ logging.error("%d read errors:\n%s",
+ len(read_errors), "\n".join(error_lines))
+ assert_lines.extend(error_lines[:20])
+
+ if dep_errors:
+ error_lines = ["%s: %s" % (x[0], ", ".join(x[1]))
+ for x in dep_errors]
+ logging.error("%d disallowed dependencies:\n%s",
+ len(dep_errors), "\n".join(error_lines))
+ assert_lines.extend(error_lines[:20])
+
+ error_count = len(read_errors) + len(dep_errors)
+ if error_count:
+ if error_count > len(assert_lines):
+ assert_lines.append("...")
+ assert_lines.append("Total number of errors: " + str(error_count))
+ self.fail("\n".join(assert_lines))
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/golden/vndk_data.py b/golden/vndk_data.py
index e215a91..4eb127d 100644
--- a/golden/vndk_data.py
+++ b/golden/vndk_data.py
@@ -17,6 +17,12 @@
import collections
import logging
import os
+import re
+
+try:
+ from importlib import resources
+except ImportError:
+ resources = None
# The tags in VNDK list:
# Low-level NDK libraries that can be used by framework and vendor modules.
@@ -48,6 +54,9 @@ _ABI_NAMES = ("arm64", "arm", "mips64", "mips", "x86_64", "x86")
# The data directory.
_GOLDEN_DIR = os.path.join("vts", "testcases", "vndk", "golden")
+# The data package.
+_RESOURCE_PACKAGE = "vts.testcases.vndk";
+
# Regular expression prefix for library name patterns.
_REGEX_PREFIX = "[regex]"
@@ -108,32 +117,31 @@ def GetAbiDumpDirectory(data_file_path, version, binder_bitness, abi_name,
return dump_dir
-def _LoadVndkLibraryListsFile(vndk_lists, tags, vndk_lib_list_path):
+def _LoadVndkLibraryListsFile(vndk_lists, tags, vndk_lib_list_file):
"""Load VNDK libraries from the file to the specified tuple.
Args:
vndk_lists: The output tuple of lists containing library names.
tags: Strings, the tags of the libraries to find.
- vndk_lib_list_path: The path to load the VNDK library list.
+ vndk_lib_list_file: The file object containing the VNDK library list.
"""
lib_sets = collections.defaultdict(set)
# Load VNDK tags from the list.
- with open(vndk_lib_list_path) as vndk_lib_list_file:
- for line in vndk_lib_list_file:
- # Ignore comments.
- if line.startswith('#'):
- continue
+ for line in vndk_lib_list_file:
+ # Ignore comments.
+ if line.startswith('#'):
+ continue
- # Split columns.
- cells = line.split(': ', 1)
- if len(cells) < 2:
- continue
- tag = cells[0]
- lib_name = cells[1].strip()
+ # Split columns.
+ cells = line.split(': ', 1)
+ if len(cells) < 2:
+ continue
+ tag = cells[0]
+ lib_name = cells[1].strip()
- lib_sets[tag].add(lib_name)
+ lib_sets[tag].add(lib_name)
# Compute VNDK-core-private and VNDK-SP-private.
private = lib_sets.get('VNDK-private', set())
@@ -187,6 +195,47 @@ def LoadVndkLibraryLists(data_file_path, version, *tags):
vndk_lists = tuple([] for x in tags)
- _LoadVndkLibraryListsFile(vndk_lists, tags, vndk_lib_list_path)
- _LoadVndkLibraryListsFile(vndk_lists, tags, vndk_lib_extra_list_path)
+ with open(vndk_lib_list_path, "r") as f:
+ _LoadVndkLibraryListsFile(vndk_lists, tags, f)
+ with open(vndk_lib_extra_list_path, "r") as f:
+ _LoadVndkLibraryListsFile(vndk_lists, tags, f)
+ return vndk_lists
+
+
+def LoadVndkLibraryListsFromResources(version, *tags):
+ """Find the VNDK libraries with specific tags in resources.
+
+ Args:
+ version: A string, the VNDK version.
+ *tags: Strings, the tags of the libraries to find.
+
+ Returns:
+ A tuple of lists containing library names. Each list corresponds to
+ one tag in the argument. For SP-HAL, the returned names are regular
+ expressions.
+ None if the VNDK list for the version is not found.
+ """
+ if not resources:
+ logging.error("Could not import resources module.")
+ return None
+
+ version_str = (version if version and re.match("\\d+", version) else
+ "current")
+ vndk_lib_list_name = version_str + ".txt"
+ vndk_lib_extra_list_name = "vndk-lib-extra-list-" + version_str + ".txt"
+
+ if not resources.is_resource(_RESOURCE_PACKAGE, vndk_lib_list_name):
+ logging.warning("Cannot load %s.", vndk_lib_list_name)
+ return None
+
+ if not resources.is_resource(_RESOURCE_PACKAGE, vndk_lib_extra_list_name):
+ logging.warning("Cannot load %s.", vndk_lib_extra_list_name)
+ return None
+
+ vndk_lists = tuple([] for x in tags)
+
+ with resources.open_text(_RESOURCE_PACKAGE, vndk_lib_list_name) as f:
+ _LoadVndkLibraryListsFile(vndk_lists, tags, f)
+ with resources.open_text(_RESOURCE_PACKAGE, vndk_lib_extra_list_name) as f:
+ _LoadVndkLibraryListsFile(vndk_lists, tags, f)
return vndk_lists
diff --git a/utils.py b/utils.py
new file mode 100644
index 0000000..8e6cc52
--- /dev/null
+++ b/utils.py
@@ -0,0 +1,165 @@
+#!/usr/bin/env python
+#
+# 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.
+#
+
+# TODO(b/147454897): Keep the logic in sync with
+# test/vts/utils/python/controllers/android_device.py until
+# it is removed.
+import logging
+import subprocess
+
+class AndroidDevice(object):
+ """This class controls the device via adb commands."""
+
+ def _ExecuteCommand(self, *cmd):
+ """Executes a command.
+
+ Args:
+ args: Strings, the arguments.
+
+ Returns:
+ Stdout as a string, stderr as a string, and return code as an
+ integer.
+ """
+ proc = subprocess.Popen(cmd, shell=False, stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ out, err = proc.communicate()
+ # Compatible with python2 and python3
+ if not isinstance(out, str):
+ out = out.decode("utf-8")
+ if not isinstance(err, str):
+ err = err.decode("utf-8")
+ return out, err, proc.returncode
+
+ def _GetProp(self, name):
+ """Gets an Android system property.
+
+ Args:
+ name: A string, the property name.
+
+ Returns:
+ A string, the value of the property.
+
+ Raises:
+ IOError if the command fails.
+ """
+ out, err, return_code = self._ExecuteCommand("getprop", name)
+ if err.strip() or return_code != 0:
+ raise IOError("`getprop %s` stdout: %s\nstderr: %s" %
+ (name, out, err))
+ return out.strip()
+
+ def GetCpuAbiList(self, bitness=""):
+ """Gets the list of supported ABIs from property.
+
+ Args:
+ bitness: 32 or 64. If the argument is not specified, this method
+ returns both 32 and 64-bit ABIs.
+
+ Returns:
+ A list of strings, the supported ABIs.
+ """
+ out = self._GetProp("ro.product.cpu.abilist" + str(bitness))
+ return out.lower().split(",") if out else []
+
+ def GetLaunchApiLevel(self):
+ """Gets the API level that the device was initially launched with.
+
+ This method reads ro.product.first_api_level from the device. If the
+ value is 0, it then reads ro.build.version.sdk.
+
+ Returns:
+ An integer, the API level.
+ """
+ level_str = self._GetProp("ro.product.first_api_level")
+ level = int(level_str)
+ if level != 0:
+ return level
+
+ level_str = self._GetProp("ro.build.version.sdk")
+ return int(level_str)
+
+ def getLaunchApiLevel(self, strict=True):
+ """Gets the API level that the device was initially launched with.
+
+ This method is compatible with vndk_utils in vts package.
+
+ Args:
+ strict: A boolean, whether to raise an error if the property is
+ not an integer or not defined.
+
+ Returns:
+ An integer, the API level.
+ 0 if the value is undefined and strict is False.
+
+ Raises:
+ ValueError: if the value is undefined and strict is True.
+ """
+ try:
+ return self.GetLaunchApiLevel()
+ except ValueError as e:
+ if strict:
+ raise
+ logging.exception(e)
+ return 0
+
+ @property
+ def vndk_lite(self):
+ """Checks whether the vendor partition requests lite VNDK enforcement.
+
+ This method is compatible with vndk_utils in vts package.
+
+ Returns:
+ A boolean, True for lite vndk enforcement.
+ """
+ return self._GetProp("ro.vndk.lite").lower() == "true"
+
+ def GetVndkVersion(self):
+ """Gets the VNDK version that the vendor partition requests."""
+ return self._GetProp("ro.vndk.version")
+
+ def IsRoot(self):
+ """Returns whether adb has root privilege on the device."""
+ out, err, return_code = self._ExecuteCommand("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("sh", "-c",
+ "test " + " ".join(args))
+ if out.strip() or err.strip():
+ raise IOError("`test` args: %s\nstdout: %s\nstderr: %s" %
+ (args, out, err))
+ return return_code == 0
+
+ def IsDirectory(self, path):
+ """Returns whether a path on the device is a directory."""
+ return self._Test("-d", path)
+
+ def _Stat(self, fmt, path):
+ """Executes stat command."""
+ out, err, return_code = self._ExecuteCommand("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))
+ return out.strip()
+
+ def IsExecutable(self, path):
+ """Returns if execute permission is granted to a path on the device."""
+ return "x" in self._Stat("%A", path)