vndk-def: Fix sp-hal and vndk-stable computation.

This commit fixes the computation algorithm for sp-hal and vndk-stable.
Before this commit, vndk-stable are determined from a pre-defined set
and sp-hal might include AOSP libraries.  After this commit, we are
adding SP-HAL dependencies to VNDK-stable if the dependency is an AOSP
library.

Test: ./tests/test_elf_linker.py
Change-Id: I7df3374ff49ff33826f496c0fabcb3749efd4fb2
diff --git a/vndk/tools/definition-tool/README.md b/vndk/tools/definition-tool/README.md
index 35a533e..bad020f 100644
--- a/vndk/tools/definition-tool/README.md
+++ b/vndk/tools/definition-tool/README.md
@@ -220,21 +220,30 @@
         --vendor ${ANDROID_PRODUCT_OUT}/vendor
 
 
-## Same-Process HAL
+## Same-Process NDK, Same-Process HAL, and VNDK-stable
 
-VNDK Definition Tool supports the same-process HAL definition as well.  To find
-same-process HALs, run `sp-hal` subcommand:
+VNDK Definition Tool can define the same-process HAL as well.  To find SP-NDK,
+SP-HAL, and VNDK-stable, run `sp-lib` subcommand:
 
-    $ python3 vndk_definition_tool.py sp-hal \
+    $ python3 vndk_definition_tool.py sp-lib \
         --system ${ANDROID_PRODUCT_OUT}/system \
         --vendor ${ANDROID_PRODUCT_OUT}/vendor
 
-To find the dependencies of same-process HALs, add `--closure` option:
+The output consists of following categories:
 
-    $ python3 vndk_definition_tool.py sp-hal \
-        --system ${ANDROID_PRODUCT_OUT}/system \
-        --vendor ${ANDROID_PRODUCT_OUT}/vendor
-        --closure
+* **sp-hal** -- Pre-defined same-process HALs.
+
+* **sp-hal-dep** -- Shared libraries that are used by SP-HALs.  These libraries
+  have to be installed into /vendor/lib[64]/sameprocess as well.
+
+* **sp-hal-vndk-stable** -- VNDK stable libraries that are used by SP-HAL.
+
+* **sp-ndk** -- Pre-defined same-process NDK libraries.
+
+* **sp-ndk-vndk-stable** -- Shared libraries that are used by SP-NDKs.
+
+**vndk-stable** is the union set of **sp-hal-vndk-stable** and
+**sp-ndk-vndk-stable**.
 
 
 ## Python 2 Support
diff --git a/vndk/tools/definition-tool/tests/test_elf_linker.py b/vndk/tools/definition-tool/tests/test_elf_linker.py
index 037e626..60fdb4d 100755
--- a/vndk/tools/definition-tool/tests/test_elf_linker.py
+++ b/vndk/tools/definition-tool/tests/test_elf_linker.py
@@ -9,8 +9,8 @@
 import unittest
 
 from compat import StringIO
-from vndk_definition_tool import (BannedLibDict, ELF, ELFLinker, PT_SYSTEM,
-                                  PT_VENDOR)
+from vndk_definition_tool import (BannedLibDict, ELF, ELFLinker, GenericRefs,
+                                  PT_SYSTEM, PT_VENDOR)
 
 
 class GraphBuilder(object):
@@ -305,7 +305,7 @@
         self.assertNotIn('/system/lib/vndk-stable/libbar.so', vndk_stable)
         self.assertNotIn('/system/lib64/vndk-stable/libbar.so', vndk_stable)
 
-    def test_compute_sp_hal(self):
+    def test_compute_predefined_sp_hal(self):
         gb = GraphBuilder()
 
         # HIDL SP-HAL implementation.
@@ -341,7 +341,7 @@
         gb.resolve()
 
         # Compute SP-HAL.
-        sp_hals = set(lib.path for lib in gb.graph.compute_sp_hal(set(), False))
+        sp_hals = set(lib.path for lib in gb.graph.compute_predefined_sp_hal())
 
         for lib in ('lib', 'lib64'):
             # Check HIDL SP-HAL implementation.
@@ -379,38 +379,101 @@
             self.assertNotIn('/system/' + lib + '/libfoo.so', sp_hals)
             self.assertNotIn('/vendor/' + lib + '/libfoo.so', sp_hals)
 
-    def test_compute_sp_hal_closure(self):
+    def test_copmute_sp_lib(self):
+        # Create graph.
         gb = GraphBuilder()
 
-        libc = gb.add_lib(PT_SYSTEM, ELF.ELFCLASS64, 'libc')
+        # LL-NDK (should be excluded from result)
+        gb.add_multilib(PT_SYSTEM, 'libc')
 
-        libhidlbase = gb.add_lib(PT_SYSTEM, ELF.ELFCLASS64, 'libhidlbase')
+        # SP-NDK VNDK-stable
+        gb.add_multilib(PT_SYSTEM, 'libcutils_dep', dt_needed=['libc.so'])
+        gb.add_multilib(PT_SYSTEM, 'libcutils',
+                        dt_needed=['libc.so', 'libcutils_dep.so'])
 
-        libhidltransport = gb.add_lib(PT_SYSTEM, ELF.ELFCLASS64,
-                                      'libhidltransport')
+        # SP-NDK dependencies
+        gb.add_multilib(PT_SYSTEM, 'libutils',
+                        dt_needed=['libc.so', 'libcutils.so'])
 
-        gralloc_mapper = gb.add_lib(
-                PT_VENDOR, ELF.ELFCLASS64,
-                name='android.hardware.graphics.mapper@2.0-impl',
-                dt_needed=['libhidlbase.so', 'libhidltransport.so',
-                           'libc.so', 'gralloc_vnd.so'],
-                extra_dir='sameprocess')
+        # SP-NDK
+        gb.add_multilib(PT_SYSTEM, 'libEGL',
+                        dt_needed=['libc.so', 'libutils.so'])
 
-        gralloc_vnd = gb.add_lib(PT_VENDOR, ELF.ELFCLASS64, 'gralloc_vnd')
+        # SP-HAL dependencies
+        gb.add_multilib(PT_VENDOR, 'libllvm_vendor_dep')
+        gb.add_multilib(PT_VENDOR, 'libllvm_vendor',
+                        dt_needed=['libc.so', 'libllvm_vendor_dep.so'])
+
+        # SP-HAL VNDK-stable
+        gb.add_multilib(PT_SYSTEM, 'libhidlbase')
+        gb.add_multilib(PT_SYSTEM, 'libhidlmemory',
+                        dt_needed=['libhidlbase.so'])
+
+        # SP-HAL
+        gb.add_multilib(PT_VENDOR, 'libEGL_chipset', extra_dir='egl',
+                        dt_needed=['libc.so', 'libllvm_vendor.so',
+                                   'libhidlmemory.so'])
 
         gb.resolve()
 
-        vndk_stable = {libhidlbase, libhidltransport}
+        # Create generic reference.
+        class MockGenericRefs(object):
+            def classify_lib(self, lib):
+                if 'libllvm_vendor' in lib.path:
+                    return GenericRefs.NEW_LIB
+                return GenericRefs.EXPORT_EQUAL
 
-        sp_hal = gb.graph.compute_sp_hal(vndk_stable, closure=False)
-        sp_hal_closure = gb.graph.compute_sp_hal(vndk_stable, closure=True)
+        sp_hal, sp_hal_dep, sp_hal_vndk_stable, sp_ndk, sp_ndk_vndk_stable = \
+                gb.graph.compute_sp_lib(MockGenericRefs())
 
-        self.assertSetEqual({gralloc_mapper}, sp_hal)
+        self.assertEqual(2 * 1, len(sp_hal))
+        self.assertEqual(2 * 2, len(sp_hal_dep))
+        self.assertEqual(2 * 2, len(sp_hal_vndk_stable))
+        self.assertEqual(2 * 1, len(sp_ndk))
+        self.assertEqual(2 * 3, len(sp_ndk_vndk_stable))
 
-        self.assertSetEqual({gralloc_mapper, gralloc_vnd}, sp_hal_closure)
-        self.assertNotIn(libhidlbase, sp_hal_closure)
-        self.assertNotIn(libhidltransport, sp_hal_closure)
-        self.assertNotIn(libc, sp_hal_closure)
+        sp_hal = self._get_paths_from_nodes(sp_hal)
+        sp_hal_dep = self._get_paths_from_nodes(sp_hal_dep)
+        sp_hal_vndk_stable = self._get_paths_from_nodes(sp_hal_vndk_stable)
+        sp_ndk = self._get_paths_from_nodes(sp_ndk)
+        sp_ndk_vndk_stable = self._get_paths_from_nodes(sp_ndk_vndk_stable)
+
+        for lib_dir in ('lib', 'lib64'):
+            # SP-NDK dependencies
+            self.assertIn('/system/{}/libcutils.so'.format(lib_dir),
+                          sp_ndk_vndk_stable)
+            self.assertIn('/system/{}/libcutils_dep.so'.format(lib_dir),
+                          sp_ndk_vndk_stable)
+            self.assertIn('/system/{}/libutils.so'.format(lib_dir),
+                          sp_ndk_vndk_stable)
+
+            # SP-NDK
+            self.assertIn('/system/{}/libEGL.so'.format(lib_dir), sp_ndk)
+
+            # SP-HAL dependencies
+            self.assertIn('/vendor/{}/libllvm_vendor.so'.format(lib_dir),
+                          sp_hal_dep)
+            self.assertIn('/vendor/{}/libllvm_vendor_dep.so'.format(lib_dir),
+                          sp_hal_dep)
+
+            # SP-HAL VNDK-stable
+            self.assertIn('/system/{}/libhidlbase.so'.format(lib_dir),
+                          sp_hal_vndk_stable)
+            self.assertIn('/system/{}/libhidlmemory.so'.format(lib_dir),
+                          sp_hal_vndk_stable)
+
+            # SP-HAL
+            self.assertIn('/vendor/{}/egl/libEGL_chipset.so'.format(lib_dir),
+                          sp_hal)
+
+            # LL-NDK must be excluded.
+            libc_path = '/system/{}/libc.so'.format(lib_dir)
+            self.assertNotIn(libc_path, sp_hal)
+            self.assertNotIn(libc_path, sp_hal_dep)
+            self.assertNotIn(libc_path, sp_hal_vndk_stable)
+            self.assertNotIn(libc_path, sp_ndk)
+            self.assertNotIn(libc_path, sp_ndk_vndk_stable)
+
 
     def test_find_existing_vndk(self):
         gb = GraphBuilder()
diff --git a/vndk/tools/definition-tool/vndk_definition_tool.py b/vndk/tools/definition-tool/vndk_definition_tool.py
index bc85863..8f42b8f 100755
--- a/vndk/tools/definition-tool/vndk_definition_tool.py
+++ b/vndk/tools/definition-tool/vndk_definition_tool.py
@@ -917,25 +917,14 @@
         self._resolve_lib_set_deps(
                 self.lib64, self.lib64_resolver, generic_refs)
 
-    def compute_matched_libs(self, path_patterns, closure=False,
-                             is_excluded_libs=None):
-        patt = re.compile('|'.join('(?:' + p + ')' for p in path_patterns))
-
-        # Find libraries with matching paths.
-        libs = set()
+    def all_lib(self):
         for lib_set in self.lib_pt:
             for lib in lib_set.values():
-                if patt.match(lib.path):
-                    libs.add(lib)
+                yield lib
 
-        if closure:
-            # Compute transitive closure.
-            if not is_excluded_libs:
-                def is_excluded_libs(lib):
-                    return False
-            libs = self.compute_closure(libs, is_excluded_libs)
-
-        return libs
+    def compute_path_matched_lib(self, path_patterns):
+        patt = re.compile('|'.join('(?:' + p + ')' for p in path_patterns))
+        return set(lib for lib in self.all_lib() if patt.match(lib.path))
 
     def compute_predefined_vndk_stable(self):
         """Find all vndk stable libraries."""
@@ -975,13 +964,9 @@
             '^.*/libvintf\\.so$',
         )
 
-        def is_excluded_libs(lib):
-            return lib.is_ndk
+        return self.compute_path_matched_lib(path_patterns)
 
-        return self.compute_matched_libs(path_patterns, False,
-                                         is_excluded_libs)
-
-    def compute_sp_hal(self, vndk_stable, closure):
+    def compute_predefined_sp_hal(self):
         """Find all same-process HALs."""
 
         path_patterns = (
@@ -1000,11 +985,39 @@
             '^.*/android\\.hardware\\.graphics\\.mapper@\\d+\\.\\d+-impl\\.so$',
         )
 
-        def is_excluded_libs(lib):
-            return lib.is_ndk or lib in vndk_stable
+        return self.compute_path_matched_lib(path_patterns)
 
-        return self.compute_matched_libs(path_patterns, closure,
-                                         is_excluded_libs)
+    def compute_sp_ndk(self):
+        """Find all SP-NDK libraries."""
+        return set(lib for lib in self.all_lib() if lib.is_sp_ndk)
+
+    def compute_sp_lib(self, generic_refs):
+        def is_ndk(lib):
+            return lib.is_ndk
+
+        sp_ndk = self.compute_sp_ndk()
+        sp_ndk_closure = self.compute_closure(sp_ndk, is_ndk)
+
+        sp_hal = self.compute_predefined_sp_hal()
+        sp_hal_closure = self.compute_closure(sp_hal, is_ndk)
+
+        def is_aosp_lib(lib):
+            return (not generic_refs or \
+                    generic_refs.classify_lib(lib) != GenericRefs.NEW_LIB)
+
+        sp_hal_vndk_stable = set()
+        sp_hal_dep = set()
+        for lib in sp_hal_closure - sp_hal:
+            if is_aosp_lib(lib):
+                sp_hal_vndk_stable.add(lib)
+            else:
+                sp_hal_dep.add(lib)
+
+        sp_ndk_vndk_stable = sp_ndk_closure - sp_ndk
+        sp_hal_vndk_stable = sp_hal_vndk_stable - sp_ndk - sp_ndk_vndk_stable
+
+        return (sp_hal, sp_hal_dep, sp_hal_vndk_stable, sp_ndk, \
+                sp_ndk_vndk_stable)
 
     def _po_component_sorted(self, lib_set, get_successors,
                              get_strong_successors):
@@ -1771,14 +1784,16 @@
             self._warn_banned_vendor_lib_deps(graph, banned_libs)
 
         # Compute sp-hal and vndk-stable.
-        vndk_stable = graph.compute_predefined_vndk_stable()
-        sp_hals = graph.compute_sp_hal(vndk_stable, closure=False)
-        sp_hals_closure = graph.compute_sp_hal(vndk_stable, closure=True)
+        sp_hal, sp_hal_dep, sp_hal_vndk_stable, sp_ndk, sp_ndk_vndk_stable = \
+                graph.compute_sp_lib(generic_refs)
+
+        vndk_stable = sp_hal_vndk_stable | sp_ndk_vndk_stable
+        sp_hal_closure = sp_hal | sp_hal_dep
 
         # Normalize partition tags.  We expect many violations from the
         # pre-Treble world.  Guess a resolution for the incorrect partition
         # tag.
-        graph.normalize_partition_tags(sp_hals, generic_refs)
+        graph.normalize_partition_tags(sp_hal, generic_refs)
 
         # User may specify the partition for outward-customized vndk libs.  The
         # following code converts the path into ELFLinkData.
@@ -1804,7 +1819,7 @@
 
         # Compute vndk heuristics.
         vndk = graph.compute_vndk(
-                sp_hals_closure, vndk_stable, vndk_customized_for_system,
+                sp_hal_closure, vndk_stable, vndk_customized_for_system,
                 vndk_customized_for_vendor, generic_refs, banned_libs)
 
         if args.warn_high_level_ndk_deps:
@@ -1812,8 +1827,11 @@
                     (vndk.vndk_core, vndk.vndk_indirect, vndk.vndk_fwk_ext,
                      vndk.vndk_vnd_ext))
 
-        for lib in sorted_lib_path_list(sp_hals_closure):
-            print('sp-hals:', lib)
+        for lib in sorted_lib_path_list(sp_hal):
+            print('sp-hal:', lib)
+
+        for lib in sorted_lib_path_list(sp_hal_dep):
+            print('sp-hal-dep:', lib)
 
         for lib in sorted_lib_path_list(vndk_stable):
             print('vndk-stable:', lib)
@@ -1983,26 +2001,42 @@
         return 0
 
 
-class SpHalCommand(ELFGraphCommand):
+class SpLibCommand(ELFGraphCommand):
     def __init__(self):
-        super(SpHalCommand, self).__init__(
-                'sp-hal', help='Find transitive closure of same-process HALs')
+        super(SpLibCommand, self).__init__(
+                'sp-lib', help='Define sp-ndk, sp-hal, and vndk-stable')
 
     def add_argparser_options(self, parser):
-        super(SpHalCommand, self).add_argparser_options(parser)
+        super(SpLibCommand, self).add_argparser_options(parser)
 
-        parser.add_argument('--closure', action='store_true',
-                            help='show the closure')
+        parser.add_argument(
+                '--load-generic-refs',
+                help='compare with generic reference symbols')
 
     def main(self, args):
+        generic_refs = None
+        if args.load_generic_refs:
+            generic_refs = GenericRefs.create_from_dir(args.load_generic_refs)
+
         graph = ELFLinker.create(args.system, args.system_dir_as_vendor,
                                  args.vendor, args.vendor_dir_as_system,
                                  args.load_extra_deps)
 
-        vndk_stable = graph.compute_predefined_vndk_stable()
-        sp_hals = graph.compute_sp_hal(vndk_stable, closure=args.closure)
-        for lib in sorted_lib_path_list(sp_hals):
-            print(lib)
+        sp_hal, sp_hal_dep, sp_hal_vndk_stable, sp_ndk, sp_ndk_vndk_stable = \
+                graph.compute_sp_lib(generic_refs)
+
+        for lib in sorted_lib_path_list(sp_hal):
+            print('sp-hal:', lib)
+        for lib in sorted_lib_path_list(sp_hal_dep):
+            print('sp-hal-dep:', lib)
+        for lib in sorted_lib_path_list(sp_hal_vndk_stable):
+            print('sp-hal-vndk-stable:', lib)
+
+        for lib in sorted_lib_path_list(sp_ndk):
+            print('sp-ndk:', lib)
+        for lib in sorted_lib_path_list(sp_ndk_vndk_stable):
+            print('sp-ndk-vndk-stable:', lib)
+
         return 0
 
 
@@ -2022,7 +2056,7 @@
     register_subcmd(VNDKCapCommand())
     register_subcmd(DepsCommand())
     register_subcmd(DepsClosureCommand())
-    register_subcmd(SpHalCommand())
+    register_subcmd(SpLibCommand())
     register_subcmd(VNDKStableCommand())
 
     args = parser.parse_args()