1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
|
#!/usr/bin/env python
#
# Copyright (C) 2017 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 re
import shutil
import tempfile
from vts.runners.host import asserts
from vts.runners.host import base_test_with_webdb
from vts.runners.host import test_runner
from vts.utils.python.controllers import android_device
from vts.testcases.vndk.dependency import elf_parser
class VtsVndkDependencyTest(base_test_with_webdb.BaseTestWithWebDbClass):
"""A test case to verify vendor library dependency.
Attributes:
_temp_dir: The temporary directory to which the vendor partition is
copied.
_vendor_libs: Collection of strings. The names of the shared libraries
on vendor partition.
"""
_SHELL_NAME = "vendor_dep_test_shell"
_VENDOR_PATH = "/vendor"
_SAME_PROCESS_DIR_32 = "/vendor/lib/sameprocess"
_SAME_PROCESS_DIR_64 = "/vendor/lib64/sameprocess"
_LOW_LEVEL_NDK = [
"libc.so",
"libm.so",
"libz.so",
"liblog.so",
"libdl.so",
"libstdc++.so"
]
_SAME_PROCESS_NDK = [re.compile(p) for p in [
"libEGL_.*\\.so$",
"libGLESv1_CM_.*\\.so$",
"libGLESv2_.*\\.so$",
"libGLESv3_.*\\.so$",
"vulkan.*\\.so$",
"libRSDriver.*\\.so$",
"libPVRRS\\.so$",
"android\\.hardware\\.graphics\\.mapper@\\d+\\.\\d+-impl\\.so$"
]]
def setUpClass(self):
"""Initializes device and temporary directory."""
self.dut = self.registerController(android_device)[0]
self.dut.shell.InvokeTerminal(self._SHELL_NAME)
self._temp_dir = tempfile.mkdtemp()
logging.info("adb pull %s %s", self._VENDOR_PATH, self._temp_dir)
pull_output = self.dut.adb.pull(self._VENDOR_PATH, self._temp_dir)
logging.debug(pull_output)
self._vendor_libs = self._listSharedLibraries(self._temp_dir)
logging.info("Vendor libraries: " + str(self._vendor_libs))
def tearDownClass(self):
"""Deletes the temporary directory."""
logging.info("Delete %s", self._temp_dir)
shutil.rmtree(self._temp_dir)
def _isSameProcessLibrary(self, lib_name):
"""Checks whether a library is same-process.
Args:
lib_name: String. The name of the library.
Returns:
A boolean representing whether the library is same-process.
"""
for pattern in self._SAME_PROCESS_NDK:
if pattern.match(lib_name):
return True
return False
def _isAllowedDependency(self, lib_name):
"""Checks whether a library dependency is allowed.
A vendor library/executable is only allowed to depend on
- Low-level NDK
- Same-process NDK
- Other libraries on vendor partition
Args:
lib_name: String. The name of the depended library.
Returns:
A boolean representing whether the library is allowed.
"""
if lib_name in self._vendor_libs or lib_name in self._LOW_LEVEL_NDK:
return True
if self._isSameProcessLibrary(lib_name):
return True
return False
@staticmethod
def _iterateFiles(dir_path):
"""A generator yielding regular files in a directory recursively.
Args:
dir_path: String. The path to search.
Yields:
A tuple of strings (directory, file). The directory containing
the file and the file name.
"""
for root_dir, dir_names, file_names in os.walk(dir_path):
for file_name in file_names:
yield root_dir, file_name
@staticmethod
def _listSharedLibraries(path):
"""Finds all shared libraries under a directory.
Args:
path: String. The path to search.
Returns:
Set of strings. The names of the found libraries.
"""
results = set()
for root_dir, file_name in VtsVndkDependencyTest._iterateFiles(path):
if file_name.endswith(".so"):
results.add(file_name)
return results
def testSameProcessLibrary(self):
"""Checks if same-process directory contains only allowed libraries."""
dev_sp_dirs = [self._SAME_PROCESS_DIR_32]
if self.dut.is64Bit:
dev_sp_dirs.append(self._SAME_PROCESS_DIR_64)
error_count = 0
for dev_sp_dir in dev_sp_dirs:
sp_dir = os.path.join(self._temp_dir, dev_sp_dir)
if not os.path.isdir(sp_dir):
logging.warning("%s is not a directory", sp_dir)
continue
logging.info("Enter %s", sp_dir)
for root_dir, file_name in self._iterateFiles(sp_dir):
full_path = os.path.join(root_dir, file_name)
if self._isSameProcessLibrary(file_name):
logging.info("%s is a same-process lib", full_path)
continue
error_count += 1
logging.error("%s is not a same-process lib", full_path)
asserts.assertEqual(error_count, 0,
"Total number of errors: " + str(error_count))
def testElfDependency(self):
"""Scans library/executable dependency on vendor partition."""
if not elf_parser.ElfParser.isSupported():
asserts.fail("readelf is not available")
error_count = 0
for root_dir, file_name in self._iterateFiles(self._temp_dir):
file_path = os.path.join(root_dir, file_name)
elf = elf_parser.ElfParser(file_path)
if not elf.isValid():
logging.info("%s is not an ELF file", file_path)
continue
try:
dep_libs = elf.listDependencies()
except OSError as e:
error_count += 1
logging.exception("Cannot read %s: %s", file_path, str(e))
continue
logging.info("%s depends on: %s", file_path, str(dep_libs))
disallowed_libs = filter(
lambda x: not self._isAllowedDependency(x), dep_libs)
if not disallowed_libs:
continue
error_count += 1
logging.error("%s depends on disallowed libs: %s",
file_path.replace(self._temp_dir, "", 1),
str(disallowed_libs))
asserts.assertEqual(error_count, 0,
"Total number of errors: " + str(error_count))
if __name__ == "__main__":
test_runner.main()
|