Merge c9d682a1e578ff1196f38e6d371e8145713914ce on remote branch

Change-Id: Id97016192dcb5a621a7076192e1e81627d5b29ff
diff --git a/build/Android.mk b/build/Android.mk
index 220fee7..8e361e9 100644
--- a/build/Android.mk
+++ b/build/Android.mk
@@ -101,6 +101,9 @@
 # org.apache.http.legacy.jar stubs
 ALL_SDK_FILES += $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/org.apache.http.legacy.stubs_intermediates/classes.jar
 
+# Android Automotive OS stubs
+ALL_SDK_FILES += $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android.car-stubs_intermediates/classes.jar
+
 # test stubs
 ALL_SDK_FILES += $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android.test.mock.stubs_intermediates/classes.jar
 ALL_SDK_FILES += $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android.test.base.stubs_intermediates/classes.jar
diff --git a/build/README.md b/build/README.md
new file mode 100644
index 0000000..8bb2df4
--- /dev/null
+++ b/build/README.md
@@ -0,0 +1,11 @@
+# Android SDK
+
+This directory contains configuration for building Android SDK. The root
+configuration file is `sdk.atree`.
+
+## Building the SDK
+
+```
+$ lunch sdk
+$ m -j sdk
+```
diff --git a/build/optional.json b/build/optional.json
index 4b3bedf..b63c165 100644
--- a/build/optional.json
+++ b/build/optional.json
@@ -5,6 +5,11 @@
     "manifest": false
   },
   {
+    "name": "android.car",
+    "jar": "android.car.jar",
+    "manifest": false
+  },
+  {
     "name": "android.test.mock",
     "jar": "android.test.mock.jar",
     "manifest": false
diff --git a/build/sdk.atree b/build/sdk.atree
index cf96ed5..8fb30d5 100644
--- a/build/sdk.atree
+++ b/build/sdk.atree
@@ -17,11 +17,6 @@
 #
 # These are the files that comprise that SDK.
 #
-# The files that will go in the tools folder are setup through
-#    sdk/build/tools.atree
-# This is to help when the sdk.git project is branched differently from
-# the other projects.
-#
 
 ##############################################################################
 # SDK Root folder
@@ -172,6 +167,8 @@
 ${OUT_DIR}/target/common/obj/JAVA_LIBRARIES/org.apache.http.legacy.stubs_intermediates/classes.jar  platforms/${PLATFORM_NAME}/optional/org.apache.http.legacy.jar
 # deprecated APIs
 ${OUT_DIR}/target/common/obj/JAVA_LIBRARIES/android_uiautomator_intermediates/classes.jar     platforms/${PLATFORM_NAME}/uiautomator.jar
+# Android Automotive OS stubs.
+${OUT_DIR}/target/common/obj/JAVA_LIBRARIES/android.car-stubs_intermediates/classes.jar         platforms/${PLATFORM_NAME}/optional/android.car.jar
 # Test APIs
 ${OUT_DIR}/target/common/obj/JAVA_LIBRARIES/android.test.mock.stubs_intermediates/classes.jar   platforms/${PLATFORM_NAME}/optional/android.test.mock.jar
 ${OUT_DIR}/target/common/obj/JAVA_LIBRARIES/android.test.base.stubs_intermediates/classes.jar   platforms/${PLATFORM_NAME}/optional/android.test.base.jar
diff --git a/sdk/build_tools_source.prop_template b/sdk/build_tools_source.prop_template
index 49c19ad..fb40467 100644
--- a/sdk/build_tools_source.prop_template
+++ b/sdk/build_tools_source.prop_template
@@ -1,3 +1,3 @@
 Pkg.UserSrc=false
-Pkg.Revision=${PLATFORM_SDK_VERSION}.0.1
+Pkg.Revision=${PLATFORM_SDK_VERSION}.0.3
 #Pkg.Revision=30.0.0 rc4
diff --git a/sdk/platform_source.prop_template b/sdk/platform_source.prop_template
index 0093ab1..9dc99a0 100644
--- a/sdk/platform_source.prop_template
+++ b/sdk/platform_source.prop_template
@@ -2,7 +2,7 @@
 Pkg.UserSrc=false
 Platform.Version=${PLATFORM_VERSION}
 Platform.CodeName=
-Pkg.Revision=2
+Pkg.Revision=3
 AndroidVersion.ApiLevel=${PLATFORM_SDK_VERSION}
 AndroidVersion.CodeName=${PLATFORM_VERSION_CODENAME}
 Layoutlib.Api=15
diff --git a/vendor_snapshot/update.py b/vendor_snapshot/update.py
index 318318e..d0dc0ac 100644
--- a/vendor_snapshot/update.py
+++ b/vendor_snapshot/update.py
@@ -145,12 +145,12 @@
     return ret
 
 
-def gen_bp_module(variation, name, version, target_arch, arch_props, bp_dir):
+def gen_bp_module(image, variation, name, version, target_arch, arch_props, bp_dir):
     prop = {
         # These three are common for all snapshot modules.
         'version': str(version),
         'target_arch': target_arch,
-        'vendor': True,
+        image: True,
         'arch': {},
     }
 
@@ -202,7 +202,7 @@
         elif stem64:
             prop['compile_multilib'] = '64'
 
-    bp = 'vendor_snapshot_%s {\n' % variation
+    bp = '%s_snapshot_%s {\n' % (image, variation)
     bp += gen_bp_prop(prop, INDENT)
     bp += '}\n\n'
     return bp
@@ -268,7 +268,7 @@
     return props
 
 
-def gen_bp_files(install_dir, snapshot_version):
+def gen_bp_files(image, install_dir, snapshot_version):
     props = build_props(install_dir)
 
     for target_arch in sorted(props):
@@ -276,8 +276,8 @@
         bp_dir = os.path.join(install_dir, target_arch)
         for variation in sorted(props[target_arch]):
             for name in sorted(props[target_arch][variation]):
-                androidbp += gen_bp_module(variation, name, snapshot_version,
-                                           target_arch,
+                androidbp += gen_bp_module(image, variation, name,
+                                           snapshot_version, target_arch,
                                            props[target_arch][variation][name],
                                            bp_dir)
         with open(os.path.join(bp_dir, 'Android.bp'), 'w') as f:
@@ -285,6 +285,199 @@
             f.write(androidbp)
 
 
+def find_all_installed_files(install_dir):
+    installed_files = dict()
+    for root, _, files in os.walk(install_dir, followlinks = True):
+        for file_name in sorted(files):
+            if file_name.endswith('.json'):
+                continue
+            if file_name.endswith('Android.bp'):
+                continue
+            full_path = os.path.join(root, file_name)
+            size = os.stat(full_path).st_size
+            installed_files[full_path] = size
+
+    logging.debug('')
+    for f in sorted(installed_files.keys()):
+        logging.debug(f)
+    logging.debug('')
+    logging.debug('found {} installed files'.format(len(installed_files)))
+    logging.debug('')
+    return installed_files
+
+
+def find_files_in_props(target_arch, arch_install_dir, variation, name, props, file_to_info):
+    logging.debug('{} {} {} {} {}'.format(
+        target_arch, arch_install_dir, variation, name, props))
+
+    def add_info(file, name, variation, arch, is_cfi, is_header):
+        info = (name, variation, arch, is_cfi, is_header)
+        info_list = file_to_info.get(file)
+        if not info_list:
+            info_list = []
+            file_to_info[file] = info_list
+        info_list.append(info)
+
+    def find_file_in_list(dict, key, is_cfi):
+        list = dict.get(key)
+        logging.debug('    {} {}'.format(key, list))
+        if list:
+            for item in list:
+                item_path = os.path.join(arch_install_dir, item)
+                add_info(item_path, name, variation, arch, is_cfi, False)
+
+    def find_file_in_dirs(dict, key, is_cfi, is_header):
+        dirs = dict.get(key)
+        logging.debug('    {} {}'.format(key, dirs))
+        if dirs:
+            for dir in dirs:
+                dir_path = os.path.join(arch_install_dir, dir)
+                logging.debug('        scanning {}'.format(dir_path))
+                for root, _, files in os.walk(dir_path, followlinks = True):
+                    for file_name in sorted(files):
+                        item_path = os.path.join(root, file_name)
+                        add_info(item_path, name, variation, arch, is_cfi, is_header)
+
+    def find_file_in_dict(dict, is_cfi):
+        logging.debug('    arch {}'.format(arch))
+        logging.debug('    name {}'.format( name))
+        logging.debug('    is_cfi {}'.format(is_cfi))
+
+        src = dict.get('src')
+        logging.debug('    src {}'.format(src))
+        if src:
+            src_path = os.path.join(arch_install_dir, src)
+            add_info(src_path, name, variation, arch, is_cfi, False)
+
+        notice = dict.get('notice')
+        logging.debug('    notice {}'.format(notice))
+        if notice:
+            notice_path = os.path.join(arch_install_dir, notice)
+            add_info(notice_path, name, variation, arch, is_cfi, False)
+
+        find_file_in_list(dict, 'init_rc', is_cfi)
+        find_file_in_list(dict, 'vintf_fragments', is_cfi)
+
+        find_file_in_dirs(dict, 'export_include_dirs', is_cfi, True)
+        find_file_in_dirs(dict, 'export_system_include_dirs', is_cfi, True)
+
+    for arch in sorted(props):
+        name = props[arch]['name']
+        find_file_in_dict(props[arch], False)
+        cfi = props[arch].get('cfi')
+        if cfi:
+            find_file_in_dict(cfi, True)
+
+
+def find_all_props_files(install_dir):
+
+    # This function builds a database of filename to module. This means that we
+    # need to dive into the json to find the files that the vendor snapshot
+    # provides, and link these back to modules that provide them.
+
+    file_to_info = dict()
+
+    props = build_props(install_dir)
+    for target_arch in sorted(props):
+        arch_install_dir = os.path.join(install_dir, target_arch)
+        for variation in sorted(props[target_arch]):
+            for name in sorted(props[target_arch][variation]):
+                find_files_in_props(
+                    target_arch,
+                    arch_install_dir,
+                    variation,
+                    name,
+                    props[target_arch][variation][name],
+                    file_to_info)
+
+    logging.debug('')
+    for f in sorted(file_to_info.keys()):
+        logging.debug(f)
+    logging.debug('')
+    logging.debug('found {} props files'.format(len(file_to_info)))
+    logging.debug('')
+    return file_to_info
+
+
+def get_ninja_inputs(ninja_binary, ninja_build_file, modules):
+    """Returns the set of input file path strings for the given modules.
+
+    Uses the `ninja -t inputs` tool.
+
+    Args:
+        ninja_binary: The path to a ninja binary.
+        ninja_build_file: The path to a .ninja file from a build.
+        modules: The list of modules to scan for inputs.
+    """
+    inputs = set()
+    cmd = [
+        ninja_binary,
+        "-f",
+        ninja_build_file,
+        "-t",
+        "inputs",
+        "-d",
+    ] + list(modules)
+    logging.debug('invoke ninja {}'.format(cmd))
+    inputs = inputs.union(set(
+        subprocess.check_output(cmd).decode().strip("\n").split("\n")))
+
+    return inputs
+
+
+def check_module_usage(install_dir, ninja_binary, image, ninja_file, goals,
+                       output):
+    all_installed_files = find_all_installed_files(install_dir)
+    all_props_files = find_all_props_files(install_dir)
+
+    ninja_inputs = get_ninja_inputs(ninja_binary, ninja_file, goals)
+    logging.debug('')
+    logging.debug('ninja inputs')
+    for ni in ninja_inputs:
+        logging.debug(ni)
+
+    logging.debug('found {} ninja_inputs for goals {}'.format(
+        len(ninja_inputs), goals))
+
+    # Intersect the file_to_info dict with the ninja_inputs to determine
+    # which items from the vendor snapshot are actually used by the goals.
+
+    total_size = 0
+    used_size = 0
+    used_file_to_info = dict()
+
+    for file, size in all_installed_files.items():
+        total_size += size
+        if file in ninja_inputs:
+            logging.debug('used: {}'.format(file))
+            used_size += size
+            info = all_props_files.get(file)
+
+            if info:
+                used_file_to_info[file] = info
+            else:
+                logging.warning('No info for file {}'.format(file))
+                used_file_to_info[file] = 'no info'
+
+    logging.debug('Total size {}'.format(total_size))
+    logging.debug('Used size {}'.format(used_size))
+    logging.debug('')
+    logging.debug('used items')
+
+    used_modules = set()
+
+    for f, i in sorted(used_file_to_info.items()):
+        logging.debug('{} {}'.format(f, i))
+        for m in i:
+            (name, variation, arch, is_cfi, is_header) = m
+            if not is_header:
+                used_modules.add(name)
+
+    with open(output, 'w') as f:
+        f.write('%s_SNAPSHOT_MODULES := \\\n' % image.upper())
+        for m in sorted(used_modules):
+            f.write('  %s \\\n' % m)
+
 def check_call(cmd):
     logging.debug('Running `{}`'.format(' '.join(cmd)))
     subprocess.check_call(cmd)
@@ -307,13 +500,16 @@
     ]
     check_call(cmd)
 
-def install_artifacts(branch, build, target, local_dir, symlink, install_dir):
+def install_artifacts(image, branch, build, target, local_dir, symlink,
+                      install_dir):
     """Installs vendor snapshot build artifacts to {install_dir}/v{version}.
 
     1) Fetch build artifacts from Android Build server or from local_dir
     2) Unzip or create symlinks to build artifacts
 
     Args:
+      image: string, img file for which the snapshot was created (vendor,
+             recovery, etc.)
       branch: string or None, branch name of build artifacts
       build: string or None, build number of build artifacts
       target: string or None, target name of build artifacts
@@ -324,7 +520,7 @@
       temp_artifact_dir: string, temp directory to hold build artifacts fetched
         from Android Build server. For 'local' option, is set to None.
     """
-    artifact_pattern = 'vendor-*.zip'
+    artifact_pattern = image + '-*.zip'
 
     def unzip_artifacts(artifact_dir):
         artifacts = glob.glob(os.path.join(artifact_dir, artifact_pattern))
@@ -374,6 +570,11 @@
         'snapshot_version',
         type=int,
         help='Vendor snapshot version to install, e.g. "30".')
+    parser.add_argument(
+        '--image',
+        help=('Image whose snapshot is being updated (e.g., vendor, '
+              'recovery , ramdisk, etc.)'),
+        default='vendor')
     parser.add_argument('--branch', help='Branch to pull build from.')
     parser.add_argument('--build', help='Build number to pull.')
     parser.add_argument('--target', help='Target to pull.')
@@ -388,6 +589,7 @@
         help='Use symlinks instead of unzipping vendor snapshot zip')
     parser.add_argument(
         '--install-dir',
+        required=True,
         help=(
             'Base directory to which vendor snapshot artifacts are installed. '
             'Example: --install-dir vendor/<company name>/vendor_snapshot/v30'))
@@ -396,6 +598,20 @@
         action='store_true',
         help=(
             'If provided, does not ask before overwriting the install-dir.'))
+    parser.add_argument(
+        '--check-module-usage',
+        action='store_true',
+        help='Check which modules are used.')
+    parser.add_argument(
+        '--check-module-usage-goal',
+        action='append',
+        help='Goal(s) for which --check-module-usage is calculated.')
+    parser.add_argument(
+        '--check-module-usage-ninja-file',
+        help='Ninja file for which --check-module-usage is calculated.')
+    parser.add_argument(
+        '--check-module-usage-output',
+        help='File to which to write the check-module-usage results.')
 
     parser.add_argument(
         '-v',
@@ -410,6 +626,34 @@
     """Program entry point."""
     args = get_args()
 
+    verbose_map = (logging.WARNING, logging.INFO, logging.DEBUG)
+    verbosity = min(args.verbose, 2)
+    logging.basicConfig(
+        format='%(levelname)-8s [%(filename)s:%(lineno)d] %(message)s',
+        level=verbose_map[verbosity])
+
+    if not args.install_dir:
+        raise ValueError('Please provide --install-dir option.')
+    install_dir = os.path.expanduser(args.install_dir)
+
+    if args.check_module_usage:
+        ninja_binary = './prebuilts/build-tools/linux-x86/bin/ninja'
+
+        if not args.check_module_usage_goal:
+            raise ValueError('Please provide --check-module-usage-goal option.')
+        if not args.check_module_usage_ninja_file:
+            raise ValueError(
+                'Please provide --check-module-usage-ninja-file option.')
+        if not args.check_module_usage_output:
+            raise ValueError(
+                'Please provide --check-module-usage-output option.')
+
+        check_module_usage(install_dir, ninja_binary, args.image,
+                           args.check_module_usage_ninja_file,
+                           args.check_module_usage_goal,
+                           args.check_module_usage_output)
+        return
+
     local = None
     if args.local:
         local = os.path.expanduser(args.local)
@@ -429,18 +673,8 @@
                 'Please provide --branch, --build and --target. Or set --local '
                 'option.')
 
-    if not args.install_dir:
-        raise ValueError('Please provide --install-dir option.')
-
     snapshot_version = args.snapshot_version
 
-    verbose_map = (logging.WARNING, logging.INFO, logging.DEBUG)
-    verbosity = min(args.verbose, 2)
-    logging.basicConfig(
-        format='%(levelname)-8s [%(filename)s:%(lineno)d] %(message)s',
-        level=verbose_map[verbosity])
-
-    install_dir = os.path.expanduser(args.install_dir)
     if os.path.exists(install_dir):
         def remove_dir():
             logging.info('Removing {}'.format(install_dir))
@@ -460,13 +694,14 @@
     check_call(['mkdir', '-p', install_dir])
 
     install_artifacts(
+        image=args.image,
         branch=args.branch,
         build=args.build,
         target=args.target,
         local_dir=local,
         symlink=args.symlink,
         install_dir=install_dir)
-    gen_bp_files(install_dir, snapshot_version)
+    gen_bp_files(args.image, install_dir, snapshot_version)
 
 if __name__ == '__main__':
     main()