Snap for 5735642 from cf0ef77bc21aca6298046d9b503c23e5fcc15b93 to sdk-release

Change-Id: Ib448e7ed1b4a28e1a4448d1d6890145089c3bed7
diff --git a/aidegen/.coveragerc b/aidegen/.coveragerc
index c742664..453c2f4 100644
--- a/aidegen/.coveragerc
+++ b/aidegen/.coveragerc
@@ -9,6 +9,7 @@
 # omit file patterns must be listed one per line. */.local/* /usr/*
 omit =
     *_unittest.py
+    *run_unittests.py
     *__init__.py
     *unittest_constants.py
 
diff --git a/aidegen/aidegen_main.py b/aidegen/aidegen_main.py
index be764cf..89bf060 100644
--- a/aidegen/aidegen_main.py
+++ b/aidegen/aidegen_main.py
@@ -50,18 +50,19 @@
 from aidegen.lib import common_util
 from aidegen.lib.common_util import COLORED_INFO
 from aidegen.lib.common_util import COLORED_PASS
+from aidegen.lib.common_util import back_to_cwd
 from aidegen.lib.common_util import is_android_root
 from aidegen.lib.common_util import time_logged
 from aidegen.lib.errors import AIDEgenError
 from aidegen.lib.errors import IDENotExistError
 from aidegen.lib.ide_util import IdeUtil
-from aidegen.lib.metrics import log_usage
-from aidegen.lib.metrics import starts_asuite_metrics
-from aidegen.lib.metrics import ends_asuite_metrics
-from aidegen.lib.module_info_util import generate_module_info_json
-from aidegen.lib.project_file_gen import generate_eclipse_project_files
-from aidegen.lib.project_file_gen import generate_ide_project_files
+from aidegen.lib.aidegen_metrics import starts_asuite_metrics
+from aidegen.lib.aidegen_metrics import ends_asuite_metrics
+from aidegen.lib.module_info import AidegenModuleInfo
+from aidegen.lib.project_file_gen import ProjectFileGenerator
+from aidegen.lib.eclipse_project_file_gen import EclipseConf
 from aidegen.lib.project_info import ProjectInfo
+from aidegen.lib import project_config
 from aidegen.lib.source_locator import multi_projects_locate_source
 
 AIDEGEN_REPORT_LINK = ('To report the AIDEGen tool problem, please use this '
@@ -81,15 +82,11 @@
     'analysis, please consider to clear IDE caches if necessary. To do that, in'
     ' IntelliJ IDEA, go to [File > Invalidate Caches / Restart...].')
 
-_SKIP_BUILD_INFO = ('If you are sure the related modules and dependencies have '
-                    'been already built, please try to use command {} to skip '
-                    'the building process.')
 _MAX_TIME = 1
 _SKIP_BUILD_INFO_FUTURE = ''.join([
     'AIDEGen build time exceeds {} minute(s).\n'.format(_MAX_TIME),
-    _SKIP_BUILD_INFO.rstrip('.'), ' in the future.'
+    project_config.SKIP_BUILD_INFO.rstrip('.'), ' in the future.'
 ])
-_SKIP_BUILD_CMD = 'aidegen {} -s'
 _INFO = COLORED_INFO('INFO:')
 _SKIP_MSG = _SKIP_BUILD_INFO_FUTURE.format(
     COLORED_INFO('aidegen [ module(s) ] -s'))
@@ -155,7 +152,7 @@
         '--skip-build',
         dest='skip_build',
         action='store_true',
-        help=('Skip building jar or modules that create java files in build '
+        help=('Skip building jars or modules that create java files in build '
               'time, e.g. R/AIDL/Logtags.'))
     parser.add_argument(
         '-a',
@@ -185,7 +182,7 @@
         args: A list of arguments.
 
     Returns:
-        A IdeUtil class instance.
+        An IdeUtil class instance.
     """
     if args.no_launch:
         return None
@@ -200,29 +197,16 @@
     return ide_util_obj
 
 
-def _check_skip_build(args):
-    """Check if users skip building target, display the warning message.
-
-    Args:
-        args: A list of arguments.
-    """
-    if not args.skip_build:
-        msg = _SKIP_BUILD_INFO.format(
-            COLORED_INFO(_SKIP_BUILD_CMD.format(' '.join(args.targets))))
-        print('\n{} {}\n'.format(_INFO, msg))
-
-
-def _generate_project_files(ide, projects):
+def _generate_project_files(projects):
     """Generate project files by IDE type.
 
     Args:
-        ide: IDE type.
         projects: A list of ProjectInfo instances.
     """
-    if ide == 'e':
-        generate_eclipse_project_files(projects)
+    if ProjectInfo.config.ide_name == constant.IDE_ECLIPSE:
+        EclipseConf.generate_ide_project_files(projects)
     else:
-        generate_ide_project_files(projects)
+        ProjectFileGenerator.generate_ide_project_files(projects)
 
 
 def _compile_targets_for_whole_android_tree(atest_module_info, targets, cwd):
@@ -237,9 +221,9 @@
        the first one.
 
     Args:
-        atest_module_info: A instance of atest module-info object.
-        targets: A list of targets to be built.
-        cwd: A path of current working directory.
+        atest_module_info: An instance of atest module-info object.
+        targets: A list of targets to be imported.
+        cwd: A string of path to current working directory.
 
     Returns:
         A list of targets after adjustment.
@@ -251,9 +235,10 @@
         for target in targets:
             _, abs_path = common_util.get_related_paths(atest_module_info,
                                                         target)
-            rel_path = os.path.relpath(abs_path, constant.ANDROID_ROOT_PATH)
+            rel_path = os.path.relpath(abs_path,
+                                       common_util.get_android_root_dir())
             new_targets.append(rel_path)
-        os.chdir(constant.ANDROID_ROOT_PATH)
+        os.chdir(common_util.get_android_root_dir())
 
     if new_targets[0] != '':
         new_targets.insert(0, '')
@@ -273,8 +258,8 @@
         ide_util_obj: An ide_util instance.
         project_absolute_path: A string of project absolute path.
     """
-    ide_util_obj.config_ide()
-    ide_util_obj.launch_ide(project_absolute_path)
+    ide_util_obj.config_ide(project_absolute_path)
+    ide_util_obj.launch_ide()
     print('\n{} {}\n'.format(_CONGRATULATION, _LAUNCH_SUCCESS_MSG))
 
 
@@ -289,24 +274,35 @@
     2. If android_tree is True, add whole Android tree to the project.
 
     Args:
-        atest_module_info: A instance of atest module-info object.
-        targets: A list of targets to be built.
+        atest_module_info: An instance of atest module-info object.
+        targets: A list of targets to be imported.
         android_tree: A boolean, True if it's a whole Android tree case,
                       otherwise False.
 
     Returns:
         A list of targets to be built.
     """
-    cwd = os.getcwd()
-    if not android_tree and is_android_root(cwd) and targets == ['']:
-        android_tree = True
+    android_tree = _is_whole_android_tree(targets, android_tree)
     new_targets = targets
     if android_tree:
         new_targets = _compile_targets_for_whole_android_tree(
-            atest_module_info, targets, cwd)
+            atest_module_info, targets, os.getcwd())
     return new_targets
 
 
+def _is_whole_android_tree(targets, android_tree):
+    """Checks is AIDEGen going to process whole android tree.
+
+    Args:
+        targets: A list of targets to be imported.
+        android_tree: A boolean, True if it's a whole Android tree case,
+                      otherwise False.
+    Returns:
+        A boolean, True when user is going to import whole Android tree.
+    """
+    return android_tree or (is_android_root(os.getcwd()) and targets == [''])
+
+
 @time_logged(message=_TIME_EXCEED_MSG, maximum=_MAX_TIME)
 def main_with_message(args):
     """Main entry with skip build message.
@@ -340,7 +336,9 @@
     try:
         args = _parse_args(argv)
         _configure_logging(args.verbose)
-        starts_asuite_metrics()
+        references = [constant.ANDROID_TREE] if _is_whole_android_tree(
+            args.targets, args.android_tree) else []
+        starts_asuite_metrics(references)
         if args.skip_build:
             main_without_message(args)
         else:
@@ -353,51 +351,50 @@
         # Filter out sys.Exit(0) case, which is not an exception case.
         if isinstance(err, SystemExit) and exc_value.code == 0:
             exit_code = constant.EXIT_CODE_NORMAL
-    finally:
         if exit_code is not constant.EXIT_CODE_NORMAL:
             error_message = str(exc_value)
             traceback_list = traceback.format_tb(exc_traceback)
             traceback_list.append(error_message)
             traceback_str = ''.join(traceback_list)
+            ends_asuite_metrics(exit_code, traceback_str, error_message)
             # print out the trackback message for developers to debug
             print(traceback_str)
-            ends_asuite_metrics(exit_code, traceback_str, error_message)
-        else:
+            raise err
+    finally:
+        if exit_code is constant.EXIT_CODE_NORMAL:
             ends_asuite_metrics(exit_code)
+        print('\n{0} {1}\n\n{0} {2}\n'.format(_INFO, AIDEGEN_REPORT_LINK,
+                                              _IDE_CACHE_REMINDER_MSG))
 
 
+@back_to_cwd
 def aidegen_main(args):
     """AIDEGen main entry.
 
-    Try to generates project files for using in IDE.
+    Try to generate project files for using in IDE.
 
     Args:
         args: A list of system arguments.
     """
-    log_usage()
     # Pre-check for IDE relevant case, then handle dependency graph job.
     ide_util_obj = _get_ide_util_instance(args)
-    _check_skip_build(args)
+    ProjectInfo.config = project_config.ProjectConfig(args)
     atest_module_info = common_util.get_atest_module_info(args.targets)
-    targets = _check_whole_android_tree(atest_module_info, args.targets,
-                                        args.android_tree)
-    ProjectInfo.modules_info = generate_module_info_json(
-        atest_module_info, targets, args.verbose, args.skip_build)
-    projects = ProjectInfo.generate_projects(atest_module_info, targets)
-    if ide_util_obj:
-        multi_projects_locate_source(projects, args.verbose, args.depth,
-                                     ide_util_obj.ide_name(), args.skip_build)
-    else:
-        multi_projects_locate_source(projects, args.verbose, args.depth,
-                                     skip_build=args.skip_build)
-    _generate_project_files(args.ide[0], projects)
+    targets = _check_whole_android_tree(
+        atest_module_info, args.targets, args.android_tree)
+    ProjectInfo.modules_info = AidegenModuleInfo(
+        force_build=False,
+        module_file=None,
+        atest_module_info=atest_module_info,
+        projects=targets,
+        verbose=args.verbose,
+        skip_build=args.skip_build)
+    projects = ProjectInfo.generate_projects(targets)
+    multi_projects_locate_source(projects, args.verbose)
+    _generate_project_files(projects)
     if ide_util_obj:
         _launch_ide(ide_util_obj, projects[0].project_absolute_path)
 
 
 if __name__ == '__main__':
-    try:
-        main(sys.argv[1:])
-    finally:
-        print('\n{0} {1}\n\n{0} {2}\n'.format(_INFO, AIDEGEN_REPORT_LINK,
-                                              _IDE_CACHE_REMINDER_MSG))
+    main(sys.argv[1:])
diff --git a/aidegen/aidegen_main_unittest.py b/aidegen/aidegen_main_unittest.py
index cc10d01..89e5ab6 100644
--- a/aidegen/aidegen_main_unittest.py
+++ b/aidegen/aidegen_main_unittest.py
@@ -24,13 +24,15 @@
 
 import aidegen.unittest_constants as uc
 from aidegen import aidegen_main
-from aidegen.lib import metrics
+from aidegen.lib import aidegen_metrics
 from aidegen import constant
 from aidegen.lib import common_util
-from aidegen.lib.common_util import COLORED_INFO
 from aidegen.lib.errors import IDENotExistError
 from aidegen.lib.errors import ProjectPathNotExistError
 from aidegen.lib.ide_util import IdeUtil
+from aidegen.lib.eclipse_project_file_gen import EclipseConf
+from aidegen.lib.project_info import ProjectInfo
+from aidegen.lib.project_file_gen import ProjectFileGenerator
 from atest import module_info
 
 
@@ -96,36 +98,26 @@
         with self.assertRaises(IDENotExistError):
             aidegen_main._get_ide_util_instance(args)
 
-    @mock.patch('builtins.print')
-    def test_check_skip_build(self, mock_print):
-        """Test _check_skip_build with different conditions."""
-        target = 'tradefed'
-        args = aidegen_main._parse_args([target, '-s'])
-        aidegen_main._check_skip_build(args)
-        self.assertFalse(mock_print.called)
-        args = aidegen_main._parse_args([target])
-        aidegen_main._check_skip_build(args)
-        msg = aidegen_main._SKIP_BUILD_INFO.format(
-            COLORED_INFO(
-                aidegen_main._SKIP_BUILD_CMD.format(' '.join(args.targets))))
-        info = '\n{} {}\n'.format(aidegen_main._INFO, msg)
-        self.assertTrue(mock_print.called_with(info))
-
-    @mock.patch.object(aidegen_main, 'generate_ide_project_files')
-    @mock.patch.object(aidegen_main, 'generate_eclipse_project_files')
-    def test_generate_project_files(self, mock_eclipse, mock_ide):
+    @mock.patch('aidegen.lib.project_config.ProjectConfig')
+    @mock.patch.object(ProjectFileGenerator, 'generate_ide_project_files')
+    @mock.patch.object(EclipseConf, 'generate_ide_project_files')
+    def test_generate_project_files(self, mock_eclipse, mock_ide, mock_config):
         """Test _generate_project_files with different conditions."""
         projects = ['module_a', 'module_v']
-        aidegen_main._generate_project_files('e', projects)
+        ProjectInfo.config = mock_config
+        mock_config.ide_name = constant.IDE_ECLIPSE
+        aidegen_main._generate_project_files(projects)
         self.assertTrue(mock_eclipse.called_with(projects))
-        aidegen_main._generate_project_files('s', projects)
+        mock_config.ide_name = constant.IDE_ANDROID_STUDIO
+        aidegen_main._generate_project_files(projects)
         self.assertTrue(mock_ide.called_with(projects))
-        aidegen_main._generate_project_files('j', projects)
+        mock_config.ide_name = constant.IDE_INTELLIJ
+        aidegen_main._generate_project_files(projects)
         self.assertTrue(mock_ide.called_with(projects))
 
     @mock.patch.object(common_util, 'get_atest_module_info')
-    @mock.patch.object(metrics, 'log_usage')
-    def test_show_collect_data_notice(self, mock_log, mock_get):
+    @mock.patch.object(aidegen_metrics, 'starts_asuite_metrics')
+    def test_show_collect_data_notice(self, mock_metrics, mock_get):
         """Test main process always run through the target test function."""
         target = 'nothing'
         args = aidegen_main._parse_args([target, '-s', '-n'])
@@ -133,33 +125,43 @@
             err = common_util.PATH_NOT_EXISTS_ERROR.format(target)
             mock_get.side_effect = ProjectPathNotExistError(err)
             aidegen_main.main_without_message(args)
-            self.assertTrue(mock_log.called)
+            self.assertTrue(mock_metrics.called)
 
     @mock.patch.object(common_util, 'get_related_paths')
     def test_compile_targets_for_whole_android_tree(self, mock_get):
         """Test _add_whole_android_tree_project with different conditions."""
         mod_info = module_info.ModuleInfo()
         targets = ['']
-        cwd = constant.ANDROID_ROOT_PATH
+        cwd = common_util.get_android_root_dir()
         self.assertEqual(
             targets,
             aidegen_main._compile_targets_for_whole_android_tree(
                 mod_info, targets, cwd))
         base_dir = 'frameworks/base'
         expected_targets = ['', base_dir]
-        cwd = os.path.join(constant.ANDROID_ROOT_PATH, base_dir)
+        cwd = os.path.join(common_util.get_android_root_dir(), base_dir)
         mock_get.return_value = None, cwd
         self.assertEqual(
             expected_targets,
             aidegen_main._compile_targets_for_whole_android_tree(
                 mod_info, targets, cwd))
         targets = [base_dir]
-        cwd = constant.ANDROID_ROOT_PATH
+        cwd = common_util.get_android_root_dir()
         self.assertEqual(
             expected_targets,
             aidegen_main._compile_targets_for_whole_android_tree(
                 mod_info, targets, cwd))
 
+    @mock.patch.object(os, 'getcwd')
+    def test_is_whole_android_tree(self, mock_getcwd):
+        """Test _is_whole_android_tree with different conditions."""
+        self.assertTrue(aidegen_main._is_whole_android_tree(['a'], True))
+        self.assertFalse(aidegen_main._is_whole_android_tree(['a'], False))
+        mock_getcwd.return_value = common_util.get_android_root_dir()
+        self.assertTrue(aidegen_main._is_whole_android_tree([''], False))
+        mock_getcwd.return_value = 'frameworks/base'
+        self.assertFalse(aidegen_main._is_whole_android_tree([''], False))
+
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/aidegen/constant.py b/aidegen/constant.py
index 5a37ea2..81611c9 100644
--- a/aidegen/constant.py
+++ b/aidegen/constant.py
@@ -15,39 +15,49 @@
 # limitations under the License.
 """The common definitions of AIDEgen"""
 
-import os
-
-from atest import constants
-
 # Env constant
 OUT_DIR_COMMON_BASE_ENV_VAR = 'OUT_DIR_COMMON_BASE'
 ANDROID_DEFAULT_OUT = 'out'
-ANDROID_HOST_OUT = os.environ.get(constants.ANDROID_HOST_OUT)
-ANDROID_ROOT_PATH = os.environ.get(constants.ANDROID_BUILD_TOP)
-AIDEGEN_ROOT_PATH = os.path.join(ANDROID_ROOT_PATH, 'tools/asuite/aidegen')
-ANDROID_OUT_DIR = os.environ.get(constants.ANDROID_OUT_DIR)
-OUT_DIR_COMMON_BASE = os.getenv(OUT_DIR_COMMON_BASE_ENV_VAR)
-
-# Constants for out dir
-ANDROID_OUT_DIR_COMMON_BASE = (os.path.join(
-    OUT_DIR_COMMON_BASE, os.path.basename(ANDROID_ROOT_PATH))
-                               if OUT_DIR_COMMON_BASE else None)
-OUT_DIR = ANDROID_OUT_DIR or ANDROID_OUT_DIR_COMMON_BASE or ANDROID_DEFAULT_OUT
-SOONG_OUT_DIR_PATH = os.path.join(ANDROID_ROOT_PATH, OUT_DIR, 'soong')
-RELATIVE_HOST_OUT = os.path.relpath(ANDROID_HOST_OUT, ANDROID_ROOT_PATH)
+AIDEGEN_ROOT_PATH = 'tools/asuite/aidegen'
 
 # Constants for module's info.
 KEY_PATH = 'path'
-KEY_DEP = 'dependencies'
+KEY_DEPENDENCIES = 'dependencies'
 KEY_DEPTH = 'depth'
+KEY_CLASS = 'class'
+KEY_INSTALLED = 'installed'
+KEY_SRCS = 'srcs'
+KEY_SRCJARS = 'srcjars'
+KEY_CLASSES_JAR = 'classes_jar'
 
 # Constants for IDE util.
 IDE_ECLIPSE = 'Eclipse'
 IDE_INTELLIJ = 'IntelliJ'
 IDE_ANDROID_STUDIO = 'Android Studio'
+IDE_NAME_DICT = {'j': IDE_INTELLIJ, 's': IDE_ANDROID_STUDIO, 'e': IDE_ECLIPSE}
 
 # Constants for asuite metrics
 EXIT_CODE_EXCEPTION = -1
 EXIT_CODE_NORMAL = 0
 EXIT_CODE_AIDEGEN_EXCEPTION = 1
 AIDEGEN_TOOL_NAME = 'aidegen'
+ANDROID_TREE = 'is_android_tree'
+
+# Constants for file names
+MERGED_MODULE_INFO = 'merged_module_info.json'
+BLUEPRINT_JSONFILE_NAME = 'module_bp_java_deps.json'
+
+# Content of iml file.
+FILE_IML = '''<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+@FACETS@
+    <component name="NewModuleRootManager" inherit-compiler-output="true">
+        <exclude-output />
+@SOURCES@
+@SRCJAR@
+        <orderEntry type="sourceFolder" forTests="false" />
+@MODULE_DEPENDENCIES@
+        <orderEntry type="inheritedJdk" />
+    </component>
+</module>
+'''
diff --git a/aidegen/lib/aidegen_metrics.py b/aidegen/lib/aidegen_metrics.py
new file mode 100644
index 0000000..844f30b
--- /dev/null
+++ b/aidegen/lib/aidegen_metrics.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+# Copyright 2018 - 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.
+
+"""AIDEgen metrics functions."""
+
+import logging
+import os
+import platform
+import sys
+
+from aidegen import constant
+from atest import atest_utils
+
+try:
+    from asuite.metrics import metrics
+    from asuite.metrics import metrics_base
+    from asuite.metrics import metrics_utils
+except ImportError:
+    logging.debug('Import metrics fail, can\'t send metrics')
+    metrics = None
+    metrics_base = None
+    metrics_utils = None
+
+
+def starts_asuite_metrics(references):
+    """Starts to record metrics data.
+
+    Send a metrics data to log server at the same time.
+
+    Args:
+        references: a list of reference data, when importing whole Android
+                    it contains 'is_android_tree'.
+    """
+    if not metrics:
+        return
+    atest_utils.print_data_collection_notice()
+    metrics_base.MetricsBase.tool_name = constant.AIDEGEN_TOOL_NAME
+    metrics_utils.get_start_time()
+    command = ' '.join(sys.argv)
+    metrics.AtestStartEvent(
+        command_line=command,
+        test_references=references,
+        cwd=os.getcwd(),
+        os=platform.platform())
+
+
+def ends_asuite_metrics(exit_code, stacktrace='', logs=''):
+    """Send the end event to log server.
+
+    Args:
+        exit_code: An integer of exit code.
+        stacktrace: A string of stacktrace.
+        logs: A string of logs.
+    """
+    if not metrics_utils:
+        return
+    metrics_utils.send_exit_event(
+        exit_code,
+        stacktrace=stacktrace,
+        logs=logs)
diff --git a/aidegen/lib/common_util.py b/aidegen/lib/common_util.py
index 730b442..305252c 100644
--- a/aidegen/lib/common_util.py
+++ b/aidegen/lib/common_util.py
@@ -22,6 +22,7 @@
 
 import logging
 import os
+import sys
 import time
 
 from functools import partial
@@ -52,6 +53,8 @@
 TARGET_CLASSES = JAVA_TARGET_CLASSES
 TARGET_CLASSES.extend(NATIVE_TARGET_CLASSES)
 _REBUILD_MODULE_INFO = '%s We should rebuild module-info.json file for it.'
+_ENVSETUP_NOT_RUN = ('Please run "source build/envsetup.sh" and "lunch" before '
+                     'running aidegen.')
 
 
 def time_logged(func=None, *, message='', maximum=1):
@@ -112,23 +115,23 @@
             paths = atest_module_info.get_paths(target)
             if paths:
                 rel_path = paths[0]
-                abs_path = os.path.join(constant.ANDROID_ROOT_PATH, rel_path)
+                abs_path = os.path.join(get_android_root_dir(), rel_path)
         # User inputs a module path or a relative path of android root folder.
         elif (atest_module_info.get_module_names(target) or os.path.isdir(
-                os.path.join(constant.ANDROID_ROOT_PATH, target))):
+                os.path.join(get_android_root_dir(), target))):
             rel_path = target.strip(os.sep)
-            abs_path = os.path.join(constant.ANDROID_ROOT_PATH, rel_path)
+            abs_path = os.path.join(get_android_root_dir(), rel_path)
         # User inputs a relative path of current directory.
         else:
             abs_path = os.path.abspath(os.path.join(os.getcwd(), target))
-            rel_path = os.path.relpath(abs_path, constant.ANDROID_ROOT_PATH)
+            rel_path = os.path.relpath(abs_path, get_android_root_dir())
     else:
         # User doesn't input.
         abs_path = os.getcwd()
         if is_android_root(abs_path):
             rel_path = ''
         else:
-            rel_path = os.path.relpath(abs_path, constant.ANDROID_ROOT_PATH)
+            rel_path = os.path.relpath(abs_path, get_android_root_dir())
     return rel_path, abs_path
 
 
@@ -159,7 +162,7 @@
     Returns:
         True if abs_path is Android root, otherwise False.
     """
-    return abs_path == constant.ANDROID_ROOT_PATH
+    return abs_path == get_android_root_dir()
 
 
 def has_build_target(atest_module_info, rel_path):
@@ -246,7 +249,7 @@
         err = FAKE_MODULE_ERROR.format(target)
         logging.error(err)
         raise FakeModuleError(err)
-    if not abs_path.startswith(constant.ANDROID_ROOT_PATH):
+    if not abs_path.startswith(get_android_root_dir()):
         err = OUTSIDE_ROOT_ERROR.format(abs_path)
         logging.error(err)
         raise ProjectOutsideAndroidRootError(err)
@@ -272,17 +275,17 @@
     """Get absolute path from a relative path.
 
     Args:
-        rel_path: A string, a relative path to constant.ANDROID_ROOT_PATH.
+        rel_path: A string, a relative path to get_android_root_dir().
 
     Returns:
         abs_path: A string, an absolute path starts with
-                  constant.ANDROID_ROOT_PATH.
+                  get_android_root_dir().
     """
     if not rel_path:
-        return constant.ANDROID_ROOT_PATH
-    if rel_path.startswith(constant.ANDROID_ROOT_PATH):
+        return get_android_root_dir()
+    if rel_path.startswith(get_android_root_dir()):
         return rel_path
-    return os.path.join(constant.ANDROID_ROOT_PATH, rel_path)
+    return os.path.join(get_android_root_dir(), rel_path)
 
 
 def is_project_path_relative_module(data, project_relative_path):
@@ -328,13 +331,16 @@
     return any(src_file.endswith(x) for x in src_file_extensions)
 
 
-def get_atest_module_info(targets):
+def get_atest_module_info(targets=None):
     """Get the right version of atest ModuleInfo instance.
 
     The rules:
-        Check if the targets don't exist in ModuleInfo, we'll regain a new atest
-        ModleInfo instance by setting force_build=True and call _check_modules
-        again. If targets still don't exist, raise exceptions.
+        1. If targets is None:
+           We just want to get an atest ModuleInfo instance.
+        2. If targets isn't None:
+           Check if the targets don't exist in ModuleInfo, we'll regain a new
+           atest ModleInfo instance by setting force_build=True and call
+           _check_modules again. If targets still don't exist, raise exceptions.
 
     Args:
         targets: A list of targets to be built.
@@ -343,7 +349,120 @@
         An atest ModuleInfo instance.
     """
     amodule_info = module_info.ModuleInfo()
-    if not _check_modules(amodule_info, targets, raise_on_lost_module=False):
+    if targets and not _check_modules(
+            amodule_info, targets, raise_on_lost_module=False):
         amodule_info = module_info.ModuleInfo(force_build=True)
         _check_modules(amodule_info, targets)
     return amodule_info
+
+
+def read_file_content(path):
+    """Read file's content.
+
+    Args:
+        path: Path of input file.
+
+    Returns:
+        String: Content of the file.
+    """
+    with open(path) as template:
+        return template.read()
+
+
+def file_generate(path, content):
+    """Generate file from content.
+
+    Args:
+        path: Path of target file.
+        content: String content of file.
+    """
+    if not os.path.exists(os.path.dirname(path)):
+        os.makedirs(os.path.dirname(path))
+    with open(path, 'w') as target:
+        target.write(content)
+
+
+def get_blueprint_json_path():
+    """Assemble the path of blueprint json file.
+
+    Returns:
+        module_bp_java_deps.json path.
+    """
+    return os.path.join(get_soong_out_path(),
+                        constant.BLUEPRINT_JSONFILE_NAME)
+
+
+def back_to_cwd(func):
+    """Decorate a function change directory back to its original one.
+
+    Args:
+        func: a function is to be changed back to its original directory.
+
+    Returns:
+        The wrapper function.
+    """
+    @wraps(func)
+    def wrapper(*args, **kwargs):
+        """A wrapper function."""
+        cwd = os.getcwd()
+        try:
+            return func(*args, **kwargs)
+        finally:
+            os.chdir(cwd)
+
+    return wrapper
+
+
+def get_android_out_dir():
+    """Get out directory in different conditions.
+
+    Returns:
+        Android out directory path.
+    """
+    android_root_path = get_android_root_dir()
+    android_out_dir = os.environ.get(constants.ANDROID_OUT_DIR)
+    out_dir_common_base = os.getenv(constant.OUT_DIR_COMMON_BASE_ENV_VAR)
+    android_out_dir_common_base = (os.path.join(
+        out_dir_common_base, os.path.basename(android_root_path))
+                                   if out_dir_common_base else None)
+    return (android_out_dir or android_out_dir_common_base or
+            constant.ANDROID_DEFAULT_OUT)
+
+
+def get_android_root_dir():
+    """Get Android root directory.
+
+    If the path doesn't exist show message to ask users to run envsetup.sh and
+    lunch first.
+
+    Returns:
+        Android root directory path.
+    """
+    android_root_path = os.environ.get(constants.ANDROID_BUILD_TOP)
+    if not android_root_path:
+        _show_env_setup_msg_and_exit()
+    return android_root_path
+
+
+def get_aidegen_root_dir():
+    """Get AIDEGen root directory.
+
+    Returns:
+        AIDEGen root directory path.
+    """
+    return os.path.join(get_android_root_dir(), constant.AIDEGEN_ROOT_PATH)
+
+
+def _show_env_setup_msg_and_exit():
+    """Show message if users didn't run envsetup.sh and lunch."""
+    print(_ENVSETUP_NOT_RUN)
+    sys.exit(0)
+
+
+def get_soong_out_path():
+    """Assemble out directory's soong path.
+
+    Returns:
+        Out directory's soong path.
+    """
+    return os.path.join(get_android_root_dir(), get_android_out_dir(), 'soong')
diff --git a/aidegen/lib/common_util_unittest.py b/aidegen/lib/common_util_unittest.py
index bea77a1..872c115 100644
--- a/aidegen/lib/common_util_unittest.py
+++ b/aidegen/lib/common_util_unittest.py
@@ -25,7 +25,6 @@
 from aidegen.lib.errors import ProjectOutsideAndroidRootError
 from aidegen.lib.errors import ProjectPathNotExistError
 import aidegen.unittest_constants as uc
-from aidegen import constant
 from aidegen.lib import common_util
 from atest import module_info
 
@@ -35,10 +34,12 @@
 class AidegenCommonUtilUnittests(unittest.TestCase):
     """Unit tests for common_util.py"""
 
+    @mock.patch.object(common_util, 'get_android_root_dir')
     @mock.patch.object(module_info.ModuleInfo, 'get_module_names')
     @mock.patch.object(module_info.ModuleInfo, 'get_paths')
     @mock.patch.object(module_info.ModuleInfo, 'is_module')
-    def test_get_related_paths(self, mock_is_mod, mock_get, mock_names):
+    def test_get_related_paths(self, mock_is_mod, mock_get, mock_names,
+                               mock_get_root):
         """Test get_related_paths with different conditions."""
         mock_is_mod.return_value = True
         mock_get.return_value = []
@@ -46,7 +47,7 @@
         self.assertEqual((None, None),
                          common_util.get_related_paths(mod_info,
                                                        uc.TEST_MODULE))
-        constant.ANDROID_ROOT_PATH = uc.TEST_PATH
+        mock_get_root.return_value = uc.TEST_PATH
         mock_get.return_value = [uc.TEST_MODULE]
         expected = (uc.TEST_MODULE, os.path.join(uc.TEST_PATH, uc.TEST_MODULE))
         self.assertEqual(
@@ -56,22 +57,26 @@
         self.assertEqual(
             expected, common_util.get_related_paths(mod_info, uc.TEST_MODULE))
 
+    @mock.patch.object(common_util, 'get_android_root_dir')
     @mock.patch.object(common_util, 'get_related_paths')
-    def test_is_target_android_root(self, mock_get):
+    def test_is_target_android_root(self, mock_get_rel, mock_get_root):
         """Test is_target_android_root with different conditions."""
-        mock_get.return_value = None, uc.TEST_PATH
+        mock_get_rel.return_value = None, uc.TEST_PATH
+        mock_get_root.return_value = uc.TEST_PATH
         self.assertTrue(
             common_util.is_target_android_root(module_info.ModuleInfo(),
                                                [uc.TEST_MODULE]))
-        mock_get.return_value = None, ''
+        mock_get_rel.return_value = None, ''
         self.assertFalse(
             common_util.is_target_android_root(module_info.ModuleInfo(),
                                                [uc.TEST_MODULE]))
 
+    @mock.patch.object(common_util, 'get_android_root_dir')
     @mock.patch.object(common_util, 'has_build_target')
     @mock.patch('os.path.isdir')
     @mock.patch.object(common_util, 'get_related_paths')
-    def test_check_module(self, mock_get, mock_isdir, mock_has_target):
+    def test_check_module(self, mock_get, mock_isdir, mock_has_target,
+                          mock_get_root):
         """Test if _check_module raises errors with different conditions."""
         mod_info = module_info.ModuleInfo()
         mock_get.return_value = None, None
@@ -79,7 +84,7 @@
             common_util._check_module(mod_info, uc.TEST_MODULE)
             expected = common_util.FAKE_MODULE_ERROR.format(uc.TEST_MODULE)
             self.assertEqual(expected, str(ctx.exception))
-        constant.ANDROID_ROOT_PATH = uc.TEST_PATH
+        mock_get_root.return_value = uc.TEST_PATH
         mock_get.return_value = None, uc.TEST_MODULE
         with self.assertRaises(ProjectOutsideAndroidRootError) as ctx:
             common_util._check_module(mod_info, uc.TEST_MODULE)
@@ -116,9 +121,10 @@
         self.assertEqual(common_util._check_modules(mod_info, [target], False),
                          False)
 
-    def test_get_abs_path(self):
+    @mock.patch.object(common_util, 'get_android_root_dir')
+    def test_get_abs_path(self, mock_get_root):
         """Test get_abs_path handling."""
-        constant.ANDROID_ROOT_PATH = uc.TEST_DATA_PATH
+        mock_get_root.return_value = uc.TEST_DATA_PATH
         self.assertEqual(uc.TEST_DATA_PATH, common_util.get_abs_path(''))
         test_path = os.path.join(uc.TEST_DATA_PATH, 'test.jar')
         self.assertEqual(test_path, common_util.get_abs_path(test_path))
diff --git a/aidegen/lib/config.py b/aidegen/lib/config.py
index da1ca7b..d170f3c 100644
--- a/aidegen/lib/config.py
+++ b/aidegen/lib/config.py
@@ -21,16 +21,40 @@
 import logging
 import os
 
-_DEFAULT_CONFIG_FILE = 'aidegen.config'
+from aidegen.lib import common_util
 
 
 class AidegenConfig():
-    """Class manages AIDEGen's configurations."""
+    """Class manages AIDEGen's configurations.
+
+    Attributes:
+        _config: A dict contains the aidegen config.
+        _config_backup: A dict contains the aidegen config.
+    """
+
+    # Constants of AIDEGen config
+    _DEFAULT_CONFIG_FILE = 'aidegen.config'
+    _CONFIG_DIR = os.path.join(
+        os.path.expanduser('~'), '.config', 'asuite', 'aidegen')
+    _CONFIG_FILE_PATH = os.path.join(_CONFIG_DIR, _DEFAULT_CONFIG_FILE)
+
+    # Constants of enable debugger
+    _ENABLE_DEBUG_CONFIG_DIR = 'enable_debugger'
+    _ENABLE_DEBUG_CONFIG_FILE = 'enable_debugger.iml'
+    _ENABLE_DEBUG_TEMPLATE_FILE = os.path.join(
+        common_util.get_aidegen_root_dir(), 'templates',
+        _ENABLE_DEBUG_CONFIG_DIR, _ENABLE_DEBUG_CONFIG_FILE)
+    _ENABLE_DEBUG_DIR = os.path.join(_CONFIG_DIR, _ENABLE_DEBUG_CONFIG_DIR)
+    _ANDROID_MANIFEST_FILE_NAME = 'AndroidManifest.xml'
+    _DIR_SRC = 'src'
+    _DIR_GEN = 'gen'
+    DEBUG_ENABLED_FILE_PATH = os.path.join(_ENABLE_DEBUG_DIR,
+                                           _ENABLE_DEBUG_CONFIG_FILE)
 
     def __init__(self):
         self._config = {}
         self._config_backup = {}
-        self._config_file = self._get_default_config_file()
+        self._create_config_folder()
 
     def __enter__(self):
         self._load_aidegen_config()
@@ -61,13 +85,13 @@
 
     def _load_aidegen_config(self):
         """Load data from configuration file."""
-        if os.path.exists(self._config_file):
+        if os.path.exists(self._CONFIG_FILE_PATH):
             try:
-                with open(self._config_file, 'r') as cfg_file:
+                with open(self._CONFIG_FILE_PATH, 'r') as cfg_file:
                     self._config = json.load(cfg_file)
             except ValueError as err:
                 info = '{} format is incorrect, error: {}'.format(
-                    self._config_file, err)
+                    self._CONFIG_FILE_PATH, err)
                 logging.info(info)
             except IOError as err:
                 logging.error(err)
@@ -76,7 +100,7 @@
     def _save_aidegen_config(self):
         """Save data to configuration file."""
         if self._is_config_modified():
-            with open(self._config_file, 'w') as cfg_file:
+            with open(self._CONFIG_FILE_PATH, 'w') as cfg_file:
                 json.dump(self._config, cfg_file, indent=4)
 
     def _is_config_modified(self):
@@ -84,11 +108,61 @@
         return any(key for key in self._config if not key in self._config_backup
                    or self._config[key] != self._config_backup[key])
 
-    @staticmethod
-    def _get_default_config_file():
-        """Return path of default configuration file."""
-        cfg_path = os.path.join(
-            os.path.expanduser('~'), '.config', 'asuite', 'aidegen')
-        if not os.path.exists(cfg_path):
-            os.makedirs(cfg_path)
-        return os.path.join(cfg_path, _DEFAULT_CONFIG_FILE)
+    def _create_config_folder(self):
+        """Create the config folder if it doesn't exist."""
+        if not os.path.exists(self._CONFIG_DIR):
+            os.makedirs(self._CONFIG_DIR)
+
+    def _gen_enable_debug_sub_dir(self, dir_name):
+        """Generate a dir under enable debug dir.
+
+        Args:
+            dir_name: A string of the folder name.
+        """
+        _dir = os.path.join(self._ENABLE_DEBUG_DIR, dir_name)
+        if not os.path.exists(_dir):
+            os.makedirs(_dir)
+
+    def _gen_empty_androidmanifest(self):
+        """Generate an empty AndroidManifest.xml under enable debug dir."""
+        _file = os.path.join(self._ENABLE_DEBUG_DIR,
+                             self._ANDROID_MANIFEST_FILE_NAME)
+        if not os.path.exists(_file):
+            common_util.file_generate(_file, '')
+
+    def _gen_enable_debugger_config(self, api_level):
+        """Generate the enable_debugger.iml config file.
+
+        Create the enable_debugger.iml if it doesn't exist.
+
+        Args:
+            api_level: An integer of API level.
+        """
+        if not os.path.exists(self.DEBUG_ENABLED_FILE_PATH):
+            content = common_util.read_file_content(
+                self._ENABLE_DEBUG_TEMPLATE_FILE).format(API_LEVEL=api_level)
+            common_util.file_generate(self.DEBUG_ENABLED_FILE_PATH, content)
+
+    def create_enable_debugger_module(self, api_level):
+        """Create the enable_debugger module.
+
+        1. Create two empty folders named src and gen.
+        2. Create an empty file named AndroidManifest.xml
+        3. Create the enable_denugger.iml.
+
+        Args:
+            api_level: An integer of API level.
+
+        Returns: True if successfully generate the enable debugger module,
+                 otherwise False.
+        """
+        try:
+            self._gen_enable_debug_sub_dir(self._DIR_SRC)
+            self._gen_enable_debug_sub_dir(self._DIR_GEN)
+            self._gen_empty_androidmanifest()
+            self._gen_enable_debugger_config(api_level)
+            return True
+        except (IOError, OSError) as err:
+            logging.warning(('Can\'t create the enable_debugger module in %s.\n'
+                             '%s'), self._CONFIG_DIR, err)
+            return False
diff --git a/aidegen/lib/config_unittest.py b/aidegen/lib/config_unittest.py
new file mode 100644
index 0000000..b51c356
--- /dev/null
+++ b/aidegen/lib/config_unittest.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019, 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.
+
+"""Unittests for AidegenConfig class."""
+
+import os
+import unittest
+
+from aidegen import unittest_constants
+from aidegen.lib import config
+from aidegen.lib import common_util
+
+
+# pylint: disable=protected-access
+class AidegenConfigUnittests(unittest.TestCase):
+    """Unit tests for config.py"""
+
+    _ENABLE_DEBUG_CONFIG_FILE = 'enable_debugger.iml'
+    _TEST_API_LEVEL = '28'
+    _TEST_DATA_PATH = unittest_constants.TEST_DATA_PATH
+    _ANDROID_PROJECT_PATH = os.path.join(_TEST_DATA_PATH, 'android_project')
+    _ENABLE_DEBUGGER_IML_SAMPLE = os.path.join(_TEST_DATA_PATH,
+                                               _ENABLE_DEBUG_CONFIG_FILE)
+    _GENERATED_ENABLE_DEBUGGER_IML = os.path.join(_ANDROID_PROJECT_PATH,
+                                                  _ENABLE_DEBUG_CONFIG_FILE)
+
+    def test_gen_enable_debugger_config(self):
+        """Test _gen_enable_debugger_config."""
+        try:
+            _cfg = config.AidegenConfig()
+            _cfg.DEBUG_ENABLED_FILE_PATH = os.path.join(
+                self._ANDROID_PROJECT_PATH, _cfg._ENABLE_DEBUG_CONFIG_FILE)
+            _cfg._gen_enable_debugger_config(self._TEST_API_LEVEL)
+            expected_content = common_util.read_file_content(
+                self._ENABLE_DEBUGGER_IML_SAMPLE)
+            test_content = common_util.read_file_content(
+                self._GENERATED_ENABLE_DEBUGGER_IML)
+            self.assertEqual(test_content, expected_content)
+        finally:
+            if os.path.exists(self._GENERATED_ENABLE_DEBUGGER_IML):
+                os.remove(self._GENERATED_ENABLE_DEBUGGER_IML)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/aidegen/lib/eclipse_project_file_gen.py b/aidegen/lib/eclipse_project_file_gen.py
new file mode 100644
index 0000000..5c45d97
--- /dev/null
+++ b/aidegen/lib/eclipse_project_file_gen.py
@@ -0,0 +1,235 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - 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.
+
+"""It is an AIDEGen sub task: generate the .project file for Eclipse."""
+
+import os
+
+from aidegen import constant
+from aidegen.lib import common_util
+from aidegen.lib.project_file_gen import ProjectFileGenerator
+
+
+class EclipseConf(ProjectFileGenerator):
+    """Class to generate project file under the module path for Eclipse.
+
+    Attributes:
+        module_abspath: The absolute path of the target project.
+        module_relpath: The relative path of the target project.
+        module_name: The name of the target project.
+        jar_module_paths: A dict records a mapping of jar file and module path.
+        r_java_paths: A list contains the relative folder paths of the R.java
+                      files.
+        project_file: The absolutely path of .project file.
+        template_project_content: A string of a template project_file content.
+        project_content: A string ready to be written into project_file.
+        src_paths: A list contains the project's source paths.
+        classpath_file: The absolutely path of .classpath file.
+        classpath_content: A string ready to be written into classpath_file.
+    """
+    # Constants of .project file
+    _TEMPLATE_PROJECT_FILE = os.path.join(common_util.get_aidegen_root_dir(),
+                                          'templates/eclipse/project.xml')
+    _PROJECT_LINK = ('                <link><name>{}</name><type>2</type>'
+                     '<location>{}</location></link>\n')
+    _PROJECT_FILENAME = '.project'
+
+    # constans of .classpath file
+    _TEMPLATE_CLASSPATH_FILE = os.path.join(common_util.get_aidegen_root_dir(),
+                                            'templates/eclipse/classpath.xml')
+    _CLASSPATH_SRC_ENTRY = ('    <classpathentry kind="src" path="{}"/>\n')
+    _CLASSPATH_LIB_ENTRY = ('    <classpathentry exported="true" kind="lib" '
+                            'path="{}" sourcepath="{}"/>\n')
+    _CLASSPATH_FILENAME = '.classpath'
+
+    def __init__(self, project):
+        """Initialize class.
+
+        Args:
+            project: A ProjectInfo instance.
+        """
+        super().__init__(project)
+        self.module_abspath = project.project_absolute_path
+        self.module_relpath = project.project_relative_path
+        self.module_name = project.module_name
+        self.jar_module_paths = project.source_path['jar_module_path']
+        self.r_java_paths = list(project.source_path['r_java_path'])
+        # Related value for generating .project.
+        self.project_file = os.path.join(self.module_abspath,
+                                         self._PROJECT_FILENAME)
+        self.template_project_content = common_util.read_file_content(
+            self._TEMPLATE_PROJECT_FILE)
+        self.project_content = ''
+        # Related value for generating .classpath.
+        self.src_paths = list(project.source_path['source_folder_path'])
+        self.src_paths.extend(project.source_path['test_folder_path'])
+        self.classpath_file = os.path.join(self.module_abspath,
+                                           self._CLASSPATH_FILENAME)
+        self.classpath_content = common_util.read_file_content(
+            self._TEMPLATE_CLASSPATH_FILE)
+
+    def _gen_r_link(self):
+        """Generate the link resources of the R paths.
+
+        E.g.
+            <link>
+                <name>dependencies/out/target/common/R</name>
+                <type>2</type>
+                <location>{ANDROID_ROOT_PATH}/out/target/common/R</location>
+            </link>
+
+        Returns: A set contains R paths link resources strings.
+        """
+        return {self._gen_link(r_path) for r_path in self.r_java_paths}
+
+    def _gen_src_links(self, relpaths):
+        """Generate the link resources from relpaths.
+
+        Args:
+            relpaths: A list of module paths which are relative to
+                      ANDROID_BUILD_TOP.
+                      e.g. ['relpath/to/module1', 'relpath/to/module2', ...]
+
+        Returns: A set includes all unique link resources.
+        """
+        return {self._gen_link(relpath) for relpath in relpaths}
+
+    @classmethod
+    def _gen_link(cls, relpath):
+        """Generate a link resource from a relative path.
+
+         E.g.
+            <link>
+                <name>dependencies/path/to/relpath</name>
+                <type>2</type>
+                <location>/absolute/path/to/relpath</location>
+            </link>
+
+        Args:
+            relpath: A string of a relative path to Android_BUILD_TOP.
+
+        Returns: A string of link resource.
+        """
+        alias_name = os.path.join(constant.KEY_DEPENDENCIES, relpath)
+        abs_path = os.path.join(common_util.get_android_root_dir(), relpath)
+        return cls._PROJECT_LINK.format(alias_name, abs_path)
+
+    def _create_project_content(self):
+        """Create the project file .project under the module."""
+        # links is a set to save unique link resources.
+        links = self._gen_src_links(self.jar_module_paths.values())
+        links.update(self._gen_r_link())
+        self.project_content = self.template_project_content.format(
+            PROJECTNAME=self.module_name,
+            LINKEDRESOURCES=''.join(sorted(list(links))))
+
+    def _gen_r_path_entries(self):
+        """Generate the class path entries for the R paths.
+
+        E.g.
+            <classpathentry kind="src"
+                path="dependencies/out/target/common/R"/>
+            <classpathentry kind="src"
+                path="dependencies/out/soong/.intermediates/packages/apps/
+                      Settings/Settings/android_common/gen/aapt2/R"/>
+
+        Returns: A list of the R path's class path entry.
+        """
+        r_entry_list = []
+        for r_path in self.r_java_paths:
+            alias_path = os.path.join(constant.KEY_DEPENDENCIES, r_path)
+            r_entry_list.append(self._CLASSPATH_SRC_ENTRY.format(alias_path))
+        return r_entry_list
+
+    def _gen_src_path_entries(self):
+        """Generate the class path entries from srcs.
+
+        E.g.
+            The source folder paths list:
+                ['packages/apps/Settings/src',
+                 'packages/apps/Settings/tests/robotests/src',
+                 'packages/apps/Settings/tests/uitests/src',
+                 'packages/apps/Settings/tests/unit/src'
+                ]
+            It will generate the related <classpathentry> list:
+                ['<classpathentry kind="src" path="src"/>',
+                 '<classpathentry kind="src" path="tests/robotests/src"/>',
+                 '<classpathentry kind="src" path="tests/uitests/src"/>',
+                 '<classpathentry kind="src" path="tests/unit/src"/>'
+                ]
+
+        Returns: A list of source folders' class path entries.
+        """
+        src_path_entries = []
+        for src in self.src_paths:
+            src = src.replace(self.module_relpath, '').strip(os.sep)
+            src_path_entries.append(self._CLASSPATH_SRC_ENTRY.format(src))
+        return src_path_entries
+
+    def _gen_jar_path_entries(self):
+        """Generate the jar files' class path entries.
+
+        The self.jar_module_paths is a dictionary.
+        e.g.
+            {'/abspath/to/the/file.jar': 'relpath/to/the/module'}
+        This method will generate the <classpathentry> for each jar file.
+        The format of <classpathentry> looks like:
+        <classpathentry exported="true" kind="lib"
+            path="/abspath/to/the/file.jar"
+            sourcepath="dependencies/relpath/to/the/module"/>
+
+        Returns: A list of jar files' class path entries.
+        """
+        jar_entries = []
+        for jar_relpath, module_relpath in self.jar_module_paths.items():
+            jar_abspath = os.path.join(common_util.get_android_root_dir(),
+                                       jar_relpath)
+            alias_module_path = os.path.join(constant.KEY_DEPENDENCIES,
+                                             module_relpath)
+            jar_entries.append(self._CLASSPATH_LIB_ENTRY.format(
+                jar_abspath, alias_module_path))
+        return jar_entries
+
+    def _create_classpath_content(self):
+        """Create the project file .classpath under the module."""
+        src_entries = self._gen_src_path_entries()
+        src_entries.extend(self._gen_r_path_entries())
+        jar_entries = self._gen_jar_path_entries()
+        self.classpath_content = self.classpath_content.format(
+            SRC=''.join(sorted(src_entries)),
+            LIB=''.join(sorted(jar_entries)))
+
+    def generate_project_file(self):
+        """Generate .project file of the target module."""
+        self._create_project_content()
+        common_util.file_generate(self.project_file, self.project_content)
+
+    def generate_classpath_file(self):
+        """Generate .classpath file of the target module."""
+        self._create_classpath_content()
+        common_util.file_generate(self.classpath_file, self.classpath_content)
+
+    @classmethod
+    def generate_ide_project_files(cls, projects):
+        """Generate Eclipse project files by a list of ProjectInfo instances.
+
+        Args:
+            projects: A list of ProjectInfo instances.
+        """
+        for project in projects:
+            eclipse_configure = EclipseConf(project)
+            eclipse_configure.generate_project_file()
+            eclipse_configure.generate_classpath_file()
diff --git a/aidegen/lib/eclipse_project_file_gen_unittest.py b/aidegen/lib/eclipse_project_file_gen_unittest.py
new file mode 100644
index 0000000..5c0456d
--- /dev/null
+++ b/aidegen/lib/eclipse_project_file_gen_unittest.py
@@ -0,0 +1,122 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019, 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.
+
+"""Unittests for generate project file of Eclipse."""
+
+import os
+import unittest
+from unittest import mock
+
+import aidegen.unittest_constants as utc
+from aidegen import constant
+from aidegen.lib import common_util
+from aidegen.lib.eclipse_project_file_gen import EclipseConf
+
+
+# pylint: disable=protected-access
+class EclipseConfUnittests(unittest.TestCase):
+    """Unit tests for generate_project_file.py"""
+    _ROOT_PATH = '/android/root'
+    _PROJECT_RELPATH = 'module/path'
+    _PROJECT_ABSPATH = os.path.join(_ROOT_PATH, _PROJECT_RELPATH)
+    _PROJECT_NAME = 'test'
+    _LINK_TEMPLATE = ('                <link><name>%s</name><type>2</type>'
+                      '<location>%s</location></link>\n')
+    _PROJECT_SAMPLE = os.path.join(utc.TEST_DATA_PATH, 'eclipse.project')
+
+    @mock.patch.object(common_util, 'get_android_root_dir')
+    def test_gen_link(self, mock_get_root):
+        """Test get_link return a correct link resource config."""
+        mock_get_root.return_value = self._ROOT_PATH
+        name = os.path.join(constant.KEY_DEPENDENCIES, self._PROJECT_RELPATH)
+        expected_link = self._LINK_TEMPLATE % (name, self._PROJECT_ABSPATH)
+        generated_link = EclipseConf._gen_link(self._PROJECT_RELPATH)
+        self.assertEqual(generated_link, expected_link)
+
+    @mock.patch.object(common_util, 'get_android_root_dir')
+    @mock.patch('aidegen.lib.project_info.ProjectInfo')
+    def test_create_project_content(self, mock_project_info, mock_get_root):
+        """Test _create_project_content."""
+        mock_get_root.return_value = self._ROOT_PATH
+        mock_project_info.project_absolute_path = self._PROJECT_ABSPATH
+        mock_project_info.module_name = self._PROJECT_NAME
+        mock_project_info.source_path = {
+            'source_folder_path': '',
+            'test_folder_path': '',
+            'jar_module_path': {
+                '': self._PROJECT_RELPATH
+            },
+            'r_java_path': set()
+        }
+        expected_content = common_util.read_file_content(self._PROJECT_SAMPLE)
+        eclipse_config = EclipseConf(mock_project_info)
+        eclipse_config._create_project_content()
+        generated_content = eclipse_config.project_content
+        self.assertEqual(generated_content, expected_content)
+
+    @mock.patch.object(common_util, 'get_android_root_dir')
+    @mock.patch('aidegen.lib.project_info.ProjectInfo')
+    def test_gen_src_path_entries(self, mock_project_info, mock_get_root):
+        """Test generate source folders' class path entries."""
+        mock_get_root.return_value = self._ROOT_PATH
+        mock_project_info.project_absolute_path = self._PROJECT_ABSPATH
+        mock_project_info.project_relative_path = self._PROJECT_RELPATH
+        mock_project_info.module_name = self._PROJECT_NAME
+        mock_project_info.source_path = {
+            'source_folder_path': set([
+                'module/path/src',
+                'module/path/test',
+            ]),
+            'test_folder_path': set(),
+            'jar_module_path': {},
+            'r_java_path': {}
+        }
+        expected_result = [
+            '    <classpathentry kind="src" path="src"/>\n',
+            '    <classpathentry kind="src" path="test"/>\n',
+        ]
+        eclipse_config = EclipseConf(mock_project_info)
+        generated_result = sorted(eclipse_config._gen_src_path_entries())
+        self.assertEqual(generated_result, expected_result)
+
+    @mock.patch.object(common_util, 'get_android_root_dir')
+    @mock.patch('aidegen.lib.project_info.ProjectInfo')
+    def test_gen_jar_path_entries(self, mock_project_info, mock_get_root):
+        """Test generate jar files' class path entries."""
+        mock_get_root.return_value = self._ROOT_PATH
+        mock_project_info.project_absolute_path = self._PROJECT_ABSPATH
+        mock_project_info.project_relative_path = self._PROJECT_RELPATH
+        mock_project_info.module_name = self._PROJECT_NAME
+        mock_project_info.source_path = {
+            'source_folder_path': set(),
+            'test_folder_path': set(),
+            'jar_module_path': {
+                '/abspath/to/the/file.jar': 'relpath/to/the/module',
+            },
+            'r_java_path': {}
+        }
+        expected_result = [
+            ('    <classpathentry exported="true" kind="lib" '
+             'path="/abspath/to/the/file.jar" '
+             'sourcepath="dependencies/relpath/to/the/module"/>\n')
+        ]
+        eclipse_config = EclipseConf(mock_project_info)
+        generated_result = eclipse_config._gen_jar_path_entries()
+        self.assertEqual(generated_result, expected_result)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/aidegen/lib/errors.py b/aidegen/lib/errors.py
index 158c433..d7142e9 100644
--- a/aidegen/lib/errors.py
+++ b/aidegen/lib/errors.py
@@ -57,3 +57,7 @@
 
 class FakeModuleError(AIDEgenError):
     """Raised if the module is a fake module."""
+
+
+class InvalidXMLError(AIDEgenError):
+    """Raised if parsing xml file failed."""
diff --git a/aidegen/lib/ide_util.py b/aidegen/lib/ide_util.py
index 43c17d0..6a0e2a5 100644
--- a/aidegen/lib/ide_util.py
+++ b/aidegen/lib/ide_util.py
@@ -23,8 +23,8 @@
 
     ide_util_obj = IdeUtil()
     if ide_util_obj.is_ide_installed():
-        ide_util_obj.config_ide()
-        ide_util_obj.launch_ide(project_file)
+        ide_util_obj.config_ide(project_file)
+        ide_util_obj.launch_ide()
 """
 
 import fnmatch
@@ -35,7 +35,9 @@
 import subprocess
 
 from aidegen import constant
-from aidegen.lib.config import AidegenConfig
+from aidegen.lib import common_util
+from aidegen.lib import config
+from aidegen.lib import sdk_config
 
 # Add 'nohup' to prevent IDE from being terminated when console is terminated.
 _NOHUP = 'nohup'
@@ -43,7 +45,6 @@
 _IDEA_FOLDER = '.idea'
 _IML_EXTENSION = '.iml'
 _JDK_PATH_TOKEN = '@JDKpath'
-_TARGET_JDK_NAME_TAG = '<name value="JDK18" />'
 _COMPONENT_END_TAG = '  </component>'
 
 
@@ -55,8 +56,8 @@
 
     For example:
         1. Check if IDE is installed.
-        2. Launch an IDE.
-        3. Config IDE, e.g. config code style, SDK path, and etc.
+        2. Config IDE, e.g. config code style, SDK path, and etc.
+        3. Launch an IDE.
     """
 
     def __init__(self,
@@ -76,16 +77,17 @@
         """
         return self._ide.is_ide_installed()
 
-    def launch_ide(self, project_file):
-        """Launches the relative IDE by opening the passed project file.
+    def launch_ide(self):
+        """Launches the relative IDE by opening the passed project file."""
+        return self._ide.launch_ide()
+
+    def config_ide(self, project_abspath):
+        """To config the IDE, e.g., setup code style, init SDK, and etc.
 
         Args:
-            project_file: The full path of the IDE project file.
+            project_abspath: An absolute path of the project.
         """
-        return self._ide.launch_ide(project_file)
-
-    def config_ide(self):
-        """To config the IDE, e.g., setup code style, init SDK, and etc."""
+        self._ide.project_abspath = project_abspath
         if self.is_ide_installed() and self._ide:
             self._ide.apply_optional_config()
 
@@ -108,6 +110,7 @@
         _bin_paths: A list of all possible IDE executable file absolute paths.
         _ide_name: String for IDE name.
         _bin_folders: A list of all possible IDE installed paths.
+        project_abspath: The absolute path of the project.
 
     For example:
         1. Check if IDE is installed.
@@ -122,6 +125,7 @@
         self._bin_file_name = ''
         self._bin_paths = []
         self._bin_folders = []
+        self.project_abspath = ''
 
     def is_ide_installed(self):
         """Checks if IDE is already installed.
@@ -131,14 +135,9 @@
         """
         return bool(self._installed_path)
 
-    def launch_ide(self, project_file):
-        """Launches IDE by opening the passed project file.
-
-        Args:
-            project_file: The full path of the IDE's project file.
-        """
-        _launch_ide(project_file, self._get_ide_cmd(project_file),
-                    self._ide_name)
+    def launch_ide(self):
+        """Launches IDE by opening the passed project file."""
+        _launch_ide(self.project_abspath, self._get_ide_cmd(), self._ide_name)
 
     def apply_optional_config(self):
         """Handles IDE relevant configs."""
@@ -154,16 +153,13 @@
         """Gets IDE name."""
         return self._ide_name
 
-    def _get_ide_cmd(self, project_file):
+    def _get_ide_cmd(self):
         """Compose launch IDE command to run a new process and redirect output.
 
-        Args:
-            project_file: The full path of the IDE's project file.
-
         Returns:
             A string of launch IDE command.
         """
-        return _get_run_ide_cmd(self._installed_path, project_file)
+        return _get_run_ide_cmd(self._installed_path, self.project_abspath)
 
     def _init_installed_path(self, installed_path):
         """Initialize IDE installed path.
@@ -197,7 +193,6 @@
         _JDK_PATH: The path of JDK in android project.
         _IDE_JDK_TABLE_PATH: The path of JDK table which record JDK info in IDE.
         _JDK_PART_TEMPLATE_PATH: The path of the template of partial JDK table.
-        _JDK_FULL_TEMPLATE_PATH: The path of the template of full JDK table.
 
     For example:
         1. Check if IntelliJ is installed.
@@ -208,7 +203,7 @@
     _JDK_PATH = ''
     _IDE_JDK_TABLE_PATH = ''
     _JDK_PART_TEMPLATE_PATH = ''
-    _JDK_FULL_TEMPLATE_PATH = ''
+    _DEFAULT_ANDROID_SDK_PATH = ''
 
     def __init__(self, installed_path=None, config_reset=False):
         super().__init__(installed_path, config_reset)
@@ -230,7 +225,12 @@
             return
 
         for _config_path in _path_list:
-            self._set_jdk_config(_config_path)
+            jdk_file = os.path.join(_config_path, self._IDE_JDK_TABLE_PATH)
+            jdk_table = sdk_config.SDKConfig(
+                jdk_file, self._JDK_PART_TEMPLATE_PATH, self._JDK_PATH,
+                self._DEFAULT_ANDROID_SDK_PATH)
+            jdk_table.config_jdk_file()
+            jdk_table.gen_enable_debugger_module(self.project_abspath)
 
     def _get_config_root_paths(self):
         """Get the config root paths from derived class.
@@ -249,36 +249,6 @@
         """
         raise NotImplementedError('Method overriding is needed.')
 
-    def _set_jdk_config(self, path):
-        """Add jdk path to jdk.table.xml
-
-        Args:
-            path: The path of IntelliJ config path.
-        """
-        jdk_table_path = os.path.join(path, self._IDE_JDK_TABLE_PATH)
-        try:
-            if os.path.isfile(jdk_table_path):
-                with open(jdk_table_path, 'r+') as jdk_table_fd:
-                    jdk_table = jdk_table_fd.read()
-                    jdk_table_fd.seek(0)
-                    if _TARGET_JDK_NAME_TAG not in jdk_table:
-                        with open(self._JDK_PART_TEMPLATE_PATH) as template_fd:
-                            template = template_fd.read()
-                            template = template.replace(_JDK_PATH_TOKEN,
-                                                        self._JDK_PATH)
-                            jdk_table = jdk_table.replace(
-                                _COMPONENT_END_TAG, template)
-                            jdk_table_fd.truncate()
-                            jdk_table_fd.write(jdk_table)
-            else:
-                with open(self._JDK_FULL_TEMPLATE_PATH) as template_fd:
-                    template = template_fd.read()
-                    template = template.replace(_JDK_PATH_TOKEN, self._JDK_PATH)
-                    with open(jdk_table_path, 'w') as jdk_table_fd:
-                        jdk_table_fd.write(template)
-        except IOError as err:
-            logging.warning(err)
-
     def _get_preferred_version(self):
         """Get users' preferred IntelliJ version.
 
@@ -295,7 +265,7 @@
         uefiles = _get_intellij_version_path(self._ls_ue_path)
         all_versions = self._get_all_versions(cefiles, uefiles)
         if len(all_versions) > 1:
-            with AidegenConfig() as aconf:
+            with config.AidegenConfig() as aconf:
                 if not self._config_reset and (
                         aconf.preferred_version in all_versions):
                     return aconf.preferred_version
@@ -344,9 +314,9 @@
             None if the file is not found, otherwise a full path string of
             Intellij Android code style file.
         """
-        _config_source = os.path.join(constant.ANDROID_ROOT_PATH, 'development',
-                                      'ide', 'intellij', 'codestyles',
-                                      'AndroidStyle.xml')
+        _config_source = os.path.join(common_util.get_android_root_dir(),
+                                      'development', 'ide', 'intellij',
+                                      'codestyles', 'AndroidStyle.xml')
 
         return _config_source if os.path.isfile(_config_source) else None
 
@@ -360,14 +330,14 @@
         3. Config IntelliJ.
     """
 
-    _JDK_PATH = os.path.join(constant.ANDROID_ROOT_PATH,
+    _JDK_PATH = os.path.join(common_util.get_android_root_dir(),
                              'prebuilts/jdk/jdk8/linux-x86')
     # TODO(b/127899277): Preserve a config for jdk version option case.
     _IDE_JDK_TABLE_PATH = 'config/options/jdk.table.xml'
     _JDK_PART_TEMPLATE_PATH = os.path.join(
-        constant.AIDEGEN_ROOT_PATH, 'templates/jdkTable/part.jdk.table.xml')
-    _JDK_FULL_TEMPLATE_PATH = os.path.join(constant.AIDEGEN_ROOT_PATH,
-                                           'templates/jdkTable/jdk.table.xml')
+        common_util.get_aidegen_root_dir(),
+        'templates/jdkTable/part.jdk.table.xml')
+    _DEFAULT_ANDROID_SDK_PATH = os.path.join(os.getenv('HOME'), 'Android/Sdk')
 
     def __init__(self, installed_path=None, config_reset=False):
         super().__init__(installed_path, config_reset)
@@ -433,13 +403,14 @@
         3. Config IntelliJ.
     """
 
-    _JDK_PATH = os.path.join(constant.ANDROID_ROOT_PATH,
+    _JDK_PATH = os.path.join(common_util.get_android_root_dir(),
                              'prebuilts/jdk/jdk8/darwin-x86')
     _IDE_JDK_TABLE_PATH = 'options/jdk.table.xml'
     _JDK_PART_TEMPLATE_PATH = os.path.join(
-        constant.AIDEGEN_ROOT_PATH, 'templates/jdkTable/part.mac.jdk.table.xml')
-    _JDK_FULL_TEMPLATE_PATH = os.path.join(
-        constant.AIDEGEN_ROOT_PATH, 'templates/jdkTable/mac.jdk.table.xml')
+        common_util.get_aidegen_root_dir(),
+        'templates/jdkTable/part.mac.jdk.table.xml')
+    _DEFAULT_ANDROID_SDK_PATH = os.path.join(
+        os.getenv('HOME'), 'Library/Android/sdk')
 
     def __init__(self, installed_path=None, config_reset=False):
         super().__init__(installed_path, config_reset)
@@ -600,20 +571,16 @@
         self._bin_paths = self._get_possible_bin_paths()
         self._init_installed_path(installed_path)
 
-    def _get_ide_cmd(self, project_file):
+    def _get_ide_cmd(self):
         """Compose launch IDE command to run a new process and redirect output.
 
-        Args:
-            project_file: The full path of the IDE's project file.
-
         Returns:
             A string of launch IDE command.
         """
         return ' '.join([
-            _NOHUP,
-            'open',
+            _NOHUP, 'open',
             self._installed_path.replace(' ', r'\ '),
-            os.path.dirname(project_file), _IGNORE_STD_OUT_ERR_CMD, '&'
+            os.path.dirname(self.project_abspath), _IGNORE_STD_OUT_ERR_CMD, '&'
         ])
 
 
@@ -688,10 +655,7 @@
     # In command usage, the space ' ' should be '\ ' for correctness.
     return ' '.join([
         _NOHUP,
-        sh_path.replace(' ', r'\ '),
-        project_file,
-        _IGNORE_STD_OUT_ERR_CMD,
-        '&'
+        sh_path.replace(' ', r'\ '), project_file, _IGNORE_STD_OUT_ERR_CMD, '&'
     ])
 
 
diff --git a/aidegen/lib/ide_util_unittest.py b/aidegen/lib/ide_util_unittest.py
index ee79a5e..9e2d518 100644
--- a/aidegen/lib/ide_util_unittest.py
+++ b/aidegen/lib/ide_util_unittest.py
@@ -18,14 +18,17 @@
 """Unittests for ide_util."""
 
 import os
+import shutil
+import tempfile
 import unittest
+
 from unittest import mock
 from unittest.mock import patch
-
 from subprocess import CalledProcessError as cmd_err
 
 from aidegen.lib.android_dev_os import AndroidDevOS
 from aidegen.lib import ide_util
+from aidegen.lib import sdk_config
 from aidegen.lib.ide_util import IdeBase
 from aidegen.lib.ide_util import IdeIntelliJ
 from aidegen.lib.ide_util import IdeLinuxEclipse
@@ -48,6 +51,7 @@
     _TEST_PRJ_PATH2 = ''
     _TEST_PRJ_PATH3 = ''
     _TEST_PRJ_PATH4 = ''
+    _MODULE_XML_SAMPLE = ''
 
 
     def setUp(self):
@@ -59,6 +63,8 @@
         IdeUtilUnittests._TEST_PRJ_PATH3 = uc.TEST_DATA_PATH
         IdeUtilUnittests._TEST_PRJ_PATH4 = os.path.join(uc.TEST_DATA_PATH,
                                                         '.idea')
+        IdeUtilUnittests._MODULE_XML_SAMPLE = os.path.join(uc.TEST_DATA_PATH,
+                                                           'modules.xml')
 
 
     def tearDown(self):
@@ -108,7 +114,8 @@
         sh_path = IdeLinuxIntelliJ()._get_script_from_system()
         if sh_path:
             ide_util_obj = IdeUtil()
-            ide_util_obj.launch_ide(IdeUtilUnittests._TEST_PRJ_PATH1)
+            ide_util_obj.config_ide(IdeUtilUnittests._TEST_PRJ_PATH1)
+            ide_util_obj.launch_ide()
         else:
             self.assertRaises(cmd_err)
 
@@ -145,14 +152,27 @@
         IdeMacIntelliJ('some_path')
         self.assertTrue(mock_input.called)
 
+    @mock.patch.object(sdk_config.SDKConfig, '_android_sdk_exists')
+    @mock.patch.object(sdk_config.SDKConfig, '_target_jdk_exists')
     @mock.patch.object(IdeIntelliJ, '_get_config_root_paths')
     @mock.patch.object(IdeBase, 'apply_optional_config')
-    def test_config_ide(self, mock_config, mock_paths):
+    def test_config_ide(self, mock_config, mock_paths, mock_jdk, mock_sdk):
         """Test IDEA, IdeUtil.config_ide won't call base none implement api."""
-        util_obj = IdeUtil()
-        util_obj.config_ide()
-        self.assertFalse(mock_config.called)
-        self.assertFalse(mock_paths.called)
+        # Mock SDkConfig flow to not to generate real jdk config file.
+        mock_jdk.return_value = True
+        mock_sdk.return_value = True
+        test_path = os.path.join(tempfile.mkdtemp())
+        module_path = os.path.join(test_path, 'test')
+        idea_path = os.path.join(module_path, '.idea')
+        os.makedirs(idea_path)
+        shutil.copy(IdeUtilUnittests._MODULE_XML_SAMPLE, idea_path)
+        try:
+            util_obj = IdeUtil()
+            util_obj.config_ide(module_path)
+            self.assertFalse(mock_config.called)
+            self.assertFalse(mock_paths.called)
+        finally:
+            shutil.rmtree(test_path)
 
     @patch.object(ide_util, '_get_script_from_input_path')
     @patch.object(ide_util, '_get_script_from_internal_path')
diff --git a/aidegen/lib/metrics.py b/aidegen/lib/metrics.py
deleted file mode 100644
index a03db43..0000000
--- a/aidegen/lib/metrics.py
+++ /dev/null
@@ -1,147 +0,0 @@
-#!/usr/bin/env python3
-# Copyright 2018 - 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.
-
-"""AIDEgen metrics functions."""
-
-import json
-import logging
-import os
-import platform
-import subprocess
-import sys
-import urllib.request
-import uuid
-
-from aidegen import constant
-from atest import atest_utils
-from metrics import metrics
-from metrics import metrics_base
-from metrics import metrics_utils
-
-_METRICS_URL = 'http://asuite-218222.appspot.com/aidegen/metrics'
-_VALID_DOMAINS = ['google.com', 'android.com']
-_COMMAND_GIT_CONFIG = ['git', 'config', '--get', 'user.email']
-_JSON_HEADERS = {'Content-Type': 'application/json'}
-_METRICS_RESPONSE = b'done'
-_DUMMY_UUID = '00000000-0000-4000-8000-000000000000'
-_METRICS_TIMEOUT = 2  #seconds
-_META_FILE = os.path.join(
-    os.path.expanduser('~'), '.config', 'asuite', '.metadata')
-
-
-def log_usage():
-    """Log aidegen run."""
-    # Show privacy and license hint message before collect data.
-    atest_utils.print_data_collection_notice()
-    _log_event(_METRICS_URL, dummy_key_fallback=False, ldap=_get_ldap())
-
-
-def starts_asuite_metrics():
-    """Starts to record metrics data.
-
-    Send a metrics data to log server at the same time.
-    """
-    metrics_base.MetricsBase.tool_name = constant.AIDEGEN_TOOL_NAME
-    metrics_utils.get_start_time()
-    command = ' '.join(sys.argv)
-    metrics.AtestStartEvent(
-        command_line=command,
-        test_references=[],
-        cwd=os.getcwd(),
-        os=platform.platform())
-
-
-def ends_asuite_metrics(exit_code, stacktrace='', logs=''):
-    """Send the end event to log server.
-
-    Args:
-        exit_code: An integer of exit code.
-        stacktrace: A string of stacktrace.
-        logs: A string of logs.
-    """
-    metrics_utils.send_exit_event(
-        exit_code,
-        stacktrace=stacktrace,
-        logs=logs)
-
-
-# pylint: disable=broad-except
-def _get_ldap():
-    """Return string email username for valid domains only, None otherwise."""
-    if not atest_utils.is_external_run():
-        aidegen_project = os.path.join(constant.ANDROID_ROOT_PATH, 'tools',
-                                       'asuite', 'aidegen')
-        email = subprocess.check_output(
-            _COMMAND_GIT_CONFIG, cwd=aidegen_project).strip()
-        ldap, domain = str(email, encoding="utf-8").split('@', 2)
-        if domain in _VALID_DOMAINS:
-            return ldap
-    return None
-
-
-# pylint: disable=broad-except
-def _log_event(metrics_url, dummy_key_fallback=True, **kwargs):
-    """Base log event function for asuite backend.
-
-    Args:
-        metrics_url: String, URL to report metrics to.
-        dummy_key_fallback: Boolean, If True and unable to get grouping key,
-                            use a dummy key otherwise return out. Sometimes we
-                            don't want to return metrics for users we are
-                            unable to identify. Default True.
-        kwargs: Dict, additional fields we want to return metrics for.
-    """
-    try:
-        try:
-            key = str(_get_grouping_key())
-        except Exception:
-            if not dummy_key_fallback:
-                return
-            key = _DUMMY_UUID
-        data = {'grouping_key': key, 'run_id': str(uuid.uuid4())}
-        if kwargs:
-            data.update(kwargs)
-        data = json.dumps(data).encode("utf-8")
-        request = urllib.request.Request(
-            metrics_url, data=data, headers=_JSON_HEADERS)
-        response = urllib.request.urlopen(request, timeout=_METRICS_TIMEOUT)
-        content = response.read()
-        if content != _METRICS_RESPONSE:
-            raise Exception('Unexpected metrics response: %s' % content)
-    except Exception:
-        logging.exception('Exception sending metrics')
-
-
-def _get_grouping_key():
-    """Get grouping key. Returns UUID.uuid4."""
-    if os.path.isfile(_META_FILE):
-        with open(_META_FILE) as meta_file:
-            try:
-                return uuid.UUID(meta_file.read(), version=4)
-            except ValueError:
-                logging.exception('malformed group_key in file, rewriting')
-
-    key = uuid.uuid4()
-    dir_path = os.path.dirname(_META_FILE)
-    if os.path.isfile(dir_path):
-        os.remove(dir_path)
-    try:
-        os.makedirs(dir_path)
-    except OSError as err:
-        if not os.path.isdir(dir_path):
-            raise err
-    with open(_META_FILE, 'w+') as meta_file:
-        meta_file.write(str(key))
-    return key
diff --git a/aidegen/lib/metrics_unittest.py b/aidegen/lib/metrics_unittest.py
deleted file mode 100644
index 5ae4790..0000000
--- a/aidegen/lib/metrics_unittest.py
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright 2019, 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.
-
-"""Unittests for metrics."""
-
-from __future__ import print_function
-
-import unittest
-from unittest import mock
-
-from aidegen.lib import metrics
-from atest import atest_utils
-
-
-class MetricsUnittests(unittest.TestCase):
-
-    """Unit tests for metrics.py"""
-    @mock.patch.object(atest_utils, 'is_external_run')
-    @mock.patch.object(atest_utils, 'print_data_collection_notice')
-    def test_log_usage(self, mock_notice, mock_external_check):
-        """Test log_usage always run through the target test function."""
-        metrics.log_usage()
-        self.assertTrue(mock_notice.called)
-        self.assertTrue(mock_external_check.called)
-
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/aidegen/lib/module_info.py b/aidegen/lib/module_info.py
new file mode 100644
index 0000000..2676231
--- /dev/null
+++ b/aidegen/lib/module_info.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019, 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.
+
+"""Module Info class used to hold cached merged_module_info.json.json."""
+
+import json
+import logging
+import os
+
+from aidegen import constant
+from aidegen.lib import common_util
+from aidegen.lib import module_info_util
+from atest.module_info import ModuleInfo
+
+
+class AidegenModuleInfo(ModuleInfo):
+    """Class that offers fast/easy lookup for Module related details.
+
+    Class attributes:
+        mod_info: A ModuleInfo instance contains data of the merged json file
+                  after initialization.
+        projects: A list of project names.
+        verbose: A boolean, if true displays full build output.
+        skip_build: A boolean, if true skip building
+                    constant.BLUEPRINT_JSONFILE_NAME if it exists, otherwise
+                    build it.
+    """
+    mod_info = None
+    projects = []
+    verbose = False
+    skip_build = False
+
+    # pylint: disable=too-many-arguments
+    def __init__(self,
+                 force_build=False,
+                 module_file=None,
+                 atest_module_info=None,
+                 projects=None,
+                 verbose=False,
+                 skip_build=False):
+        """Initialize the AidegenModuleInfo object.
+
+        Load up the module-info.json file and initialize the helper vars.
+
+        Args:
+            force_build: Boolean to indicate if we should rebuild the
+                         module_info file regardless if it's created or not.
+                         The default value is False: don't force build.
+            module_file: String of path to file to load up. Used for testing.
+                         The default value is None: don't specify the path.
+            atest_module_info: A ModuleInfo instance contains data of
+                               module-info.json. The default value is None,
+                               module_info_util can get it from
+                               common_util.get_atest_module_info function.
+            projects: A list of project names. The default value is None,
+                      module_info_util won't show reuse iml project file
+                      message.
+            verbose: A boolean, if true displays full build output. The default
+                     value is False.
+            skip_build: A boolean, if true skip building
+                        constant.BLUEPRINT_JSONFILE_NAME if it exists, otherwise
+                        build it. The default value is False.
+        """
+        AidegenModuleInfo.mod_info = atest_module_info
+        AidegenModuleInfo.projects = projects
+        AidegenModuleInfo.verbose = verbose
+        AidegenModuleInfo.skip_build = skip_build
+        super().__init__(force_build, module_file)
+
+    @staticmethod
+    def _discover_mod_file_and_target(force_build):
+        """Find the module file.
+
+        If force_build is True, we'll remove module_bp_java_deps.json first and
+        let module_info_util.generate_merged_module_info regenerate it again.
+
+        Args:
+            force_build: Boolean to indicate if we should rebuild the
+                         module_info file regardless if it's created or not.
+
+        Returns:
+            Tuple of the relative and absolute paths of the merged module info
+            file.
+        """
+        module_file_path = common_util.get_blueprint_json_path()
+        if force_build and os.path.isfile(module_file_path):
+            os.remove(module_file_path)
+        merged_file_path = os.path.join(common_util.get_soong_out_path(),
+                                        constant.MERGED_MODULE_INFO)
+        if not os.path.isfile(module_file_path):
+            logging.debug(
+                'Generating %s - this is required for the initial runs.',
+                merged_file_path)
+        if not AidegenModuleInfo.mod_info:
+            AidegenModuleInfo.mod_info = common_util.get_atest_module_info()
+        data = module_info_util.generate_merged_module_info(
+            AidegenModuleInfo.mod_info, AidegenModuleInfo.projects,
+            AidegenModuleInfo.verbose, AidegenModuleInfo.skip_build)
+        with open(merged_file_path, 'w') as json_file:
+            json.dump(data, json_file, indent=4)
+        module_file_rel_path = os.path.relpath(
+            merged_file_path, common_util.get_android_root_dir())
+        return module_file_rel_path, merged_file_path
diff --git a/aidegen/lib/module_info_util.py b/aidegen/lib/module_info_util.py
index 322f3ec..1d14fc9 100644
--- a/aidegen/lib/module_info_util.py
+++ b/aidegen/lib/module_info_util.py
@@ -22,7 +22,7 @@
 merge them into one dictionary and return the merged dictionary to its caller.
 
 Example usage:
-merged_dict = generate_module_info_json(atest_module_info, project, verbose)
+merged_dict = generate_merged_module_info(atest_module_info, project, verbose)
 """
 
 import glob
@@ -33,59 +33,62 @@
 import sys
 
 from aidegen import constant
-from aidegen.lib.common_util import COLORED_INFO
-from aidegen.lib.common_util import time_logged
-from aidegen.lib.common_util import get_related_paths
+from aidegen.lib import common_util
 from aidegen.lib import errors
 
 _BLUEPRINT_JSONFILE_NAME = 'module_bp_java_deps.json'
-_KEY_CLS = 'class'
-_KEY_PATH = 'path'
-_KEY_INS = 'installed'
-_KEY_DEP = 'dependencies'
-_KEY_SRCS = 'srcs'
-_MERGE_NEEDED_ITEMS = [_KEY_CLS, _KEY_PATH, _KEY_INS, _KEY_DEP, _KEY_SRCS]
+_MERGE_NEEDED_ITEMS = [
+    constant.KEY_CLASS,
+    constant.KEY_PATH,
+    constant.KEY_INSTALLED,
+    constant.KEY_DEPENDENCIES,
+    constant.KEY_SRCS,
+    constant.KEY_SRCJARS,
+    constant.KEY_CLASSES_JAR
+]
 _INTELLIJ_PROJECT_FILE_EXT = '*.iml'
 _LAUNCH_PROJECT_QUERY = (
     'There exists an IntelliJ project file: %s. Do you want '
     'to launch it (yes/No)?')
-_GENERATE_JSON_COMMAND = ('SOONG_COLLECT_JAVA_DEPS=false make nothing;'
-                          'SOONG_COLLECT_JAVA_DEPS=true make nothing')
+_GENERATE_JSON_COMMAND = ('SOONG_COLLECT_JAVA_DEPS=false make nothing -C {DIR};'
+                          'SOONG_COLLECT_JAVA_DEPS=true make nothing -C {DIR}')
 
 
-@time_logged
-def generate_module_info_json(module_info, projects, verbose, skip_build=False):
-    """Generate a merged json dictionary.
+@common_util.time_logged
+def generate_merged_module_info(module_info, projects=None, verbose=False,
+                                skip_build=False):
+    """Generate a merged dictionary.
 
-    Change directory to ANDROID_ROOT_PATH before making _GENERATE_JSON_COMMAND
+    Change directory to Android root path before making _GENERATE_JSON_COMMAND
     to avoid command error: "make: *** No rule to make target 'nothing'.  Stop."
     and change back to current directory after command completed.
 
     Linked functions:
         _build_target(project, verbose)
         _get_soong_build_json_dict()
-        _merge_json(mk_dict, bp_dict)
+        _merge_dict(mk_dict, bp_dict)
 
     Args:
         module_info: A ModuleInfo instance contains data of module-info.json.
         projects: A list of project names.
         verbose: A boolean, if true displays full build output.
-        skip_build: A boolean, if true skip building _BLUEPRINT_JSONFILE_NAME if
-                    it exists, otherwise build it.
+        skip_build: A boolean, if true skip building
+                    get_blueprint_json_path() if it exists, otherwise
+                    build it.
 
     Returns:
-        A tuple of Atest module info instance and a merged json dictionary.
+        A merged dictionary from module-info.json and module_bp_java_deps.json.
     """
-    cwd = os.getcwd()
-    os.chdir(constant.ANDROID_ROOT_PATH)
-    _build_target([_GENERATE_JSON_COMMAND], projects[0], module_info, verbose,
-                  skip_build)
-    os.chdir(cwd)
+    main_project = projects[0] if projects else None
+    cmd = [_GENERATE_JSON_COMMAND.format(
+        DIR=common_util.get_android_root_dir())]
+    _build_target(module_info, cmd, main_project, verbose, skip_build)
     bp_dict = _get_soong_build_json_dict()
-    return _merge_json(module_info.name_to_module_info, bp_dict)
+    return _merge_dict(module_info.name_to_module_info, bp_dict)
 
 
-def _build_target(cmd, main_project, module_info, verbose, skip_build=False):
+def _build_target(module_info, cmd, main_project=None, verbose=False,
+                  skip_build=False):
     """Make nothing to generate module_bp_java_deps.json.
 
     We build without environment setting SOONG_COLLECT_JAVA_DEPS and then build
@@ -94,12 +97,13 @@
     module_bp_java_deps.json.
 
     Args:
+        module_info: A ModuleInfo instance contains data of module-info.json.
         cmd: A string list, build command.
         main_project: The main project name.
-        module_info: A ModuleInfo instance contains data of module-info.json.
         verbose: A boolean, if true displays full build output.
-        skip_build: A boolean, if true skip building _BLUEPRINT_JSONFILE_NAME if
-                    it exists, otherwise build it.
+        skip_build: A boolean, if true, skip building if
+                    get_blueprint_json_path() file exists, otherwise
+                    build it.
 
     Build results:
         1. Build successfully return.
@@ -110,12 +114,12 @@
               a) If the answer is yes, return.
               b) If the answer is not yes, sys.exit(1)
     """
-    json_path = _get_blueprint_json_path()
+    json_path = common_util.get_blueprint_json_path()
     original_json_mtime = None
     if os.path.isfile(json_path):
         if skip_build:
             logging.info('%s file exists, skipping build.',
-                         _BLUEPRINT_JSONFILE_NAME)
+                         common_util.get_blueprint_json_path())
             return
         original_json_mtime = os.path.getmtime(json_path)
     try:
@@ -131,10 +135,13 @@
             if os.path.isfile(json_path):
                 message = ('Generate new {0} failed, AIDEGen will proceed and '
                            'reuse the old {0}.'.format(json_path))
-                print('\n{} {}\n'.format(COLORED_INFO('Warning:'), message))
+                print('\n{} {}\n'.format(common_util.COLORED_INFO('Warning:'),
+                                         message))
         else:
-            _, main_project_path = get_related_paths(module_info, main_project)
-            _build_failed_handle(main_project_path)
+            if main_project:
+                _, main_project_path = common_util.get_related_paths(
+                    module_info, main_project)
+                _build_failed_handle(main_project_path)
 
 
 def _is_new_json_file_generated(json_path, original_file_mtime):
@@ -143,6 +150,9 @@
     Args:
         json_path: The path of the json file being to check.
         original_file_mtime: the original file modified time.
+
+    Returns:
+        A boolean, True if the json_path file is new generated, otherwise False.
     """
     if not original_file_mtime:
         return os.path.isfile(json_path)
@@ -171,16 +181,16 @@
             sys.exit(1)
     else:
         raise errors.BuildFailureError(
-            'Failed to generate %s.' % _get_blueprint_json_path())
+            'Failed to generate %s.' % common_util.get_blueprint_json_path())
 
 
 def _get_soong_build_json_dict():
     """Load a json file from path and convert it into a json dictionary.
 
     Returns:
-        A json dictionary.
+        A dictionary loaded from the blueprint json file.
     """
-    json_path = _get_blueprint_json_path()
+    json_path = common_util.get_blueprint_json_path()
     try:
         with open(json_path) as jfile:
             json_dict = json.load(jfile)
@@ -190,17 +200,10 @@
             '%s does not exist, error: %s.' % (json_path, err))
 
 
-def _get_blueprint_json_path():
-    """Assemble the path of blueprint json file.
-
-    Returns:
-        Blueprint json path.
-    """
-    return os.path.join(constant.SOONG_OUT_DIR_PATH, _BLUEPRINT_JSONFILE_NAME)
-
-
 def _merge_module_keys(m_dict, b_dict):
-    """Merge a module's json dictionary into another module's json dictionary.
+    """Merge a module's dictionary into another module's dictionary.
+
+    Merge b_dict module data into m_dict.
 
     Args:
         m_dict: The module dictionary is going to merge b_dict into.
@@ -211,13 +214,13 @@
 
 
 def _copy_needed_items_from(mk_dict):
-    """Shallow copy needed items from Make build system part json dictionary.
+    """Shallow copy needed items from Make build system module info dictionary.
 
     Args:
-        mk_dict: Make build system json dictionary is going to be copyed.
+        mk_dict: Make build system dictionary is going to be copied.
 
     Returns:
-        A merged json dictionary.
+        A merged dictionary.
     """
     merged_dict = dict()
     for module in mk_dict.keys():
@@ -228,18 +231,18 @@
     return merged_dict
 
 
-def _merge_json(mk_dict, bp_dict):
-    """Merge two json dictionaries.
+def _merge_dict(mk_dict, bp_dict):
+    """Merge two dictionaries.
 
     Linked function:
         _merge_module_keys(m_dict, b_dict)
 
     Args:
-        mk_dict: Make build system part json dictionary.
-        bp_dict: Soong build system part json dictionary.
+        mk_dict: Make build system module info dictionary.
+        bp_dict: Soong build system module info dictionary.
 
     Returns:
-        A merged json dictionary.
+        A merged dictionary.
     """
     merged_dict = _copy_needed_items_from(mk_dict)
     for module in bp_dict.keys():
diff --git a/aidegen/lib/module_info_util_unittest.py b/aidegen/lib/module_info_util_unittest.py
index 0cf7f6b..290e8bc 100644
--- a/aidegen/lib/module_info_util_unittest.py
+++ b/aidegen/lib/module_info_util_unittest.py
@@ -24,7 +24,6 @@
 
 import aidegen.unittest_constants as uc
 from aidegen.lib import errors
-from aidegen.lib import metrics
 from aidegen.lib import module_info_util
 from atest import module_info
 
@@ -136,7 +135,7 @@
         mock_copy.return_value = ''
         amodule_info = module_info.ModuleInfo()
         cmd = [module_info_util._GENERATE_JSON_COMMAND]
-        module_info_util._build_target(cmd, uc.TEST_MODULE, amodule_info, True)
+        module_info_util._build_target(amodule_info, cmd, uc.TEST_MODULE, True)
         self.assertTrue(mock_copy.called)
         self.assertTrue(mock_check_call.called)
         mock_check_call.assert_called_with(
@@ -144,7 +143,7 @@
             stderr=subprocess.STDOUT,
             env=mock_copy.return_value,
             shell=True)
-        module_info_util._build_target(cmd, uc.TEST_MODULE, amodule_info, False)
+        module_info_util._build_target(amodule_info, cmd, uc.TEST_MODULE, False)
         self.assertTrue(mock_check_call.called)
         mock_check_call.assert_called_with(cmd, shell=True)
 
@@ -173,10 +172,9 @@
             module_info_util._is_new_json_file_generated(
                 jfile, original_file_mtime))
 
-    @mock.patch.object(metrics, 'ends_asuite_metrics')
     @mock.patch('builtins.input')
     @mock.patch('glob.glob')
-    def test_build_failed_handle(self, mock_glob, mock_input, _send_exit):
+    def test_build_failed_handle(self, mock_glob, mock_input):
         """Test _build_failed_handle with different situations."""
         mock_glob.return_value = ['project/file.iml']
         mock_input.return_value = 'N'
@@ -203,22 +201,22 @@
         main_project = ''
         amodule_info = {}
         verbose = False
-        module_info_util._build_target(cmd, main_project, amodule_info, verbose)
+        module_info_util._build_target(amodule_info, cmd, main_project, verbose)
         mock_call.assert_called_with(cmd, shell=True)
         verbose = True
         full_env_vars = os.environ.copy()
-        module_info_util._build_target(cmd, main_project, amodule_info, verbose)
+        module_info_util._build_target(amodule_info, cmd, main_project, verbose)
         mock_call.assert_called_with(cmd, stderr=subprocess.STDOUT,
                                      env=full_env_vars, shell=True)
         mock_call.side_effect = subprocess.CalledProcessError(1, '')
         mock_new.return_value = False
-        module_info_util._build_target(cmd, main_project, amodule_info, verbose)
+        module_info_util._build_target(amodule_info, cmd, main_project, verbose)
         self.assertTrue(mock_new.called)
         self.assertFalse(mock_handle.called)
         mock_new.return_value = True
-        module_info_util._build_target(cmd, main_project, amodule_info, verbose)
+        module_info_util._build_target(amodule_info, cmd, main_project, verbose)
         self.assertTrue(mock_new.called)
-        self.assertTrue(mock_handle.called)
+        self.assertFalse(mock_handle.called)
 
 
 if __name__ == '__main__':
diff --git a/aidegen/lib/project_config.py b/aidegen/lib/project_config.py
new file mode 100644
index 0000000..e81bb36
--- /dev/null
+++ b/aidegen/lib/project_config.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - 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.
+
+"""Project config class."""
+
+from aidegen import constant
+from aidegen.lib.common_util import COLORED_INFO
+
+SKIP_BUILD_INFO = ('If you are sure the related modules and dependencies have '
+                   'been already built, please try to use command {} to skip '
+                   'the building process.')
+_SKIP_BUILD_CMD = 'aidegen {} -s'
+_SKIP_BUILD_WARN = (
+    'You choose "--skip-build". Skip building jar and module might increase '
+    'the risk of the absence of some jar or R/AIDL/logtags java files and '
+    'cause the red lines to appear in IDE tool.')
+
+
+class ProjectConfig:
+    """Class manages AIDEGen's configurations.
+
+    Attributes:
+        ide_name: The IDE name which user prefer to launch.
+        is_launch_ide: A boolean for launching IDE in the end of AIDEGen.
+        depth: The depth of module referenced by source.
+        full_repo: A boolean decides import whole Android source repo.
+        is_skip_build: A boolean decides skipping building jars or modules.
+        targets: A string list with Android module names or paths.
+    """
+
+    def __init__(self, args):
+        self.ide_name = constant.IDE_NAME_DICT[args.ide[0]]
+        self.is_launch_ide = not args.no_launch
+        self.depth = args.depth
+        self.full_repo = args.android_tree
+        self.is_skip_build = args.skip_build
+        self.targets = args.targets
+        self._show_skip_build_msg()
+
+    def _show_skip_build_msg(self):
+        """Display different messages if users skip building targets or not."""
+        if self.is_skip_build:
+            print('\n{} {}\n'.format(
+                COLORED_INFO('Warning:'), _SKIP_BUILD_WARN))
+        else:
+            msg = SKIP_BUILD_INFO.format(
+                COLORED_INFO(
+                    _SKIP_BUILD_CMD.format(' '.join(self.targets))))
+            print('\n{} {}\n'.format(COLORED_INFO('INFO:'), msg))
diff --git a/aidegen/lib/project_file_gen.py b/aidegen/lib/project_file_gen.py
index 38b3d41..2ca810f 100644
--- a/aidegen/lib/project_file_gen.py
+++ b/aidegen/lib/project_file_gen.py
@@ -16,11 +16,9 @@
 
 """It is an AIDEGen sub task : generate the project files.
 
-This module generate IDE project files from templates.
-
-    Typical usage example:
-
-    generate_ide_project_file(project_info)
+    Usage example:
+    projects: A list of ProjectInfo instances.
+    ProjectFileGenerator.generate_ide_project_file(projects)
 """
 
 import logging
@@ -38,8 +36,12 @@
     </facet>'''
 _SOURCE_FOLDER = ('            <sourceFolder url='
                   '"file://%s" isTestSource="%s" />\n')
+_EXCLUDE_ITEM = '            <excludeFolder url="file://%s" />\n'
 _CONTENT_URL = '        <content url="file://%s">\n'
 _END_CONTENT = '        </content>\n'
+_SRCJAR_URL = ('%s<content url="jar://{SRCJAR}">\n'
+               '%s<sourceFolder url="jar://{SRCJAR}" isTestSource="False" />\n'
+               '%s</content>\n') % (' ' * 8, ' ' * 12, ' ' * 8)
 _ORDER_ENTRY = ('        <orderEntry type="module-library" exported="">'
                 '<library><CLASSES><root url="jar://%s!/" /></CLASSES>'
                 '<JAVADOC /><SOURCES /></library></orderEntry>\n')
@@ -47,24 +49,23 @@
                        'module-name="%s" />')
 _MODULE_SECTION = ('            <module fileurl="file:///$PROJECT_DIR$/%s.iml"'
                    ' filepath="$PROJECT_DIR$/%s.iml" />')
-_SUB_MODULES_SECTION = ('            <module fileurl="file:///%s" '
-                        'filepath="%s" />')
+_SUB_MODULES_SECTION = ('            <module fileurl="file:///{IML}" '
+                        'filepath="{IML}" />')
 _VCS_SECTION = '        <mapping directory="%s" vcs="Git" />'
 _FACET_TOKEN = '@FACETS@'
 _SOURCE_TOKEN = '@SOURCES@'
+_SRCJAR_TOKEN = '@SRCJAR@'
 _MODULE_DEP_TOKEN = '@MODULE_DEPENDENCIES@'
 _MODULE_TOKEN = '@MODULES@'
+_ENABLE_DEBUGGER_MODULE_TOKEN = '@ENABLE_DEBUGGER_MODULE@'
 _VCS_TOKEN = '@VCS@'
 _JAVA_FILE_PATTERN = '%s/*.java'
-_ROOT_DIR = constant.AIDEGEN_ROOT_PATH
-_IDEA_DIR = os.path.join(_ROOT_DIR, 'templates/idea')
-_TEMPLATE_IML_PATH = os.path.join(_ROOT_DIR, 'templates/module-template.iml')
+_IDEA_DIR = os.path.join(common_util.get_aidegen_root_dir(), 'templates/idea')
 _IDEA_FOLDER = '.idea'
 _MODULES_XML = 'modules.xml'
 _VCS_XML = 'vcs.xml'
 _TEMPLATE_MODULES_PATH = os.path.join(_IDEA_DIR, _MODULES_XML)
 _TEMPLATE_VCS_PATH = os.path.join(_IDEA_DIR, _VCS_XML)
-_DEPENDENCIES = 'dependencies'
 _DEPENDENCIES_IML = 'dependencies.iml'
 _COPYRIGHT_FOLDER = 'copyright'
 _CODE_STYLE_FOLDER = 'codeStyles'
@@ -74,334 +75,495 @@
 _IML_EXTENSION = '.iml'
 _FRAMEWORK_JAR = os.sep + 'framework.jar'
 _HIGH_PRIORITY_JARS = [_FRAMEWORK_JAR]
+_EXCLUDE_FOLDERS = ['.idea', '.repo', 'art', 'bionic', 'bootable', 'build',
+                    'dalvik', 'developers', 'device', 'hardware', 'kernel',
+                    'libnativehelper', 'pdk', 'prebuilts', 'sdk', 'system',
+                    'toolchain', 'tools', 'vendor', 'out']
 _GIT_FOLDER_NAME = '.git'
 # Support gitignore by symbolic link to aidegen/data/gitignore_template.
 _GITIGNORE_FILE_NAME = '.gitignore'
 _GITIGNORE_REL_PATH = 'tools/asuite/aidegen/data/gitignore_template'
-_GITIGNORE_ABS_PATH = os.path.join(constant.ANDROID_ROOT_PATH,
+_GITIGNORE_ABS_PATH = os.path.join(common_util.get_android_root_dir(),
                                    _GITIGNORE_REL_PATH)
 # Support code style by symbolic link to aidegen/data/AndroidStyle_aidegen.xml.
 _CODE_STYLE_REL_PATH = 'tools/asuite/aidegen/data/AndroidStyle_aidegen.xml'
-_CODE_STYLE_SRC_PATH = os.path.join(constant.ANDROID_ROOT_PATH,
+_CODE_STYLE_SRC_PATH = os.path.join(common_util.get_android_root_dir(),
                                     _CODE_STYLE_REL_PATH)
 
-_ECLIP_SRC_ENTRY = '<classpathentry exported="true" kind="src" path="{}"/>\n'
-_ECLIP_LIB_ENTRY = '<classpathentry exported="true" kind="lib" path="{}"/>\n'
-_ECLIP_TEMPLATE_PATH = os.path.join(_ROOT_DIR, 'templates/eclipse/eclipse.xml')
-_ECLIP_EXTENSION = '.classpath'
-_ECLIP_SRC_TOKEN = '@SRC@'
-_ECLIP_LIB_TOKEN = '@LIB@'
-_ECLIP_PROJECT_PATH = os.path.join(_ROOT_DIR, 'templates/eclipse/project.xml')
-_ECLIP_PROJECT_NAME_TOKEN = '@PROJECTNAME@'
-_ECLIP_PROJECT_EXTENSION = '.project'
 
-# b/121256503: Prevent duplicated iml names from breaking IDEA.
-# Use a map to cache in-using(already used) iml project file names.
-_USED_NAME_CACHE = dict()
+class ProjectFileGenerator():
+    """Project file generator.
 
+    Class attributes:
+        _USED_NAME_CACHE: A dict to cache already used iml project file names
+                          and prevent duplicated iml names from breaking IDEA.
 
-def get_unique_iml_name(abs_module_path):
-    """Create a unique iml name if needed.
-
-    If the iml name has been used already, prefix it with the
-    parent_sub_folder_name to form a new unique name, and store iml name in
-    _USED_NAME_CACHE as: { abs_module_path:unique_name }.
-
-    Args:
-        abs_module_path: Full module path string.
-
-    Return:
-        String: A unique iml name.
+    Attributes:
+        project_info: A instance of ProjectInfo.
     """
-    if abs_module_path in _USED_NAME_CACHE:
-        return _USED_NAME_CACHE[abs_module_path]
+    # b/121256503: Prevent duplicated iml names from breaking IDEA.
+    # Use a map to cache in-using(already used) iml project file names.
+    _USED_NAME_CACHE = dict()
 
-    uniq_name = abs_module_path.strip(os.sep).split(os.sep)[-1]
-    if any(uniq_name == name for name in _USED_NAME_CACHE.values()):
-        parent_path = os.path.relpath(abs_module_path,
-                                      constant.ANDROID_ROOT_PATH)
-        sub_folders = parent_path.split(os.sep)
-        zero_base_index = len(sub_folders) - 1
-        # Compose the name by following logic. Take ['cts', 'tests', 'ui'] as
-        # an example, if 'ui' is used, then try 'cts_ui', then try
-        # 'cts_tests_ui'. And the worst case is cts_tests_ui, which must be an
-        # unique one.
-        while zero_base_index > 0:
-            uniq_name = '_'.join(
-                [sub_folders[0], '_'.join(sub_folders[zero_base_index:])])
-            zero_base_index = zero_base_index - 1
-            if uniq_name not in _USED_NAME_CACHE.values():
-                break
-    _USED_NAME_CACHE[abs_module_path] = uniq_name
-    logging.debug('Unique name for module path of %s is %s.', abs_module_path,
-                  uniq_name)
-    return uniq_name
+    def __init__(self, project_info):
+        """ProjectFileGenerator initialize.
 
+        Args:
+            project_info: A instance of ProjectInfo.
+        """
+        self.project_info = project_info
 
-def _generate_intellij_project_file(project_info, iml_path_list=None):
-    """Generates IntelliJ project file.
+    @classmethod
+    def get_unique_iml_name(cls, abs_module_path):
+        """Create a unique iml name if needed.
 
-    Args:
-        project_info: ProjectInfo instance.
-        iml_path_list: An optional list of submodule's iml paths, default None.
-    """
-    source_dict = dict.fromkeys(
-        list(project_info.source_path['source_folder_path']), False)
-    source_dict.update(
-        dict.fromkeys(list(project_info.source_path['test_folder_path']), True))
-    project_info.iml_path, _ = _generate_iml(
-        constant.ANDROID_ROOT_PATH, project_info.project_absolute_path,
-        source_dict, list(project_info.source_path['jar_path']),
-        project_info.project_relative_path)
-    _generate_modules_xml(project_info.project_absolute_path, iml_path_list)
-    project_info.git_path = _generate_vcs_xml(
-        project_info.project_absolute_path)
-    _copy_constant_project_files(project_info.project_absolute_path)
+        If the name of last sub folder is used already, prefixing it with prior
+        sub folder names as a candidate name. If finally, it's unique, storing
+        in _USED_NAME_CACHE as: { abs_module_path:unique_name }. The cts case
+        and UX of IDE view are the main reasons why using module path strategy
+        but not name of module directly. Following is the detailed strategy:
+        1. While loop composes a sensible and shorter name, by checking unique
+           to finish the loop and finally add to cache.
+           Take ['cts', 'tests', 'app', 'ui'] an example, if 'ui' isn't
+           occupied, use it, else try 'cts_ui', then 'cts_app_ui', the worst
+           case is whole three candidate names are occupied already.
+        2. 'Else' for that while stands for no suitable name generated, so
+           trying 'cts_tests_app_ui' directly. If it's still non unique, e.g.,
+           module path cts/xxx/tests/app/ui occupied that name already,
+           appending increasing sequence number to get a unique name.
 
+        Args:
+            abs_module_path: The absolute module path string.
 
-def generate_ide_project_files(projects):
-    """Generate IDE project files by a list of ProjectInfo instances.
+        Return:
+            String: A unique iml name.
+        """
+        if abs_module_path in cls._USED_NAME_CACHE:
+            return cls._USED_NAME_CACHE[abs_module_path]
 
-    For multiple modules case, we call _generate_intellij_project_file to
-    generate iml file for submodules first and pass submodules' iml file paths
-    as an argument to function _generate_intellij_project_file when we generate
-    main module.iml file. In this way, we can add submodules' dependencies iml
-    and their own iml file paths to main module's module.xml.
+        uniq_name = abs_module_path.strip(os.sep).split(os.sep)[-1]
+        if any(uniq_name == name for name in cls._USED_NAME_CACHE.values()):
+            parent_path = os.path.relpath(abs_module_path,
+                                          common_util.get_android_root_dir())
+            sub_folders = parent_path.split(os.sep)
+            zero_base_index = len(sub_folders) - 1
+            # Start compose a sensible, shorter and unique name.
+            while zero_base_index > 0:
+                uniq_name = '_'.join(
+                    [sub_folders[0], '_'.join(sub_folders[zero_base_index:])])
+                zero_base_index = zero_base_index - 1
+                if uniq_name not in cls._USED_NAME_CACHE.values():
+                    break
+            else:
+                # b/133393638: To handle several corner cases.
+                uniq_name_base = parent_path.strip(os.sep).replace(os.sep, '_')
+                i = 0
+                uniq_name = uniq_name_base
+                while uniq_name in cls._USED_NAME_CACHE.values():
+                    i = i + 1
+                    uniq_name = '_'.join([uniq_name_base, str(i)])
+        cls._USED_NAME_CACHE[abs_module_path] = uniq_name
+        logging.debug('Unique name for module path of %s is %s.',
+                      abs_module_path, uniq_name)
+        return uniq_name
 
-    Args:
-        projects: A list of ProjectInfo instances.
-    """
-    # Initialization
-    _USED_NAME_CACHE.clear()
+    def _generate_source_section(self, sect_name, is_test):
+        """Generate specific section of the project file.
 
-    for project in projects[1:]:
-        _generate_intellij_project_file(project)
-    iml_paths = [project.iml_path for project in projects[1:]]
-    _generate_intellij_project_file(projects[0], iml_paths)
-    _merge_project_vcs_xmls(projects)
+        Args:
+            sect_name: The section name, e.g. source_folder_path is for source
+                       folder section.
+            is_test: A boolean, True if it's the test section else False.
 
+        Returns:
+            A dict contains the source folder's contents of project file.
+        """
+        return dict.fromkeys(
+            list(self.project_info.source_path[sect_name]), is_test)
 
-def _generate_eclipse_project_file(project_info):
-    """Generates Eclipse project file.
+    def generate_intellij_project_file(self, iml_path_list=None):
+        """Generates IntelliJ project file.
 
-    Args:
-        project_info: ProjectInfo instance.
-    """
-    module_path = project_info.project_absolute_path
-    module_name = get_unique_iml_name(module_path)
-    _generate_eclipse_project(module_name, module_path)
-    source_dict = dict.fromkeys(
-        list(project_info.source_path['source_folder_path']), False)
-    source_dict.update(
-        dict.fromkeys(list(project_info.source_path['test_folder_path']), True))
-    project_info.iml_path = _generate_classpath(
-        project_info.project_absolute_path, list(sorted(source_dict)),
-        list(project_info.source_path['jar_path']))
+        Args:
+            iml_path_list: An optional list of submodule's iml paths, the
+                           default value is None.
+        """
+        is_main_module = iml_path_list is not None
+        source_dict = self._generate_source_section('source_folder_path', False)
+        source_dict.update(
+            self._generate_source_section('test_folder_path', True))
+        self.project_info.iml_path, _ = self._generate_iml(source_dict,
+                                                           is_main_module)
+        self._generate_modules_xml(iml_path_list)
+        self.project_info.git_path = self._generate_vcs_xml()
+        self._copy_constant_project_files()
 
+    @classmethod
+    def generate_ide_project_files(cls, projects):
+        """Generate IDE project files by a list of ProjectInfo instances.
 
-def generate_eclipse_project_files(projects):
-    """Generate Eclipse project files by a list of ProjectInfo instances.
+        For multiple modules case, we call _generate_intellij_project_file to
+        generate iml file for submodules first and pass submodules' iml file
+        paths as an argument to function _generate_intellij_project_file when we
+        generate main module.iml file. In this way, we can add submodules'
+        dependencies iml and their own iml file paths to main module's
+        module.xml.
 
-    Args:
-        projects: A list of ProjectInfo instances.
-    """
-    for project in projects:
-        _generate_eclipse_project_file(project)
+        Args:
+            projects: A list of ProjectInfo instances.
+        """
+        # Initialization
+        cls._USED_NAME_CACHE.clear()
+        _merge_all_shared_source_paths(projects)
+        for project in projects[1:]:
+            ProjectFileGenerator(project).generate_intellij_project_file()
+        iml_paths = [project.iml_path for project in projects[1:]]
+        ProjectFileGenerator(
+            projects[0]).generate_intellij_project_file(iml_paths)
+        _merge_project_vcs_xmls(projects)
 
+    def _copy_constant_project_files(self):
+        """Copy project files to target path with error handling.
 
-def _read_file_content(path):
-    """Read file's content.
+        This function would copy compiler.xml, misc.xml, codeStyles folder and
+        copyright folder to target folder. Since these files aren't mandatory in
+        IntelliJ, it only logs when an IOError occurred.
+        """
+        target_path = self.project_info.project_absolute_path
+        try:
+            self._copy_to_idea_folder(target_path, _COPYRIGHT_FOLDER)
+            self._copy_to_idea_folder(target_path, _CODE_STYLE_FOLDER)
+            code_style_target_path = os.path.join(
+                target_path, _IDEA_FOLDER, _CODE_STYLE_FOLDER, 'Project.xml')
+            # Base on current working directory to prepare the relevant location
+            # of the symbolic link file, and base on the symlink file location
+            # to prepare the relevant code style source path.
+            rel_target = os.path.relpath(code_style_target_path, os.getcwd())
+            rel_source = os.path.relpath(
+                _CODE_STYLE_SRC_PATH, os.path.dirname(code_style_target_path))
+            logging.debug('Relative target symlink path: %s.', rel_target)
+            logging.debug('Relative code style source path: %s.', rel_source)
+            os.symlink(rel_source, rel_target)
+            # Create .gitignore if it doesn't exist.
+            _generate_git_ignore(target_path)
+            shutil.copy(
+                os.path.join(_IDEA_DIR, _COMPILE_XML),
+                os.path.join(target_path, _IDEA_FOLDER, _COMPILE_XML))
+            shutil.copy(
+                os.path.join(_IDEA_DIR, _MISC_XML),
+                os.path.join(target_path, _IDEA_FOLDER, _MISC_XML))
+        except (IOError, SystemError) as err:
+            logging.warning('%s can\'t copy the project files\n %s',
+                            target_path, err)
 
-    Args:
-        path: Path of input file.
+    @staticmethod
+    def _copy_to_idea_folder(target_path, folder_name):
+        """Copy folder to project .idea path.
 
-    Returns:
-        String: Content of the file.
-    """
-    with open(path) as template:
-        return template.read()
+        Args:
+            target_path: Path of target folder.
+            folder_name: Name of target folder.
+        """
+        abs_target_path = os.path.join(target_path, _IDEA_FOLDER, folder_name)
+        # Existing folder needs to be removed first, otherwise it will raise
+        # IOError.
+        if os.path.exists(abs_target_path):
+            shutil.rmtree(abs_target_path)
+        shutil.copytree(os.path.join(_IDEA_DIR, folder_name), abs_target_path)
 
+    def _handle_facet(self, content):
+        """Handle facet part of iml.
 
-def _file_generate(path, content):
-    """Generate file from content.
+        If the module is an Android app, which contains AndroidManifest.xml, it
+        should have a facet of android, otherwise we don't need facet in iml.
 
-    Args:
-        path: Path of target file.
-        content: String content of file.
-    """
-    if not os.path.exists(os.path.dirname(path)):
-        os.makedirs(os.path.dirname(path))
-    with open(path, 'w') as target:
-        target.write(content)
+        Args:
+            content: String content of iml.
 
+        Returns:
+            String: Content with facet handled.
+        """
+        facet = ''
+        facet_path = self.project_info.project_absolute_path
+        if os.path.isfile(os.path.join(facet_path, _ANDROID_MANIFEST)):
+            facet = _FACET_SECTION
+        return content.replace(_FACET_TOKEN, facet)
 
-def _copy_constant_project_files(target_path):
-    """Copy project files to target path with error handling.
+    @staticmethod
+    def _handle_module_dependency(content, jar_dependencies):
+        """Handle module dependency part of iml.
 
-    This function would copy compiler.xml, misc.xml, codeStyles folder and
-    copyright folder to target folder. Since these files aren't mandatory in
-    IntelliJ, it only logs when an IOError occurred.
+        Args:
+            content: String content of iml.
+            jar_dependencies: List of the jar path.
 
-    Args:
-        target_path: A folder path to copy content to.
-    """
-    try:
-        _copy_to_idea_folder(target_path, _COPYRIGHT_FOLDER)
-        _copy_to_idea_folder(target_path, _CODE_STYLE_FOLDER)
-        code_style_target_path = os.path.join(target_path, _IDEA_FOLDER,
-                                              _CODE_STYLE_FOLDER, 'Project.xml')
-        # Base on current working directory to prepare the relevant location
-        # of the symbolic link file, and base on the symlink file location to
-        # prepare the relevant code style source path.
-        rel_target = os.path.relpath(code_style_target_path, os.getcwd())
-        rel_source = os.path.relpath(_CODE_STYLE_SRC_PATH,
-                                     os.path.dirname(code_style_target_path))
-        logging.debug('Relative target symlink path: %s.', rel_target)
-        logging.debug('Relative code style source path: %s.', rel_source)
-        os.symlink(rel_source, rel_target)
-        # Create .gitignore if it doesn't exist.
-        _generate_git_ignore(target_path)
-        shutil.copy(
-            os.path.join(_IDEA_DIR, _COMPILE_XML),
-            os.path.join(target_path, _IDEA_FOLDER, _COMPILE_XML))
-        shutil.copy(
-            os.path.join(_IDEA_DIR, _MISC_XML),
-            os.path.join(target_path, _IDEA_FOLDER, _MISC_XML))
-    except IOError as err:
-        logging.warning('%s can\'t copy the project files\n %s', target_path,
-                        err)
+        Returns:
+            String: Content with module dependency handled.
+        """
+        root_path = common_util.get_android_root_dir()
+        module_library = ''
+        dependencies = []
+        # Reorder deps in the iml generated by IntelliJ by inserting priority
+        # jars.
+        for jar_path in jar_dependencies:
+            if any((jar_path.endswith(high_priority_jar))
+                   for high_priority_jar in _HIGH_PRIORITY_JARS):
+                module_library += _ORDER_ENTRY % os.path.join(
+                    root_path, jar_path)
+            else:
+                dependencies.append(jar_path)
 
-
-def _copy_to_idea_folder(target_path, folder_name):
-    """Copy folder to project .idea path.
-
-    Args:
-        target_path: Path of target folder.
-        folder_name: Name of target folder.
-    """
-    target_folder_path = os.path.join(target_path, _IDEA_FOLDER, folder_name)
-    # Existing folder needs to be removed first, otherwise it will raise
-    # IOError.
-    if os.path.exists(target_folder_path):
-        shutil.rmtree(target_folder_path)
-    shutil.copytree(os.path.join(_IDEA_DIR, folder_name), target_folder_path)
-
-
-def _handle_facet(content, path):
-    """Handle facet part of iml.
-
-    If the module is an Android app, which contains AndroidManifest.xml, it
-    should have a facet of android, otherwise we don't need facet in iml.
-
-    Args:
-        content: String content of iml.
-        path: Path of the module.
-
-    Returns:
-        String: Content with facet handled.
-    """
-    facet = ''
-    if os.path.isfile(os.path.join(path, _ANDROID_MANIFEST)):
-        facet = _FACET_SECTION
-    return content.replace(_FACET_TOKEN, facet)
-
-
-def _handle_module_dependency(root_path, content, jar_dependencies):
-    """Handle module dependency part of iml.
-
-    Args:
-        root_path: Android source tree root path.
-        content: String content of iml.
-        jar_dependencies: List of the jar path.
-
-    Returns:
-        String: Content with module dependency handled.
-    """
-    module_library = ''
-    dependencies = []
-    # Reorder deps in the iml generated by IntelliJ by inserting priority jars.
-    for jar_path in jar_dependencies:
-        if any((jar_path.endswith(high_priority_jar))
-               for high_priority_jar in _HIGH_PRIORITY_JARS):
+        # IntelliJ indexes jars as dependencies from iml by the ascending order.
+        # Without sorting, the order of jar list changes everytime. Sort the jar
+        # list to keep the jar dependencies in consistency. It also can help us
+        # to discover potential issues like duplicated classes.
+        for jar_path in sorted(dependencies):
             module_library += _ORDER_ENTRY % os.path.join(root_path, jar_path)
-        else:
-            dependencies.append(jar_path)
+        return content.replace(_MODULE_DEP_TOKEN, module_library)
 
-    # IntelliJ indexes jars as dependencies from iml by the ascending order.
-    # Without sorting, the order of jar list changes everytime. Sort the jar
-    # list to keep the jar dependencies in consistency. It also can help us to
-    # discover potential issues like duplicated classes.
-    for jar_path in sorted(dependencies):
-        module_library += _ORDER_ENTRY % os.path.join(root_path, jar_path)
-    return content.replace(_MODULE_DEP_TOKEN, module_library)
+    def _is_project_relative_source(self, source):
+        """Check if the relative path of a file is a source relative path.
 
+        Check if the file path starts with the relative path or the relative is
+        an Android source tree root path.
 
-def _is_project_relative_source(source, relative_path):
-    """Check if the relative path of a file is a source relative path.
+        Args:
+            source: The file path to be checked.
 
-    Check if the file path starts with the relative path or the relative is an
-    Android source tree root path.
+        Returns:
+            True if the file is a source relative path, otherwise False.
+        """
+        relative_path = self.project_info.project_relative_path
+        abs_path = common_util.get_abs_path(relative_path)
+        if common_util.is_android_root(abs_path):
+            return True
+        if _is_source_under_relative_path(source, relative_path):
+            return True
+        return False
 
-    Args:
-        source: The file path to be checked.
-        relative_path: The relative path to be checked.
+    def _handle_source_folder(self, content, source_dict, is_module):
+        """Handle source folder part of iml.
 
-    Returns:
-        True if the file is a source relative path, otherwise False.
-    """
-    abs_path = common_util.get_abs_path(relative_path)
-    if common_util.is_android_root(abs_path):
-        return True
-    if _is_source_under_relative_path(source, relative_path):
-        return True
-    return False
+        It would make the source folder group by content.
+        e.g.
+        <content url="file://$MODULE_DIR$/a">
+            <sourceFolder url="file://$MODULE_DIR$/a/b" isTestSource="False"/>
+            <sourceFolder url="file://$MODULE_DIR$/a/test" isTestSource="True"/>
+            <sourceFolder url="file://$MODULE_DIR$/a/d/e" isTestSource="False"/>
+        </content>
 
+        Args:
+            content: String content of iml.
+            source_dict: A dictionary of sources path with a flag to identify
+                         the path is test or source folder in IntelliJ.
+                         e.g.
+                         {'path_a': True, 'path_b': False}
+            is_module: True if it is module iml, otherwise it is dependencies
+                       iml.
 
-def _handle_source_folder(root_path, content, source_dict, is_module,
-                          relative_path):
-    """Handle source folder part of iml.
+        Returns:
+            String: Content with source folder handled.
+        """
+        root_path = common_util.get_android_root_dir()
+        relative_path = self.project_info.project_relative_path
 
-    It would make the source folder group by content.
-    e.g.
-    <content url="file://$MODULE_DIR$/a">
-        <sourceFolder url="file://$MODULE_DIR$/a/b" isTestSource="False" />
-        <sourceFolder url="file://$MODULE_DIR$/a/test" isTestSource="True" />
-        <sourceFolder url="file://$MODULE_DIR$/a/d/e" isTestSource="False" />
-    </content>
-
-    Args:
-        root_path: Android source tree root path.
-        content: String content of iml.
-        source_dict: A dictionary of sources path with a flag to identify the
-                     path is test or source folder in IntelliJ.
-                     e.g.
-                     {'path_a': True, 'path_b': False}
-        is_module: True if it is module iml, otherwise it is dependencies iml.
-        relative_path: Relative path of the module.
-
-    Returns:
-        String: Content with source folder handled.
-    """
-    source_list = list(source_dict.keys())
-    source_list.sort()
-    src_builder = []
-    if is_module:
-        # Set the content url to module's path since it's the iml of target
-        # project which only has it's sub-folders in source_list.
-        src_builder.append(
-            _CONTENT_URL % os.path.join(root_path, relative_path))
-        for path, is_test_flag in sorted(source_dict.items()):
-            if _is_project_relative_source(path, relative_path):
-                src_builder.append(_SOURCE_FOLDER % (os.path.join(
-                    root_path, path), is_test_flag))
-        src_builder.append(_END_CONTENT)
-    else:
-        for path, is_test_flag in sorted(source_dict.items()):
-            path = os.path.join(root_path, path)
-            src_builder.append(_CONTENT_URL % path)
-            src_builder.append(_SOURCE_FOLDER % (path, is_test_flag))
+        src_builder = []
+        if is_module:
+            # Set the content url to module's path since it's the iml of target
+            # project which only has it's sub-folders in source_list.
+            src_builder.append(
+                _CONTENT_URL % os.path.join(root_path, relative_path))
+            for path, is_test_flag in sorted(source_dict.items()):
+                if self._is_project_relative_source(path):
+                    src_builder.append(_SOURCE_FOLDER % (os.path.join(
+                        root_path, path), is_test_flag))
+            # If relative_path empty, it is Android root. When handling root
+            # module, we add the exclude folders to speed up indexing time.
+            if not relative_path:
+                src_builder.extend(_get_exclude_content(root_path))
             src_builder.append(_END_CONTENT)
-    return content.replace(_SOURCE_TOKEN, ''.join(src_builder))
+        else:
+            for path, is_test_flag in sorted(source_dict.items()):
+                path = os.path.join(root_path, path)
+                src_builder.append(_CONTENT_URL % path)
+                src_builder.append(_SOURCE_FOLDER % (path, is_test_flag))
+                src_builder.append(_END_CONTENT)
+        return content.replace(_SOURCE_TOKEN, ''.join(src_builder))
+
+    @staticmethod
+    def _handle_srcjar_folder(content, srcjar_paths=None):
+        """Handle the aapt2.srcjar and R.jar content for iml.
+
+        Example for setting the aapt2.srcjar or R.jar as a source folder in
+        IntelliJ.
+        e.g.
+        <content url="jar://$MODULE_DIR$/aapt2.srcjar!/">
+            <sourceFolder url="jar://$MODULE_DIR$/aapt2.srcjar!/"
+                          isTestSource="False"/>
+        </content>
+        <content url="jar://$MODULE_DIR$/R.jar!/">
+            <sourceFolder url="jar://$MODULE_DIR$/R.jar!/"
+                          isTestSource="False"/>
+        </content>
+
+        Args:
+            content: String content of iml.
+            srcjar_paths: A set of srcjar paths, default value is None.
+
+        Returns:
+            String: Content with srcjar folder handled.
+        """
+        srcjar_urls = []
+        if srcjar_paths:
+            for srcjar_dir in srcjar_paths:
+                srcjar_urls.append(_SRCJAR_URL.format(SRCJAR=os.path.join(
+                    common_util.get_android_root_dir(), srcjar_dir)))
+        return content.replace(_SRCJAR_TOKEN, ''.join(srcjar_urls))
+
+    # pylint: disable=too-many-locals
+    def _generate_iml(self, source_dict, is_main_module=False):
+        """Generate iml file.
+
+        Args:
+            source_dict: A dictionary of sources path with a flag to distinguish
+                         the path is test or source folder in IntelliJ.
+                         e.g.
+                         {'path_a': True, 'path_b': False}
+            is_main_module: A boolean with default False, True if the current
+                            project is the main module.
+
+        Returns:
+            String: The absolute paths of module iml and dependencies iml.
+        """
+        module_path = self.project_info.project_absolute_path
+        jar_dependencies = list(self.project_info.source_path['jar_path'])
+        # Separate module and dependencies source folder
+        project_source_dict = {}
+        for source in list(source_dict):
+            if self._is_project_relative_source(source):
+                is_test = source_dict.get(source)
+                source_dict.pop(source)
+                project_source_dict.update({source: is_test})
+
+        # Generate module iml.
+        module_content = self._handle_facet(constant.FILE_IML)
+        module_content = self._handle_source_folder(module_content,
+                                                    project_source_dict, True)
+        module_content = self._handle_srcjar_folder(module_content)
+        # b/121256503: Prevent duplicated iml names from breaking IDEA.
+        module_name = self.get_unique_iml_name(module_path)
+
+        module_iml_path = os.path.join(module_path,
+                                       module_name + _IML_EXTENSION)
+
+        dep_sect = _MODULE_ORDER_ENTRY % constant.KEY_DEPENDENCIES
+        module_content = module_content.replace(_MODULE_DEP_TOKEN, dep_sect)
+        common_util.file_generate(module_iml_path, module_content)
+
+        # Only generate the dependencies.iml in the main module's folder.
+        dependencies_iml_path = None
+        if is_main_module:
+            dependencies_content = constant.FILE_IML.replace(_FACET_TOKEN, '')
+            dependencies_content = self._handle_source_folder(
+                dependencies_content, source_dict, False)
+            dependencies_content = self._handle_srcjar_folder(
+                dependencies_content,
+                self.project_info.source_path['srcjar_path'])
+            dependencies_content = self._handle_module_dependency(
+                dependencies_content, jar_dependencies)
+            dependencies_iml_path = os.path.join(
+                module_path, constant.KEY_DEPENDENCIES + _IML_EXTENSION)
+            common_util.file_generate(dependencies_iml_path,
+                                      dependencies_content)
+            logging.debug('Paired iml names are %s, %s', module_iml_path,
+                          dependencies_iml_path)
+        # The dependencies_iml_path is use for removing the file itself in
+        # unittest.
+        return module_iml_path, dependencies_iml_path
+
+    def _generate_modules_xml(self, iml_path_list=None):
+        """Generate modules.xml file.
+
+        IntelliJ uses modules.xml to import which modules should be loaded to
+        project. In multiple modules case, we will pass iml_path_list of
+        submodules' dependencies and their iml file paths to add them into main
+        module's module.xml file. The dependencies.iml file contains all shared
+        dependencies source folders and jar files.
+
+        Args:
+            iml_path_list: A list of submodule iml paths.
+        """
+        module_path = self.project_info.project_absolute_path
+        content = common_util.read_file_content(_TEMPLATE_MODULES_PATH)
+
+        # b/121256503: Prevent duplicated iml names from breaking IDEA.
+        module_name = self.get_unique_iml_name(module_path)
+
+        if iml_path_list is not None:
+            module_list = [
+                _MODULE_SECTION % (module_name, module_name),
+                _MODULE_SECTION % (constant.KEY_DEPENDENCIES,
+                                   constant.KEY_DEPENDENCIES)
+            ]
+            for iml_path in iml_path_list:
+                module_list.append(_SUB_MODULES_SECTION.format(IML=iml_path))
+        else:
+            module_list = [
+                _MODULE_SECTION % (module_name, module_name)
+            ]
+        module = '\n'.join(module_list)
+        content = content.replace(_MODULE_TOKEN, module)
+        target_path = os.path.join(module_path, _IDEA_FOLDER, _MODULES_XML)
+        common_util.file_generate(target_path, content)
+
+    def _generate_vcs_xml(self):
+        """Generate vcs.xml file.
+
+        IntelliJ use vcs.xml to record version control software's information.
+        Since we are using a single project file, it will only contain the
+        module itself. If there is no git folder inside, it would find it in
+        parent's folder.
+
+        Return:
+            String: A module's git path.
+        """
+        module_path = self.project_info.project_absolute_path
+        # When importing whole Android repo, it shouldn't add vcs.xml,
+        # because IntelliJ doesn't handle repo as a version control.
+        if module_path == common_util.get_android_root_dir():
+            # TODO(b/135103553): Do a survey about: does devs want add
+            # every git into IntelliJ when importing whole Android.
+            return None
+        git_path = module_path
+        while not os.path.isdir(os.path.join(git_path, _GIT_FOLDER_NAME)):
+            git_path = str(pathlib.Path(git_path).parent)
+            if git_path == os.sep:
+                logging.warning('%s can\'t find its .git folder', module_path)
+                return None
+        _write_vcs_xml(module_path, [git_path])
+        return git_path
+
+
+def _get_exclude_content(root_path):
+    """Get the exclude folder content list.
+
+    It returns the exclude folders content list.
+    e.g.
+    ['<excludeFolder url="file://a/.idea" />',
+    '<excludeFolder url="file://a/.repo" />']
+
+    Args:
+        root_path: Android source file path.
+
+    Returns:
+        String: exclude folder content list.
+    """
+    exclude_items = []
+    for folder in _EXCLUDE_FOLDERS:
+        folder_path = os.path.join(root_path, folder)
+        if os.path.isdir(folder_path):
+            exclude_items.append(_EXCLUDE_ITEM % folder_path)
+    return exclude_items
 
 
 def _trim_same_root_source(source_list):
@@ -440,175 +602,6 @@
     return source == relative_path or source.startswith(relative_path + os.sep)
 
 
-# pylint: disable=too-many-locals
-def _generate_iml(root_path, module_path, source_dict, jar_dependencies,
-                  relative_path):
-    """Generate iml file.
-
-    Args:
-        root_path: Android source tree root path.
-        module_path: Absolute path of the module.
-        source_dict: A dictionary of sources path with a flag to distinguish the
-                     path is test or source folder in IntelliJ.
-                     e.g.
-                     {'path_a': True, 'path_b': False}
-        jar_dependencies: List of the jar path.
-        relative_path: Relative path of the module.
-
-    Returns:
-        String: The absolute paths of module iml and dependencies iml.
-    """
-    template = _read_file_content(_TEMPLATE_IML_PATH)
-
-    # Separate module and dependencies source folder
-    project_source_dict = {}
-    for source in list(source_dict):
-        if _is_project_relative_source(source, relative_path):
-            is_test = source_dict.get(source)
-            source_dict.pop(source)
-            project_source_dict.update({source: is_test})
-
-    # Generate module iml.
-    module_content = _handle_facet(template, module_path)
-    module_content = _handle_source_folder(
-        root_path, module_content, project_source_dict, True, relative_path)
-    # b/121256503: Prevent duplicated iml names from breaking IDEA.
-    module_name = get_unique_iml_name(module_path)
-
-    module_iml_path = os.path.join(module_path, module_name + _IML_EXTENSION)
-
-    dep_name = _get_dependencies_name(module_name)
-    dep_sect = _MODULE_ORDER_ENTRY % dep_name
-    module_content = module_content.replace(_MODULE_DEP_TOKEN, dep_sect)
-    _file_generate(module_iml_path, module_content)
-
-    # Generate dependencies iml.
-    dependencies_content = template.replace(_FACET_TOKEN, '')
-    dependencies_content = _handle_source_folder(
-        root_path, dependencies_content, source_dict, False, relative_path)
-    dependencies_content = _handle_module_dependency(
-        root_path, dependencies_content, jar_dependencies)
-    dependencies_iml_path = os.path.join(module_path, dep_name + _IML_EXTENSION)
-    _file_generate(dependencies_iml_path, dependencies_content)
-    logging.debug('Paired iml names are %s, %s', module_iml_path,
-                  dependencies_iml_path)
-    # The dependencies_iml_path is use for removing the file itself in unittest.
-    return module_iml_path, dependencies_iml_path
-
-
-def _generate_classpath(module_path, source_list, jar_dependencies):
-    """Generate .classpath file.
-
-    Args:
-        module_path: Absolute path of the module.
-        source_list: A list of sources path.
-        jar_dependencies: List of the jar path.
-
-    Returns:
-        String: The absolute paths of .classpath.
-    """
-    template = _read_file_content(_ECLIP_TEMPLATE_PATH)
-
-    src_list = [_ECLIP_SRC_ENTRY.format(s) for s in source_list]
-    template = template.replace(_ECLIP_SRC_TOKEN, ''.join(src_list))
-
-    lib_list = [_ECLIP_LIB_ENTRY.format(j) for j in jar_dependencies]
-    template = template.replace(_ECLIP_LIB_TOKEN, ''.join(lib_list))
-
-    classpath_path = os.path.join(module_path, _ECLIP_EXTENSION)
-
-    _file_generate(classpath_path, template)
-
-    return classpath_path
-
-
-def _generate_eclipse_project(project_name, module_path):
-    """Generate .project file of Eclipse.
-
-    Args:
-        project_name: A string of the project name.
-        module_path: Absolute path of the module.
-    """
-    template = _read_file_content(_ECLIP_PROJECT_PATH)
-    template = template.replace(_ECLIP_PROJECT_NAME_TOKEN, project_name)
-    eclipse_project = os.path.join(module_path, _ECLIP_PROJECT_EXTENSION)
-    _file_generate(eclipse_project, template)
-
-
-def _get_dependencies_name(module_name):
-    """Get module's dependencies iml name which will be written in module.xml.
-
-    Args:
-        module_name: The name will be appended to "dependencies-".
-
-    Returns:
-        String: The joined dependencies iml file name, e.g. "dependencies-core"
-    """
-    return '-'.join([_DEPENDENCIES, module_name])
-
-
-def _generate_modules_xml(module_path, iml_path_list=None):
-    """Generate modules.xml file.
-
-    IntelliJ uses modules.xml to import which modules should be loaded to
-    project. Only in multiple modules case will we pass iml_path_list of
-    submodules' dependencies and their iml file paths to add them into main
-    module's module.xml file. The dependencies iml file names will be changed
-    from original dependencies.iml to dependencies-[module_name].iml,
-    e.g. dependencies-core.iml for core.iml.
-
-    Args:
-        module_path: Path of the module.
-        iml_path_list: A list of submodule iml paths.
-    """
-    content = _read_file_content(_TEMPLATE_MODULES_PATH)
-
-    # b/121256503: Prevent duplicated iml names from breaking IDEA.
-    module_name = get_unique_iml_name(module_path)
-
-    file_name = os.path.splitext(module_name)[0]
-    dep_name = _get_dependencies_name(file_name)
-    module_list = [
-        _MODULE_SECTION % (module_name, module_name),
-        _MODULE_SECTION % (dep_name, dep_name)
-    ]
-    if iml_path_list:
-        for iml_path in iml_path_list:
-            iml_dir, iml_name = os.path.split(iml_path)
-            dep_file = _get_dependencies_name(iml_name)
-            dep_path = os.path.join(iml_dir, dep_file)
-            module_list.append(_SUB_MODULES_SECTION % (dep_path, dep_path))
-            module_list.append(_SUB_MODULES_SECTION % (iml_path, iml_path))
-    module = '\n'.join(module_list)
-    content = content.replace(_MODULE_TOKEN, module)
-    target_path = os.path.join(module_path, _IDEA_FOLDER, _MODULES_XML)
-    _file_generate(target_path, content)
-
-
-def _generate_vcs_xml(module_path):
-    """Generate vcs.xml file.
-
-    IntelliJ use vcs.xml to record version control software's information.
-    Since we are using a single project file, it will only contain the
-    module itself. If there is no git folder inside, it would find it in
-    parent's folder.
-
-    Args:
-        module_path: Path of the module.
-
-    Return:
-        String: A module's git path.
-    """
-    git_path = module_path
-    while not os.path.isdir(os.path.join(git_path, _GIT_FOLDER_NAME)):
-        git_path = str(pathlib.Path(git_path).parent)
-        if git_path == os.sep:
-            logging.warning('%s can\'t find its .git folder', module_path)
-            return None
-    _write_vcs_xml(module_path, [git_path])
-    return git_path
-
-
 def _write_vcs_xml(module_path, git_paths):
     """Write the git path into vcs.xml.
 
@@ -620,10 +613,10 @@
         git_paths: A list of git path.
     """
     _vcs_content = '\n'.join([_VCS_SECTION % p for p in git_paths if p])
-    content = _read_file_content(_TEMPLATE_VCS_PATH)
+    content = common_util.read_file_content(_TEMPLATE_VCS_PATH)
     content = content.replace(_VCS_TOKEN, _vcs_content)
     target_path = os.path.join(module_path, _IDEA_FOLDER, _VCS_XML)
-    _file_generate(target_path, content)
+    common_util.file_generate(target_path, content)
 
 
 def _merge_project_vcs_xmls(projects):
@@ -661,3 +654,69 @@
             os.symlink(rel_source, rel_target)
     except OSError as err:
         logging.error('Not support to run aidegen on Windows.\n %s', err)
+
+
+def _filter_out_source_paths(source_paths, module_relpaths):
+    """Filter out the source paths which belong to the target module.
+
+    The source_paths is a union set of all source paths of all target modules.
+    For generating the dependencies.iml, we only need the source paths outside
+    the target modules.
+
+    Args:
+        source_paths: A set contains the source folder paths.
+        module_relpaths: A list, contains the relative paths of target modules
+                         except the main module.
+
+    Returns: A set of source paths.
+    """
+    return {x for x in source_paths if not any(
+        {_is_source_under_relative_path(x, y) for y in module_relpaths})}
+
+
+def _merge_all_shared_source_paths(projects):
+    """Merge all source paths and jar paths into main project.
+
+    There should be no duplicate source root path in IntelliJ. The issue doesn't
+    happen in single project case. Once users choose multiple projects, there
+    could be several same source paths of different projects. In order to
+    prevent that, we should remove the source paths in dependencies.iml which
+    are duplicate with the paths in [module].iml files.
+
+    Args:
+        projects: A list of ProjectInfo instances.
+    """
+    main_project = projects[0]
+    # Merge all source paths of sub projects into main project.
+    for project in projects[1:]:
+        main_project.source_path['source_folder_path'].update(
+            project.source_path['source_folder_path'])
+        main_project.source_path['test_folder_path'].update(
+            project.source_path['test_folder_path'])
+        main_project.source_path['jar_path'].update(
+            project.source_path['jar_path'])
+    # Filter duplicate source/test paths from dependencies.iml.
+    sub_projects_relpaths = {p.project_relative_path for p in projects[1:]}
+    main_project.source_path['source_folder_path'] = _filter_out_source_paths(
+        main_project.source_path['source_folder_path'], sub_projects_relpaths)
+    main_project.source_path['test_folder_path'] = _filter_out_source_paths(
+        main_project.source_path['test_folder_path'], sub_projects_relpaths)
+
+
+def update_enable_debugger(module_path, enable_debugger_module_abspath=None):
+    """Append the enable_debugger module's info in modules.xml file.
+
+    Args:
+        module_path: A string of the folder path contains IDE project content,
+                     e.g., the folder contains the .idea folder.
+        enable_debugger_module_abspath: A string of the im file path of enable
+                                        debugger module.
+    """
+    replace_string = ''
+    if enable_debugger_module_abspath:
+        replace_string = _SUB_MODULES_SECTION.format(
+            IML=enable_debugger_module_abspath)
+    target_path = os.path.join(module_path, _IDEA_FOLDER, _MODULES_XML)
+    content = common_util.read_file_content(target_path)
+    content = content.replace(_ENABLE_DEBUGGER_MODULE_TOKEN, replace_string)
+    common_util.file_generate(target_path, content)
diff --git a/aidegen/lib/project_file_gen_unittest.py b/aidegen/lib/project_file_gen_unittest.py
index fe24b81..212f14e 100644
--- a/aidegen/lib/project_file_gen_unittest.py
+++ b/aidegen/lib/project_file_gen_unittest.py
@@ -20,12 +20,13 @@
 import os
 import shutil
 import unittest
-
 from unittest import mock
 
+import aidegen.unittest_constants as uc
 from aidegen import constant
-from aidegen import unittest_constants
+from aidegen.lib import common_util
 from aidegen.lib import project_file_gen
+from aidegen.lib.project_file_gen import ProjectFileGenerator
 from atest import module_info
 
 
@@ -34,67 +35,67 @@
     """Unit tests for project_file_gen.py."""
 
     maxDiff = None
-    _TEST_DATA_PATH = unittest_constants.TEST_DATA_PATH
-    _ANDROID_PROJECT_PATH = os.path.join(_TEST_DATA_PATH, 'android_project')
+    _TEST_DATA_PATH = uc.TEST_DATA_PATH
     _PROJECT_PATH = os.path.join(_TEST_DATA_PATH, 'project')
     _ANDROID_FACET_SAMPLE = os.path.join(_TEST_DATA_PATH, 'android_facet.iml')
     _PROJECT_FACET_SAMPLE = os.path.join(_TEST_DATA_PATH, 'project_facet.iml')
     _MODULE_DEP_SAMPLE = os.path.join(_TEST_DATA_PATH, 'module_dependency.iml')
     _IML_SAMPLE = os.path.join(_TEST_DATA_PATH, 'test.iml')
-    _CLASSPATH_SAMPLE = os.path.join(_TEST_DATA_PATH, 'eclipse.classpath')
     _DEPENDENCIES_IML_SAMPLE = os.path.join(_TEST_DATA_PATH, 'dependencies.iml')
     _MODULE_XML_SAMPLE = os.path.join(_TEST_DATA_PATH, 'modules.xml')
+    _MAIN_MODULE_XML_SAMPLE = os.path.join(_TEST_DATA_PATH,
+                                           'modules_only_self_module.xml')
+    _ENABLE_DEBUGGER_MODULE_SAMPLE = os.path.join(
+        _TEST_DATA_PATH, 'modules_with_enable_debugger.xml')
     _VCS_XML_SAMPLE = os.path.join(_TEST_DATA_PATH, 'vcs.xml')
-    _IML_PATH = os.path.join(_ANDROID_PROJECT_PATH, 'android_project.iml')
-    _DEPENDENCIES_IML_PATH = os.path.join(_ANDROID_PROJECT_PATH,
+    _IML_PATH = os.path.join(uc.ANDROID_PROJECT_PATH, 'android_project.iml')
+    _DEPENDENCIES_IML_PATH = os.path.join(uc.ANDROID_PROJECT_PATH,
                                           'dependencies.iml')
-    _IDEA_PATH = os.path.join(_ANDROID_PROJECT_PATH, '.idea')
+    _IDEA_PATH = os.path.join(uc.ANDROID_PROJECT_PATH, '.idea')
     _MODULE_PATH = os.path.join(_IDEA_PATH, 'modules.xml')
     _VCS_PATH = os.path.join(_IDEA_PATH, 'vcs.xml')
     _SOURCE_SAMPLE = os.path.join(_TEST_DATA_PATH, 'source.iml')
+    _SRCJAR_SAMPLE = os.path.join(_TEST_DATA_PATH, 'srcjar.iml')
     _LOCAL_PATH_TOKEN = '@LOCAL_PATH@'
     _AOSP_FOLDER = '/aosp'
-    _JAR_DEP_LIST = ['test1.jar', 'test2.jar']
     _TEST_SOURCE_LIST = [
         'a/b/c/d', 'a/b/c/d/e', 'a/b/c/d/e/f', 'a/b/c/d/f', 'e/f/a', 'e/f/b/c',
         'e/f/g/h'
     ]
-    _ANDROID_SOURCE_DICT = {
-        'test_data/project/level11/level21': True,
-        'test_data/project/level11/level22/level31': False,
-        'test_data/project/level12/level22': False,
-    }
     _ANDROID_SOURCE_RELATIVE_PATH = 'test_data/project'
     _SAMPLE_CONTENT_LIST = ['a/b/c/d', 'e/f']
     _SAMPLE_TRIMMED_SOURCE_LIST = ['a/b/c/d', 'e/f/a', 'e/f/b/c', 'e/f/g/h']
+    _SAMPLE_EXCLUDE_FOLDERS = [
+        '            <excludeFolder url="file://%s/.idea" />\n'
+        % _TEST_DATA_PATH,
+        '            <excludeFolder url="file://%s/out" />\n' % _TEST_DATA_PATH,
+    ]
 
-    def test_handle_facet_for_android(self):
+    @mock.patch('aidegen.lib.project_info.ProjectInfo')
+    def test_handle_facet_for_android(self, mock_project):
         """Test _handle_facet with android project."""
-        template = project_file_gen._read_file_content(
-            project_file_gen._TEMPLATE_IML_PATH)
-        android_facet = project_file_gen._handle_facet(
-            template, self._ANDROID_PROJECT_PATH)
-        sample_android_facet = project_file_gen._read_file_content(
+        mock_project.project_absolute_path = uc.ANDROID_PROJECT_PATH
+        android_facet = ProjectFileGenerator(mock_project)._handle_facet(
+            constant.FILE_IML)
+        sample_android_facet = common_util.read_file_content(
             self._ANDROID_FACET_SAMPLE)
         self.assertEqual(android_facet, sample_android_facet)
 
-    def test_handle_facet_for_normal(self):
+    @mock.patch('aidegen.lib.project_info.ProjectInfo')
+    def test_handle_facet_for_normal(self, mock_project):
         """Test _handle_facet with normal module."""
-        template = project_file_gen._read_file_content(
-            project_file_gen._TEMPLATE_IML_PATH)
-        project_facet = project_file_gen._handle_facet(template,
-                                                       self._PROJECT_PATH)
-        sample_project_facet = project_file_gen._read_file_content(
+        mock_project.project_absolute_path = self._PROJECT_PATH
+        project_facet = ProjectFileGenerator(mock_project)._handle_facet(
+            constant.FILE_IML)
+        sample_project_facet = common_util.read_file_content(
             self._PROJECT_FACET_SAMPLE)
         self.assertEqual(project_facet, sample_project_facet)
 
     def test_handle_module_dependency(self):
         """Test _module_dependency."""
-        module_dependency = project_file_gen._read_file_content(
-            project_file_gen._TEMPLATE_IML_PATH)
-        module_dependency = module_dependency.replace(
+        module_dependency = constant.FILE_IML.replace(
             project_file_gen._MODULE_DEP_TOKEN, '')
-        correct_module_dep = project_file_gen._read_file_content(
+        correct_module_dep = common_util.read_file_content(
             self._MODULE_DEP_SAMPLE)
         self.assertEqual(correct_module_dep, module_dependency)
 
@@ -104,58 +105,96 @@
             self._TEST_SOURCE_LIST[:])
         self.assertEqual(url_list, self._SAMPLE_TRIMMED_SOURCE_LIST)
 
-    def test_handle_source_folder(self):
+    @mock.patch('aidegen.lib.common_util.get_android_root_dir')
+    @mock.patch('aidegen.lib.project_info.ProjectInfo')
+    def test_handle_source_folder(self, mock_project, mock_get_root):
         """Test _handle_source_folder."""
-        template = project_file_gen._read_file_content(
-            project_file_gen._TEMPLATE_IML_PATH)
-        source = project_file_gen._handle_source_folder(
-            self._AOSP_FOLDER, template,
-            copy.deepcopy(self._ANDROID_SOURCE_DICT), True,
-            self._ANDROID_SOURCE_RELATIVE_PATH)
-        sample_source = project_file_gen._read_file_content(self._SOURCE_SAMPLE)
+        mock_get_root.return_value = self._AOSP_FOLDER
+        mock_project.project_relative_path = self._ANDROID_SOURCE_RELATIVE_PATH
+        source = ProjectFileGenerator(mock_project)._handle_source_folder(
+            constant.FILE_IML, copy.deepcopy(uc.ANDROID_SOURCE_DICT), True)
+        sample_source = common_util.read_file_content(self._SOURCE_SAMPLE)
         self.assertEqual(source, sample_source)
 
-    def test_generate_iml(self):
+    @mock.patch('aidegen.lib.common_util.get_android_root_dir')
+    @mock.patch('aidegen.lib.project_info.ProjectInfo')
+    def test_generate_iml(self, mock_project, mock_get_root):
         """Test _generate_iml."""
+        mock_get_root.return_value = self._AOSP_FOLDER
+        mock_project.project_absolute_path = uc.ANDROID_PROJECT_PATH
+        mock_project.project_relative_path = self._ANDROID_SOURCE_RELATIVE_PATH
+        mock_project.source_path['jar_path'] = set(uc.JAR_DEP_LIST)
+        pfile_gen = ProjectFileGenerator(mock_project)
+        # Test for main project.
         try:
-            iml_path, dependencies_iml_path = project_file_gen._generate_iml(
-                self._AOSP_FOLDER, self._ANDROID_PROJECT_PATH,
-                copy.deepcopy(self._ANDROID_SOURCE_DICT), self._JAR_DEP_LIST,
-                self._ANDROID_SOURCE_RELATIVE_PATH)
-            test_iml = project_file_gen._read_file_content(iml_path)
-            sample_iml = project_file_gen._read_file_content(self._IML_SAMPLE)
+            iml_path, dependencies_iml_path = pfile_gen._generate_iml(
+                copy.deepcopy(uc.ANDROID_SOURCE_DICT), is_main_module=True)
+            test_iml = common_util.read_file_content(iml_path)
+            sample_iml = common_util.read_file_content(self._IML_SAMPLE)
         finally:
             os.remove(iml_path)
-            os.remove(dependencies_iml_path)
+            if dependencies_iml_path:
+                os.remove(dependencies_iml_path)
         self.assertEqual(test_iml, sample_iml)
 
-    def test_generate_modules_xml(self):
-        """Test _generate_modules_xml."""
+        # Test for sub projects.
         try:
-            project_file_gen._generate_modules_xml(self._ANDROID_PROJECT_PATH)
-            test_module = project_file_gen._read_file_content(self._MODULE_PATH)
+            iml_path, _ = pfile_gen._generate_iml(
+                copy.deepcopy(uc.ANDROID_SOURCE_DICT))
+            test_iml = common_util.read_file_content(iml_path)
+            sample_iml = common_util.read_file_content(self._IML_SAMPLE)
+        finally:
+            os.remove(iml_path)
+        self.assertEqual(test_iml, sample_iml)
+
+    @mock.patch('aidegen.lib.project_info.ProjectInfo')
+    def test_generate_modules_xml(self, mock_project):
+        """Test _generate_modules_xml."""
+        mock_project.project_absolute_path = uc.ANDROID_PROJECT_PATH
+        pfile_gen = ProjectFileGenerator(mock_project)
+        # Test for main project.
+        try:
+            pfile_gen._generate_modules_xml([])
+            project_file_gen.update_enable_debugger(uc.ANDROID_PROJECT_PATH)
+            test_module = common_util.read_file_content(self._MODULE_PATH)
         finally:
             shutil.rmtree(self._IDEA_PATH)
-        sample_module = project_file_gen._read_file_content(
-            self._MODULE_XML_SAMPLE)
+        sample_module = common_util.read_file_content(self._MODULE_XML_SAMPLE)
         self.assertEqual(test_module, sample_module)
 
-    def test_generate_vcs_xml(self):
-        """Test _generate_vcs_xml."""
+        # Test for sub projects which only has self module.
         try:
-            git_path = os.path.join(self._ANDROID_PROJECT_PATH,
-                                    project_file_gen._GIT_FOLDER_NAME)
-            os.mkdir(git_path)
-            project_file_gen._generate_vcs_xml(self._ANDROID_PROJECT_PATH)
-            test_vcs = project_file_gen._read_file_content(self._VCS_PATH)
+            pfile_gen._generate_modules_xml()
+            project_file_gen.update_enable_debugger(uc.ANDROID_PROJECT_PATH)
+            test_module = common_util.read_file_content(self._MODULE_PATH)
         finally:
             shutil.rmtree(self._IDEA_PATH)
+        sample_module = common_util.read_file_content(
+            self._MAIN_MODULE_XML_SAMPLE)
+        self.assertEqual(test_module, sample_module)
+
+    @mock.patch('aidegen.lib.project_info.ProjectInfo')
+    def test_generate_vcs_xml(self, mock_project):
+        """Test _generate_vcs_xml."""
+        mock_project.project_absolute_path = uc.ANDROID_PROJECT_PATH
+        try:
+            git_path = os.path.join(uc.ANDROID_PROJECT_PATH,
+                                    project_file_gen._GIT_FOLDER_NAME)
+            if not os.path.exists(git_path):
+                os.mkdir(git_path)
+            pfile_gen = ProjectFileGenerator(mock_project)
+            pfile_gen._generate_vcs_xml()
+            test_vcs = common_util.read_file_content(self._VCS_PATH)
+        finally:
             shutil.rmtree(git_path)
-        sample_vcs = project_file_gen._read_file_content(self._VCS_XML_SAMPLE)
+        sample_vcs = common_util.read_file_content(self._VCS_XML_SAMPLE)
         # The sample must base on the real path.
         sample_vcs = sample_vcs.replace(self._LOCAL_PATH_TOKEN,
-                                        self._ANDROID_PROJECT_PATH)
+                                        uc.ANDROID_PROJECT_PATH)
         self.assertEqual(test_vcs, sample_vcs)
+        mock_project.project_absolute_path = common_util.get_android_root_dir()
+        pfile_gen = ProjectFileGenerator(mock_project)
+        self.assertIsNone(pfile_gen._generate_vcs_xml())
 
     def test_get_uniq_iml_name(self):
         """Test the unique name cache mechanism.
@@ -164,9 +203,21 @@
         name data set is the same as sub folder path count, then it means
         there's no duplicated name, the test PASS.
         """
+        # Add following test path
+        test_paths = {
+            'cts/tests/tests/app',
+            'cts/tests/app',
+            'cts/tests/app/app1/../app',
+            'cts/tests/app/app2/../app',
+            'cts/tests/app/app3/../app',
+            'frameworks/base/tests/xxxxxxxxxxxx/base',
+            'frameworks/base',
+            'external/xxxxx-xxx/robolectric',
+            'external/robolectric',
+        }
         mod_info = module_info.ModuleInfo()
-        test_paths = mod_info._get_path_to_module_info(
-            mod_info.name_to_module_info).keys()
+        test_paths.update(mod_info._get_path_to_module_info(
+            mod_info.name_to_module_info).keys())
         print('\n{} {}.'.format('Test_paths length:', len(test_paths)))
 
         path_list = []
@@ -174,7 +225,7 @@
             path_list.append(k)
         print('{} {}.'.format('path list with length:', len(path_list)))
 
-        names = [project_file_gen.get_unique_iml_name(f) for f in path_list]
+        names = [ProjectFileGenerator.get_unique_iml_name(f) for f in path_list]
         print('{} {}.'.format('Names list with length:', len(names)))
 
         self.assertEqual(len(names), len(path_list))
@@ -184,10 +235,11 @@
         print('{} {}.'.format('The size of name set is:', len(dic)))
         self.assertEqual(len(dic), len(path_list))
 
-    def test_copy_project_files(self):
+    @mock.patch('aidegen.lib.project_info.ProjectInfo')
+    def test_copy_project_files(self, mock_project):
         """Test _copy_constant_project_files."""
-        project_file_gen._copy_constant_project_files(
-            self._ANDROID_PROJECT_PATH)
+        mock_project.project_absolute_path = uc.ANDROID_PROJECT_PATH
+        ProjectFileGenerator(mock_project)._copy_constant_project_files()
         self.assertTrue(
             os.path.isfile(
                 os.path.join(self._IDEA_PATH,
@@ -205,28 +257,102 @@
                              'profiles_settings.xml')))
         shutil.rmtree(self._IDEA_PATH)
 
-    def test_generate_classpath(self):
-        """Test _generate_classpath."""
-        try:
-            classpath = project_file_gen._generate_classpath(
-                self._ANDROID_PROJECT_PATH,
-                copy.deepcopy(list(sorted(self._ANDROID_SOURCE_DICT))),
-                self._JAR_DEP_LIST)
-            test_iml = project_file_gen._read_file_content(classpath)
-            sample_iml = project_file_gen._read_file_content(
-                self._CLASSPATH_SAMPLE)
-        finally:
-            os.remove(classpath)
-        self.assertEqual(test_iml, sample_iml)
-
     @mock.patch('os.symlink')
     @mock.patch.object(os.path, 'exists')
     def test_generate_git_ignore(self, mock_path_exist, mock_link):
         """Test _generate_git_ignore."""
         mock_path_exist.return_value = True
-        project_file_gen._generate_git_ignore(constant.AIDEGEN_ROOT_PATH)
+        project_file_gen._generate_git_ignore(
+            common_util.get_aidegen_root_dir())
         self.assertFalse(mock_link.called)
 
+    def test_filter_out_source_paths(self):
+        """Test _filter_out_source_paths."""
+        test_set = {'a/a.java', 'b/b.java', 'c/c.java'}
+        module_relpath = {'a', 'c'}
+        expected_result = {'b/b.java'}
+        result_set = project_file_gen._filter_out_source_paths(test_set,
+                                                               module_relpath)
+        self.assertEqual(result_set, expected_result)
+
+    @mock.patch('aidegen.lib.project_info.ProjectInfo')
+    @mock.patch('aidegen.lib.project_info.ProjectInfo')
+    def test_merge_all_source_paths(self, mock_main_project, mock_sub_project):
+        """Test _merge_all_shared_source_paths."""
+        mock_main_project.project_relative_path = 'main'
+        mock_main_project.source_path = {
+            'source_folder_path': {
+                'main/java.java',
+                'sub/java.java',
+                'share1/java.java'
+            },
+            'test_folder_path': {'main/test.java', 'share1/test.java'},
+            'jar_path': {'main/jar.jar', 'share1/jar.jar'},
+            'r_java_path': {'out/R.java'},
+        }
+        mock_sub_project.project_relative_path = 'sub'
+        mock_sub_project.source_path = {
+            'source_folder_path': {'sub/java.java', 'share2/java.java'},
+            'test_folder_path': {'sub/test.java', 'share2/test.java'},
+            'jar_path': {'sub/jar.jar', 'share2/jar.jar'},
+            'r_java_path': {'out/R.java'},
+        }
+        expected_result = {
+            'source_folder_path': {
+                'main/java.java',
+                'share1/java.java',
+                'share2/java.java',
+            },
+            'test_folder_path': {
+                'main/test.java',
+                'share1/test.java',
+                'share2/test.java',
+            },
+            'jar_path': {
+                'main/jar.jar',
+                'sub/jar.jar',
+                'share1/jar.jar',
+                'share2/jar.jar',
+            },
+            'r_java_path': {'out/R.java'}
+        }
+        projects = [mock_main_project, mock_sub_project]
+        project_file_gen._merge_all_shared_source_paths(projects)
+        self.assertEqual(mock_main_project.source_path, expected_result)
+
+    def test_get_exclude_folders(self):
+        """Test _get_exclude_folders."""
+        exclude_folders = project_file_gen._get_exclude_content(
+            self._TEST_DATA_PATH)
+        self.assertEqual(self._SAMPLE_EXCLUDE_FOLDERS, exclude_folders)
+
+    @mock.patch('aidegen.lib.project_info.ProjectInfo')
+    def test_update_enable_debugger(self, mock_project):
+        """Test update_enable_debugger."""
+        enable_debugger_iml = '/path/to/enable_debugger/enable_debugger.iml'
+        sample_module = common_util.read_file_content(
+            self._ENABLE_DEBUGGER_MODULE_SAMPLE)
+        mock_project.project_absolute_path = uc.ANDROID_PROJECT_PATH
+        pfile_gen = ProjectFileGenerator(mock_project)
+        try:
+            pfile_gen._generate_modules_xml([])
+            project_file_gen.update_enable_debugger(
+                uc.ANDROID_PROJECT_PATH, enable_debugger_iml)
+            test_module = common_util.read_file_content(self._MODULE_PATH)
+            self.assertEqual(test_module, sample_module)
+        finally:
+            shutil.rmtree(self._IDEA_PATH)
+
+    @mock.patch('aidegen.lib.common_util.get_android_root_dir')
+    @mock.patch('aidegen.lib.project_info.ProjectInfo')
+    def test_handle_srcjar_folder(self, mock_project, mock_get_root):
+        """Test _handle_srcjar_folder."""
+        mock_get_root.return_value = self._AOSP_FOLDER
+        source = ProjectFileGenerator(mock_project)._handle_srcjar_folder(
+            constant.FILE_IML, {'out/aapt2.srcjar!/'})
+        sample_source = common_util.read_file_content(self._SRCJAR_SAMPLE)
+        self.assertEqual(source, sample_source)
+
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/aidegen/lib/project_info.py b/aidegen/lib/project_info.py
index 55a600c..cefb284 100644
--- a/aidegen/lib/project_info.py
+++ b/aidegen/lib/project_info.py
@@ -26,7 +26,6 @@
 from aidegen.lib.common_util import COLORED_INFO
 from aidegen.lib.common_util import get_related_paths
 
-_KEY_ROBOTESTS = ['robotests', 'robolectric']
 _ANDROID_MK = 'Android.mk'
 _ANDROID_BP = 'Android.bp'
 _CONVERT_MK_URL = ('https://android.googlesource.com/platform/build/soong/'
@@ -45,17 +44,19 @@
 _EXCLUDE_MODULES = ['fake-framework']
 
 
-class ProjectInfo():
+class ProjectInfo:
     """Project information.
 
     Class attributes:
-        modules_info: A dict of all modules info by combining module-info.json
-                      with module_bp_java_deps.json.
+        modules_info: A AidegenModuleInfo instance whose name_to_module_info is
+                      combining module-info.json with module_bp_java_deps.json.
+        config: A ProjectConfig instance which contains user preference of
+                project.
 
     Attributes:
         project_absolute_path: The absolute path of the project.
         project_relative_path: The relative path of the project to
-                               constant.ANDROID_ROOT_PATH.
+                               common_util.get_android_root_dir().
         project_module_names: A list of module names under project_absolute_path
                               directory or it's subdirectories.
         dep_modules: A dict has recursively dependent modules of
@@ -69,24 +70,32 @@
                                        paths.
                      jar_path: A set contains the jar file paths.
                      jar_module_path: A dictionary contains the jar file and
-                                      the module's path mapping.
+                                      the module's path mapping, only used in
+                                      Eclipse.
+                     r_java_path: A set contains the relative path to the
+                                  R.java files, only used in Eclipse.
+                     srcjar_path: A source content descriptor only used in
+                                  IntelliJ.
+                                  e.g. out/.../aapt2.srcjar!/
+                                  The "!/" is a content descriptor for
+                                  compressed files in IntelliJ.
     """
 
-    modules_info = {}
+    modules_info = None
+    config = None
 
-    def __init__(self, module_info, target=None):
+    def __init__(self, target=None):
         """ProjectInfo initialize.
 
         Args:
-            module_info: A ModuleInfo instance contains data of
-                         module-info.json.
             target: Includes target module or project path from user input, when
                     locating the target, project with matching module name of
                     the given target has a higher priority than project path.
         """
-        rel_path, abs_path = get_related_paths(module_info, target)
-        target = self._get_target_name(target, abs_path)
-        self.project_module_names = set(module_info.get_module_names(rel_path))
+        rel_path, abs_path = get_related_paths(self.modules_info, target)
+        self.module_name = self._get_target_name(target, abs_path)
+        self.project_module_names = set(
+            self.modules_info.get_module_names(rel_path))
         self.project_relative_path = rel_path
         self.project_absolute_path = abs_path
         self.iml_path = ''
@@ -94,7 +103,7 @@
         self._init_source_path()
         self.dep_modules = self.get_dep_modules()
         self._filter_out_modules()
-        self._display_convert_make_files_message(module_info, target)
+        self._display_convert_make_files_message()
 
     def _set_default_modues(self):
         """Append default hard-code modules, source paths and jar files.
@@ -117,35 +126,25 @@
             'source_folder_path': set(),
             'test_folder_path': set(),
             'jar_path': set(),
-            'jar_module_path': dict()
+            'jar_module_path': dict(),
+            'r_java_path': set(),
+            'srcjar_path': set()
         }
 
-    def _display_convert_make_files_message(self, module_info, target):
-        """Show message info users convert their Android.mk to Android.bp.
-
-        Args:
-            module_info: A ModuleInfo instance contains data of
-                         module-info.json.
-            target: When locating the target module or project path from users'
-                    input, project with matching module name of the given target
-                    has a higher priority than project path.
-        """
-        mk_set = set(self._search_android_make_files(module_info))
+    def _display_convert_make_files_message(self):
+        """Show message info users convert their Android.mk to Android.bp."""
+        mk_set = set(self._search_android_make_files())
         if mk_set:
             print('\n{} {}\n'.format(
                 COLORED_INFO('Warning:'),
-                _ANDROID_MK_WARN.format(target, '\n'.join(mk_set))))
+                _ANDROID_MK_WARN.format(self.module_name, '\n'.join(mk_set))))
 
-    def _search_android_make_files(self, module_info):
+    def _search_android_make_files(self):
         """Search project and dependency modules contain Android.mk files.
 
         If there is only Android.mk but no Android.bp, we'll show the warning
         message, otherwise we won't.
 
-        Args:
-            module_info: A ModuleInfo instance contains data of
-                         module-info.json.
-
         Yields:
             A string: the relative path of Android.mk.
         """
@@ -153,12 +152,13 @@
         android_bp = os.path.join(self.project_absolute_path, _ANDROID_BP)
         if os.path.isfile(android_mk) and not os.path.isfile(android_bp):
             yield '\t' + os.path.join(self.project_relative_path, _ANDROID_MK)
-        for module_name in self.dep_modules:
-            rel_path, abs_path = get_related_paths(module_info, module_name)
-            mod_mk = os.path.join(abs_path, _ANDROID_MK)
-            mod_bp = os.path.join(abs_path, _ANDROID_BP)
-            if os.path.isfile(mod_mk) and not os.path.isfile(mod_bp):
-                yield '\t' + os.path.join(rel_path, _ANDROID_MK)
+        for mod_name in self.dep_modules:
+            rel_path, abs_path = get_related_paths(self.modules_info, mod_name)
+            if rel_path and abs_path:
+                mod_mk = os.path.join(abs_path, _ANDROID_MK)
+                mod_bp = os.path.join(abs_path, _ANDROID_BP)
+                if os.path.isfile(mod_mk) and not os.path.isfile(mod_bp):
+                    yield '\t' + os.path.join(rel_path, _ANDROID_MK)
 
     def set_modules_under_project_path(self):
         """Find modules whose class is qualified to be included under the
@@ -166,12 +166,12 @@
         """
         logging.info('Find modules whose class is in %s under %s.',
                      common_util.TARGET_CLASSES, self.project_relative_path)
-        for name, data in self.modules_info.items():
+        for name, data in self.modules_info.name_to_module_info.items():
             if common_util.is_project_path_relative_module(
                     data, self.project_relative_path):
                 if self._is_a_target_module(data):
                     self.project_module_names.add(name)
-                    if self._is_a_robolectric_module(data):
+                    if self.modules_info.is_robolectric_test(name):
                         self.project_module_names.add(_ROBOLECTRIC_MODULE)
                 else:
                     logging.debug(_NOT_TARGET, name, data['class'],
@@ -198,25 +198,6 @@
             return False
         return any(x in data['class'] for x in common_util.TARGET_CLASSES)
 
-    @staticmethod
-    def _is_a_robolectric_module(data):
-        """Determine if the module is a robolectric module.
-
-        Hardcode for robotest dependency. If a folder named robotests or
-        robolectric is in the module's path hierarchy then add the module
-        Robolectric_all as a dependency.
-
-        Args:
-            data: the module-info dictionary of the checked module.
-
-        Returns:
-            A boolean, true if robolectric, otherwise false.
-        """
-        if not 'path' in data:
-            return False
-        path = data['path'][0]
-        return any(key_dir in path.split(os.sep) for key_dir in _KEY_ROBOTESTS)
-
     def get_dep_modules(self, module_names=None, depth=0):
         """Recursively find dependent modules of the project.
 
@@ -256,24 +237,23 @@
             module_names = self.project_module_names
             self.project_module_names = set()
         for name in module_names:
-            if (name in self.modules_info
+            if (name in self.modules_info.name_to_module_info
                     and name not in self.project_module_names):
-                dep[name] = self.modules_info[name]
+                dep[name] = self.modules_info.name_to_module_info[name]
                 dep[name][constant.KEY_DEPTH] = depth
                 self.project_module_names.add(name)
-                if (constant.KEY_DEP in dep[name]
-                        and dep[name][constant.KEY_DEP]):
-                    children.update(dep[name][constant.KEY_DEP])
+                if (constant.KEY_DEPENDENCIES in dep[name]
+                        and dep[name][constant.KEY_DEPENDENCIES]):
+                    children.update(dep[name][constant.KEY_DEPENDENCIES])
         if children:
             dep.update(self.get_dep_modules(children, depth + 1))
         return dep
 
     @staticmethod
-    def generate_projects(module_info, targets):
+    def generate_projects(targets):
         """Generate a list of projects in one time by a list of module names.
 
         Args:
-            module_info: An Atest module-info instance.
             targets: A list of target modules or project paths from user input,
                      when locating the target, project with matched module name
                      of the target has a higher priority than project path.
@@ -281,7 +261,7 @@
         Returns:
             List: A list of ProjectInfo instances.
         """
-        return [ProjectInfo(module_info, target) for target in targets]
+        return [ProjectInfo(target) for target in targets]
 
     @staticmethod
     def _get_target_name(target, abs_path):
@@ -300,6 +280,6 @@
         Returns:
             A string, the target name.
         """
-        if abs_path == constant.ANDROID_ROOT_PATH:
+        if abs_path == common_util.get_android_root_dir():
             return os.path.basename(abs_path)
         return target
diff --git a/aidegen/lib/project_info_unittest.py b/aidegen/lib/project_info_unittest.py
index e3f5bb7..6964c33 100644
--- a/aidegen/lib/project_info_unittest.py
+++ b/aidegen/lib/project_info_unittest.py
@@ -20,7 +20,7 @@
 import unittest
 from unittest import mock
 
-from aidegen import constant
+from aidegen.lib import common_util
 from aidegen.lib import project_info
 from aidegen.lib.project_info import ProjectInfo
 
@@ -60,13 +60,12 @@
     @mock.patch('atest.module_info.ModuleInfo')
     def test_get_dep_modules(self, mock_module_info):
         """Test get_dep_modules recursively find dependent modules."""
+        mock_module_info.name_to_module_info = _MODULE_INFO
         mock_module_info.is_module.return_value = True
         mock_module_info.get_paths.return_value = ['m1']
         mock_module_info.get_module_names.return_value = ['m1']
-        proj_info = project_info.ProjectInfo(mock_module_info,
-                                             self.args.module_name)
-        proj_info.modules_info = _MODULE_INFO
-        proj_info.dep_modules = proj_info.get_dep_modules()
+        project_info.ProjectInfo.modules_info = mock_module_info
+        proj_info = project_info.ProjectInfo(self.args.module_name)
         self.assertEqual(proj_info.dep_modules, _EXPECT_DEPENDENT_MODULES)
 
     def test_is_a_target_module(self):
@@ -87,29 +86,10 @@
                 'class': ['ROBOLECTRIC']
             }), True)
 
-    def test_is_a_robolectric_module(self):
-        """Test _is_a_robolectric_module with different conditions."""
-        self.assertEqual(ProjectInfo._is_a_robolectric_module({}), False)
-        self.assertEqual(
-            ProjectInfo._is_a_robolectric_module({
-                'path': [uc.TEST_PATH]
-            }), False)
-        self.assertEqual(
-            ProjectInfo._is_a_robolectric_module({
-                'path': ['path/robotests']
-            }), True)
-        self.assertEqual(
-            ProjectInfo._is_a_robolectric_module({
-                'path': ['path/robolectric']
-            }), True)
-        self.assertEqual(
-            ProjectInfo._is_a_robolectric_module({
-                'path': ['robotests/robolectric']
-            }), True)
-
-    def test_get_target_name(self):
+    @mock.patch.object(common_util, 'get_android_root_dir')
+    def test_get_target_name(self, mock_get_root):
         """Test _get_target_name with different conditions."""
-        constant.ANDROID_ROOT_PATH = uc.TEST_DATA_PATH
+        mock_get_root.return_value = uc.TEST_DATA_PATH
         self.assertEqual(
             ProjectInfo._get_target_name(uc.TEST_MODULE, uc.TEST_DATA_PATH),
             os.path.basename(uc.TEST_DATA_PATH))
diff --git a/aidegen/lib/sdk_config.py b/aidegen/lib/sdk_config.py
new file mode 100644
index 0000000..2c73366
--- /dev/null
+++ b/aidegen/lib/sdk_config.py
@@ -0,0 +1,344 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - 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.
+
+"""Config JDK/SDK in jdk.table.xml.
+
+Depends on Linux and Mac there are different config file paths, such as
+jdk.table.xml, template_jdk_file.xml, etc. The information is saved in IdeUtil
+class and it uses SDKConfig class to write JDK/SDK configuration in
+jdk.table.xml.
+
+    Usage example:
+    1.The parameters jdk_table_xml_file, jdk_template_path, default_jdk_path and
+      default_android_sdk_path are generated in IdeUtil class by different OS
+      and IDE. Please reference to ide_util.py for more detail information.
+    2.Configure JDK and Android SDK in jdk.table.xml for IntelliJ and Android
+      Studio.
+    3.Generate the enable_debugger module in IntelliJ.
+
+    sdk_config = SDKConfig(jdk_table_xml_file,
+                           jdk_template_path,
+                           default_jdk_path,
+                           default_android_sdk_path)
+    sdk_config.config_jdk_file()
+    sdk_config.gen_enable_debugger_module()
+"""
+
+from __future__ import absolute_import
+
+import logging
+import os
+import re
+import xml.dom.minidom
+
+from aidegen.lib import common_util
+from aidegen.lib import config
+from aidegen.lib import errors
+from aidegen.lib import project_file_gen
+
+_API_LEVEL = 'api_level'
+_PLATFORMS = 'platforms'
+
+
+class SDKConfig():
+    """SDK config.
+
+    Instance attributes:
+        config_file: The absolute file path of the jdk.table.xml, the file
+                     might not exist.
+        template_jdk_file: The JDK table template file path.
+        jdk_path: The path of JDK in android project.
+        config_exists: A boolean, True if the config_file exists otherwise
+                       False.
+        config_string: A string, it will be written into config_file.
+        xml_dom: An object which contains the xml file parsing result of the
+                 config_file.
+        jdks: An element list with tag named "jdk" in xml_dom.
+
+    Class attrubute:
+        android_sdk_path: The path to the Android SDK, None if the Android SDK
+                          doesn't exist.
+        max_api_level: An integer, parsed from the folder named android-{n}
+                       under Android/Sdk/platforms.
+    """
+
+    android_sdk_path = None
+    max_api_level = 0
+    _TARGET_JDK_NAME_TAG = '<name value="JDK18" />'
+    _COMPONENT_END_TAG = '  </component>'
+    _XML_CONTENT = ('<application>\n  <component name="ProjectJdkTable">\n'
+                    '  </component>\n</application>\n')
+    _TAG_JDK = 'jdk'
+    _TAG_NAME = 'name'
+    _TAG_TYPE = 'type'
+    _TAG_ADDITIONAL = 'additional'
+    _ATTRIBUTE_VALUE = 'value'
+    _ATTRIBUTE_JDK = _TAG_JDK
+    _ATTRIBUTE_SDK = 'sdk'
+    _TYPE_JAVASDK = 'JavaSDK'
+    _NAME_JDK18 = 'JDK18'
+    _TYPE_ANDROID_SDK = 'Android SDK'
+    _INPUT_QUERY_TIMES = 3
+    _API_FOLDER_RE = re.compile(r'platforms/android-(?P<api_level>[\d]+)')
+    _API_LEVEL_RE = re.compile(r'android-(?P<api_level>[\d]+)')
+    _ROOT_DIR = common_util.get_aidegen_root_dir()
+    _TEMPLATE_ANDROID_SDK = os.path.join(
+        _ROOT_DIR, 'templates/jdkTable/part.android.sdk.xml')
+    _ENTER_ANDROID_SDK_PATH = ('\nThe Android SDK folder:{} doesn\'t exist. '
+                               'The debug function "Attach debugger to Android '
+                               'process" is disabled without Android SDK in '
+                               'IntelliJ. Please set it up to enable the '
+                               'function. \nPlease enter the absolute path '
+                               'to Android SDK:')
+    _WARNING_API_LEVEL = ('Cannot find the Android SDK API folder from {}. '
+                          'Please install the SDK platform, otherwise the '
+                          'debug function "Attach debugger to Android process" '
+                          'cannot be enabled in IntelliJ.')
+    _WARNING_PARSE_XML_FAILED = ('The content of jdk.table.xml is not a valid'
+                                 'xml.')
+
+    def __init__(self, config_file, template_jdk_file, jdk_path,
+                 default_android_sdk_path):
+        """SDKConfig initialize.
+
+        Args:
+            config_file: The absolute file path of the jdk.table.xml, the file
+                         might not exist.
+            template_jdk_file: The JDK table template file path.
+            jdk_path: The path of JDK in android project.
+            default_android_sdk_path: The default path to the Android SDK, it
+                                      might not exist.
+        """
+        self.config_file = config_file
+        self.template_jdk_file = template_jdk_file
+        self.jdk_path = jdk_path
+        self.config_exists = os.path.isfile(config_file)
+        self.config_string = self._get_default_config_content()
+        self._parse_xml()
+        SDKConfig.android_sdk_path = default_android_sdk_path
+
+    def _parse_xml(self):
+        """Parse the content of jdk.table.xml to a minidom object."""
+        try:
+            self.xml_dom = xml.dom.minidom.parseString(self.config_string)
+            self.jdks = self.xml_dom.getElementsByTagName(self._TAG_JDK)
+        except (TypeError, AttributeError) as err:
+            print('\n{} {}\n'.format(common_util.COLORED_INFO('Warning:'),
+                                     self._WARNING_PARSE_XML_FAILED))
+            raise errors.InvalidXMLError(err)
+
+    def _get_default_config_content(self):
+        """Get the default content of self.config_file.
+
+        If self.config_file exists, read the content as default. Otherwise, load
+        the content of self.template_jdk_file.
+
+        Returns:
+            String: The content will be written into self.config_file.
+        """
+        if self.config_exists:
+            with open(self.config_file) as config_file:
+                return config_file.read()
+        return self._XML_CONTENT
+
+    def _get_first_element_value(self, node, element_name):
+        """Get the first element if it exists in node.
+
+        Example:
+            The node:
+                <jdk version="2">
+                    <name value="JDK18" />
+                    <type value="JavaSDK" />
+                </jdk>
+            The element_name could be name or type. And the value of name is
+            JDK18, the value of type is JavaSDK.
+
+        Args:
+            node: A minidom object parsed from a xml.
+            element_name: A string of tag name in xml.
+
+        Returns:
+            String: None if the element_name doesn't exist.
+        """
+        elements = node.getElementsByTagName(element_name)
+        return (elements[0].getAttribute(self._ATTRIBUTE_VALUE) if elements
+                else None)
+
+    def _target_jdk_exists(self):
+        """Check if the JDK18 is already set in jdk.table.xml.
+
+        Returns:
+            Boolean: True if the JDK18 exists else False.
+        """
+        for jdk in self.jdks:
+            jdk_type = self._get_first_element_value(jdk, self._TAG_TYPE)
+            jdk_name = self._get_first_element_value(jdk, self._TAG_NAME)
+            if jdk_type == self._TYPE_JAVASDK and jdk_name == self._NAME_JDK18:
+                return True
+        return False
+
+    def _android_sdk_exists(self):
+        """Check if the Android SDK is already set in jdk.table.xml.
+
+        Returns:
+            Boolean: True if the Android SDK configuration exists, otherwise
+                     False.
+        """
+        for jdk in self.jdks:
+            jdk_type = self._get_first_element_value(jdk, self._TAG_TYPE)
+            if jdk_type == self._TYPE_ANDROID_SDK:
+                return True
+        return False
+
+    @staticmethod
+    def _enter_android_sdk_path(input_message):
+        """Ask user input the path to Android SDK."""
+        return input(input_message)
+
+    @classmethod
+    def _get_android_sdk_path(cls):
+        """Get the Android SDK path.
+
+        Returns: The android sdk path if it exists, otherwise None.
+        """
+        # Set the maximum times require user to input the path of Android Sdk.
+        _check_times = cls._INPUT_QUERY_TIMES
+        while not cls._set_max_api_level():
+            if _check_times == 0:
+                cls.android_sdk_path = None
+                break
+            cls.android_sdk_path = cls._enter_android_sdk_path(
+                common_util.COLORED_FAIL(cls._ENTER_ANDROID_SDK_PATH.format(
+                    cls.android_sdk_path)))
+            _check_times -= 1
+        return cls.android_sdk_path
+
+    @classmethod
+    def _set_max_api_level(cls):
+        """Set the max API level from Android SDK folder.
+
+        1. Find the api folder such as android-28 in platforms folder.
+        2. Parse the API level 28 from folder name android-28.
+
+        Returns: An integer, the max api level. Defatult is 0 if there is no
+                 android-{x} folder under android_sdk_path.
+        """
+        platforms_dir = cls._get_platforms_dir_path()
+        if os.path.isdir(platforms_dir):
+            for abspath, _, _ in os.walk(platforms_dir):
+                match_api_folder = cls._API_FOLDER_RE.search(abspath)
+                if match_api_folder:
+                    api_level = int(match_api_folder.group(_API_LEVEL))
+                    if api_level > cls.max_api_level:
+                        cls.max_api_level = api_level
+        return cls.max_api_level
+
+    @classmethod
+    def _get_platforms_dir_path(cls):
+        """Get the platform's dir path from user input Android SDK path.
+
+        Supposed users could input the SDK or platforms path:
+        e.g.
+        /.../Android/Sdk or /.../Android/Sdk/platforms
+        In order to minimize the search range for android-x folder, we check if
+        the last folder name is platforms from user input. If it is, return the
+        path, otherwise return the path which combines user input and platforms
+        folder name.
+
+        Returns: The platforms folder path string.
+        """
+        if cls.android_sdk_path.split(os.sep)[-1] == _PLATFORMS:
+            return cls.android_sdk_path
+        return os.path.join(cls.android_sdk_path, _PLATFORMS)
+
+    def _set_api_level_from_xml(self):
+        """Get the API level from jdk.table.xml."""
+        for jdk in self.jdks:
+            jdk_type = self._get_first_element_value(jdk, self._TAG_TYPE)
+            if jdk_type == self._TYPE_ANDROID_SDK:
+                additionals = jdk.getElementsByTagName(self._TAG_ADDITIONAL)
+                for additional in additionals:
+                    jdk_value = additional.getAttribute(self._ATTRIBUTE_JDK)
+                    sdk_value = additional.getAttribute(self._ATTRIBUTE_SDK)
+                    if jdk_value == self._NAME_JDK18:
+                        find_api_level = self._API_LEVEL_RE.match(sdk_value)
+                        if find_api_level:
+                            self.max_api_level = find_api_level.group(
+                                'api_level')
+                            return True
+        logging.warning('Can\'t set api level from jdk.table.xml.')
+        return False
+
+    def _append_jdk_config_string(self, new_jdk_config):
+        """Add a jdk configuration at the last of <component>.
+
+        Args:
+            new_jdk_config: A string of new jdk configuration.
+        """
+        self.config_string = self.config_string.replace(
+            self._COMPONENT_END_TAG, new_jdk_config + self._COMPONENT_END_TAG)
+
+    def _write_jdk_config_file(self):
+        """Override the jdk.table.xml with the config_string."""
+        try:
+            common_util.file_generate(self.config_file, self.config_string)
+        except (IOError, OSError) as err:
+            logging.warning('Can\'t write the JDK info in %s.\n %s',
+                            self.config_file, err)
+
+    def generate_jdk_config_string(self):
+        """Generate the default jdk configuration."""
+        if not self._target_jdk_exists():
+            self._append_jdk_config_string(
+                common_util.read_file_content(self.template_jdk_file))
+        self.config_string = self.config_string.format(JDKpath=self.jdk_path)
+
+    def generate_sdk_config_string(self):
+        """Generate Android SDK configuration."""
+        if not self._android_sdk_exists():
+            if self._get_android_sdk_path():
+                self._append_jdk_config_string(
+                    common_util.read_file_content(self._TEMPLATE_ANDROID_SDK))
+                self.config_string = self.config_string.format(
+                    ANDROID_SDK_PATH=SDKConfig.android_sdk_path,
+                    API_LEVEL=SDKConfig.max_api_level)
+            else:
+                print('\n{} {}\n'.format(common_util.COLORED_INFO('Warning:'),
+                                         self._WARNING_API_LEVEL.format(
+                                             SDKConfig.max_api_level)))
+
+    def config_jdk_file(self):
+        """Generate the content of config and write it to the jdk.table.xml."""
+        if self._target_jdk_exists() and self._android_sdk_exists():
+            return
+        self.generate_jdk_config_string()
+        self.generate_sdk_config_string()
+        self._write_jdk_config_file()
+        # Parse the content of the updated jdk.table.xml to refresh self.jdks.
+        self._parse_xml()
+
+    def gen_enable_debugger_module(self, module_abspath):
+        """Generate the enable_debugger module under AIDEGen config folder.
+
+        Args:
+            module_abspath: the absolute path of the main project.
+        """
+        if self._set_api_level_from_xml():
+            with config.AidegenConfig() as aconf:
+                if aconf.create_enable_debugger_module(self.max_api_level):
+                    project_file_gen.update_enable_debugger(
+                        module_abspath,
+                        config.AidegenConfig.DEBUG_ENABLED_FILE_PATH)
diff --git a/aidegen/lib/sdk_config_unittest.py b/aidegen/lib/sdk_config_unittest.py
new file mode 100644
index 0000000..80ae3ff
--- /dev/null
+++ b/aidegen/lib/sdk_config_unittest.py
@@ -0,0 +1,243 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019, 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.
+
+"""Unittests for checking the jdk is generated in jdk.table.xml.."""
+
+import os
+import shutil
+import tempfile
+import unittest
+from unittest.mock import patch
+
+from aidegen import unittest_constants
+from aidegen.lib import common_util
+from aidegen.lib import sdk_config
+
+
+# pylint: disable=protected-access
+# pylint: disable=invalid-name
+class SDKConfigUnittests(unittest.TestCase):
+    """Unit tests for sdk_config.py"""
+    _JDK_FILE_NAME = 'jdk.table.xml'
+    _JDK_SAMPLE = os.path.join(unittest_constants.TEST_DATA_PATH,
+                               'jdk_table_xml', 'jdk18.xml')
+    _JDK_SAMPLE2 = os.path.join(unittest_constants.TEST_DATA_PATH,
+                                'jdk_table_xml', 'jdk_nonexistent.xml')
+    _JDK_SAMPLE3 = os.path.join(unittest_constants.TEST_DATA_PATH,
+                                'jdk_table_xml', 'android_sdk.xml')
+    _JDK_SAMPLE4 = os.path.join(unittest_constants.TEST_DATA_PATH,
+                                'jdk_table_xml', 'android_sdk_nonexistent.xml')
+    _JDK_TEMPLATE = os.path.join(common_util.get_aidegen_root_dir(),
+                                 'templates', 'jdkTable', 'part.jdk.table.xml')
+    _JDK_PATH = os.path.join('/path', 'to', 'android', 'root',
+                             'prebuilts', 'jdk', 'jdk8', 'linux-x86')
+    _JDK_OTHER_CONTENT = """<application>
+  <component name="ProjectJdkTable">
+    <jdk version="2">
+      <name value="JDK_OTHER" />
+      <type value="JavaSDK" />
+    </jdk>
+  </component>
+</application>
+"""
+    _NONEXISTENT_ANDROID_SDK_PATH = os.path.join('/path', 'to', 'Android',
+                                                 'Sdk')
+    _DEFAULT_ANDROID_SDK_PATH = os.path.join(unittest_constants.TEST_DATA_PATH,
+                                             'Android/Sdk')
+    _CUSTOM_ANDROID_SDK_PATH = os.path.join(unittest_constants.TEST_DATA_PATH,
+                                            'Android/custom/sdk')
+
+    def test_generate_jdk_config(self):
+        """Test generating jdk config."""
+        expected_content = ''
+        tmp_folder = tempfile.mkdtemp()
+        config_file = os.path.join(tmp_folder, self._JDK_FILE_NAME)
+        try:
+            with open(self._JDK_SAMPLE) as sample:
+                expected_content = sample.read()
+            jdk = sdk_config.SDKConfig(config_file,
+                                       self._JDK_TEMPLATE,
+                                       self._JDK_PATH,
+                                       self._NONEXISTENT_ANDROID_SDK_PATH)
+            jdk.generate_jdk_config_string()
+            self.assertEqual(jdk.config_string, expected_content)
+        finally:
+            shutil.rmtree(tmp_folder)
+
+    def test_jdk_config_no_change(self):
+        """The config file exists and the JDK18 also exists.
+
+        In this case, there is nothing to do, make sure the config content is
+        not changed.
+        """
+        expected_content = ''
+        tmp_folder = tempfile.mkdtemp()
+        config_file = os.path.join(tmp_folder, self._JDK_FILE_NAME)
+        try:
+            with open(self._JDK_SAMPLE) as sample:
+                expected_content = sample.read()
+            # Reset the content of config file.
+            with open(config_file, 'w') as cf:
+                cf.write(expected_content)
+            jdk = sdk_config.SDKConfig(config_file,
+                                       self._JDK_TEMPLATE,
+                                       self._JDK_PATH,
+                                       self._DEFAULT_ANDROID_SDK_PATH)
+            jdk.generate_jdk_config_string()
+            self.assertEqual(jdk.config_string, expected_content)
+        finally:
+            shutil.rmtree(tmp_folder)
+
+    def test_append_jdk_config(self):
+        """The config file exists, test on the JDK18 does not exist."""
+        expected_content = ''
+        tmp_folder = tempfile.mkdtemp()
+        config_file = os.path.join(tmp_folder, self._JDK_FILE_NAME)
+        try:
+            with open(self._JDK_SAMPLE2) as sample:
+                expected_content = sample.read()
+            # Reset the content of config file.
+            with open(config_file, 'w') as cf:
+                cf.write(self._JDK_OTHER_CONTENT)
+            jdk = sdk_config.SDKConfig(config_file,
+                                       self._JDK_TEMPLATE,
+                                       self._JDK_PATH,
+                                       self._NONEXISTENT_ANDROID_SDK_PATH)
+            jdk.generate_jdk_config_string()
+            self.assertEqual(jdk.config_string, expected_content)
+        finally:
+            shutil.rmtree(tmp_folder)
+
+    def test_android_sdk_exist(self):
+        """Test to check the Android SDK configuration does exist."""
+        expected_content = ''
+        tmp_folder = tempfile.mkdtemp()
+        config_file = os.path.join(tmp_folder, self._JDK_FILE_NAME)
+        try:
+            with open(self._JDK_SAMPLE3) as sample:
+                expected_content = sample.read()
+            with open(config_file, 'w') as cf:
+                cf.write(expected_content)
+            jdk = sdk_config.SDKConfig(config_file,
+                                       self._JDK_TEMPLATE,
+                                       self._JDK_PATH,
+                                       self._NONEXISTENT_ANDROID_SDK_PATH)
+            self.assertEqual(jdk._android_sdk_exists(), True)
+        finally:
+            shutil.rmtree(tmp_folder)
+
+    def test_android_sdk_not_exist(self):
+        """Test to check the Android SDK configuration doesn't exist."""
+        expected_content = ''
+        tmp_folder = tempfile.mkdtemp()
+        config_file = os.path.join(tmp_folder, self._JDK_FILE_NAME)
+        try:
+            with open(self._JDK_SAMPLE4) as sample:
+                expected_content = sample.read()
+            with open(config_file, 'w') as cf:
+                cf.write(expected_content)
+            jdk = sdk_config.SDKConfig(config_file,
+                                       self._JDK_TEMPLATE,
+                                       self._JDK_PATH,
+                                       self._NONEXISTENT_ANDROID_SDK_PATH)
+            self.assertEqual(jdk._android_sdk_exists(), False)
+        finally:
+            shutil.rmtree(tmp_folder)
+
+    def test_get_android_sdk_path(self):
+        """Test get default Android SDK path."""
+        expected_content = self._DEFAULT_ANDROID_SDK_PATH
+        jdk = sdk_config.SDKConfig(self._JDK_SAMPLE4,
+                                   self._JDK_TEMPLATE,
+                                   self._JDK_PATH,
+                                   self._DEFAULT_ANDROID_SDK_PATH)
+        jdk._get_android_sdk_path()
+        self.assertEqual(jdk.android_sdk_path, expected_content)
+
+    def test_get_custom_android_sdk_path(self):
+        """Test get custom Android SDK path."""
+        api_folder = os.path.join(self._CUSTOM_ANDROID_SDK_PATH, 'platforms',
+                                  'android-28')
+        os.makedirs(api_folder)
+        user_input = [self._CUSTOM_ANDROID_SDK_PATH]
+        sdk_config.SDKConfig.max_api_level = 0
+        expected_path = self._CUSTOM_ANDROID_SDK_PATH
+        try:
+            with patch('builtins.input', side_effect=user_input):
+                jdk = sdk_config.SDKConfig(self._JDK_SAMPLE4,
+                                           self._JDK_TEMPLATE,
+                                           self._JDK_PATH,
+                                           self._NONEXISTENT_ANDROID_SDK_PATH)
+                jdk._get_android_sdk_path()
+                self.assertEqual(jdk.android_sdk_path, expected_path)
+        finally:
+            shutil.rmtree(os.path.dirname(self._CUSTOM_ANDROID_SDK_PATH))
+
+    def test_set_max_api_level(self):
+        """Test set max API level."""
+        expected_api_level = 28
+        jdk = sdk_config.SDKConfig(self._JDK_SAMPLE3,
+                                   self._JDK_TEMPLATE,
+                                   self._JDK_PATH,
+                                   self._DEFAULT_ANDROID_SDK_PATH)
+        jdk._set_max_api_level()
+        self.assertEqual(jdk.max_api_level, expected_api_level)
+
+        # Test Android SDK platforms folder doesn't exist.
+        sdk_config.SDKConfig.max_api_level = 0
+        expected_api_level = 0
+        jdk = sdk_config.SDKConfig(self._JDK_SAMPLE3,
+                                   self._JDK_TEMPLATE,
+                                   self._JDK_PATH,
+                                   self._NONEXISTENT_ANDROID_SDK_PATH)
+        jdk._set_max_api_level()
+        self.assertEqual(jdk.max_api_level, expected_api_level)
+
+    def test_generate_android_sdk_config(self):
+        """Test generating Android SDK config."""
+        expected_content = ''
+        tmp_folder = tempfile.mkdtemp()
+        config_file = os.path.join(tmp_folder, self._JDK_FILE_NAME)
+        try:
+            with open(self._JDK_SAMPLE3) as sample:
+                expected_content = sample.read().replace(
+                    self._NONEXISTENT_ANDROID_SDK_PATH,
+                    self._DEFAULT_ANDROID_SDK_PATH)
+            jdk = sdk_config.SDKConfig(config_file,
+                                       self._JDK_TEMPLATE,
+                                       self._JDK_PATH,
+                                       self._NONEXISTENT_ANDROID_SDK_PATH)
+            user_input = [self._DEFAULT_ANDROID_SDK_PATH]
+            with patch('builtins.input', side_effect=user_input):
+                jdk.generate_sdk_config_string()
+                self.assertEqual(jdk.config_string, expected_content)
+        finally:
+            shutil.rmtree(tmp_folder)
+
+    def test_get_platforms_dir_path(self):
+        """Test _get_platforms_dir_path."""
+        expected_path = '/a/b/platforms'
+        sdk_config.SDKConfig.android_sdk_path = '/a/b'
+        test_path = sdk_config.SDKConfig._get_platforms_dir_path()
+        self.assertEqual(test_path, expected_path)
+
+        sdk_config.SDKConfig.android_sdk_path = '/a/b/platforms'
+        test_path = sdk_config.SDKConfig._get_platforms_dir_path()
+        self.assertEqual(test_path, expected_path)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/aidegen/lib/source_locator.py b/aidegen/lib/source_locator.py
index b032915..b74b49d 100644
--- a/aidegen/lib/source_locator.py
+++ b/aidegen/lib/source_locator.py
@@ -28,45 +28,43 @@
 from aidegen.lib import common_util
 from aidegen.lib.common_util import COLORED_INFO
 from atest import atest_utils
-from atest import constants
 
 # Parse package name from the package declaration line of a java.
 # Group matches "foo.bar" of line "package foo.bar;" or "package foo.bar"
 _PACKAGE_RE = re.compile(r'\s*package\s+(?P<package>[^(;|\s)]+)\s*', re.I)
 
 _ANDROID_SUPPORT_PATH_KEYWORD = 'prebuilts/sdk/current/'
-_JAR = '.jar'
-_TARGET_LIBS = [_JAR]
+# File extensions
+_JAR_EXT = '.jar'
+_JAVA_EXT = '.java'
+_KOTLIN_EXT = '.kt'
+_SRCJAR_EXT = '.srcjar'
+
+_TARGET_LIBS = [_JAR_EXT]
+_TARGET_FILES = [_JAVA_EXT, _KOTLIN_EXT]
 _JARJAR_RULES_FILE = 'jarjar-rules.txt'
-_JAVA = '.java'
-_KOTLIN = '.kt'
-_TARGET_FILES = [_JAVA, _KOTLIN]
-_KEY_INSTALLED = 'installed'
 _KEY_JARJAR_RULES = 'jarjar_rules'
 _KEY_JARS = 'jars'
-_KEY_PATH = 'path'
-_KEY_SRCS = 'srcs'
 _KEY_TESTS = 'tests'
-_SRCJAR = '.srcjar'
-_AAPT2_DIR = 'out/target/common/obj/APPS/%s_intermediates/aapt2'
-_AAPT2_SRCJAR = 'out/target/common/obj/APPS/%s_intermediates/aapt2.srcjar'
+_NAME_AAPT2 = 'aapt2'
+_TARGET_R_JAR = 'R.jar'
+_TARGET_AAPT2_SRCJAR = _NAME_AAPT2 + _SRCJAR_EXT
+_TARGET_BUILD_FILES = [_TARGET_AAPT2_SRCJAR, _TARGET_R_JAR]
 _IGNORE_DIRS = [
     # The java files under this directory have to be ignored because it will
     # cause duplicated classes by libcore/ojluni/src/main/java.
     'libcore/ojluni/src/lambda/java'
 ]
 _DIS_ROBO_BUILD_ENV_VAR = {'DISABLE_ROBO_RUN_TESTS': 'true'}
-_SKIP_BUILD_WARN = (
-    'You choose "--skip-build". Skip building jar and module might increase '
-    'the risk of the absence of some jar or R/AIDL/logtags java files and '
-    'cause the red lines to appear in IDE tool.')
+# When we use atest_utils.build(), there is a command length limit on
+# soong_ui.bash. We reserve 5000 characters for rewriting the command line
+# in soong_ui.bash.
+_CMD_LENGTH_BUFFER = 5000
+# For each argument, it need a space to separate following argument.
+_BLANK_SIZE = 1
 
 
-# TODO(b/132831520): Remove default IDE, source locator should be neutral. It
-#                    shouldn't set default IDE.
-def multi_projects_locate_source(projects, verbose, depth,
-                                 ide_name=constant.IDE_INTELLIJ,
-                                 skip_build=True):
+def multi_projects_locate_source(projects, verbose):
     """Locate the paths of dependent source folders and jar files with projects.
 
     Args:
@@ -74,20 +72,14 @@
                   as project relative path, project real path, project
                   dependencies.
         verbose: A boolean, if true displays full build output.
-        depth: An integer shows the depth of module dependency referenced by
-               source. Zero means the max module depth.
-        ide_name: A string stands for the IDE name, default is IntelliJ.
-        skip_build: A boolean default to true, if true skip building jar and
-                    srcjar files, otherwise build them.
     """
-    if skip_build:
-        print('\n{} {}\n'.format(COLORED_INFO('Warning:'), _SKIP_BUILD_WARN))
     for project in projects:
-        locate_source(project, verbose, depth, ide_name, build=not skip_build)
+        locate_source(project, verbose, project.config.depth,
+                      project.config.ide_name,
+                      build=not project.config.is_skip_build)
 
 
-def locate_source(project, verbose, depth, ide_name=constant.IDE_INTELLIJ,
-                  build=True):
+def locate_source(project, verbose, depth, ide_name, build=True):
     """Locate the paths of dependent source folders and jar files.
 
     Try to reference source folder path as dependent module unless the
@@ -142,35 +134,84 @@
         module.locate_sources_path()
         dependencies['source_folder_path'].update(module.src_dirs)
         dependencies['test_folder_path'].update(module.test_dirs)
+        dependencies['r_java_path'].update(module.r_java_paths)
+        dependencies['srcjar_path'].update(module.srcjar_paths)
         _append_jars_as_dependencies(dependencies, module)
         if module.build_targets:
             rebuild_targets |= module.build_targets
     if rebuild_targets:
         if build:
-            _build_dependencies(verbose, rebuild_targets)
+            _batch_build_dependencies(verbose, rebuild_targets)
             locate_source(project, verbose, depth, ide_name, build=False)
         else:
-            logging.warning('Jar files or modules build failed:\n\t%s.',
+            logging.warning('Jar or srcjar files build failed:\n\t%s.',
                             '\n\t'.join(rebuild_targets))
 
 
-def _build_dependencies(verbose, rebuild_targets):
-    """Build the jar or srcjar files of the modules if it don't exist.
+def _batch_build_dependencies(verbose, rebuild_targets):
+    """Batch build the jar or srcjar files of the modules if they don't exist.
+
+    Command line has the max length limit, MAX_ARG_STRLEN, and
+    MAX_ARG_STRLEN = (PAGE_SIZE * 32).
+    If the build command is longer than MAX_ARG_STRLEN, this function will
+    separate the rebuild_targets into chunks with size less or equal to
+    MAX_ARG_STRLEN to make sure it can be built successfully.
 
     Args:
         verbose: A boolean, if true displays full build output.
-        rebuild_targets: A list of jar or srcjar files which do not exist.
+        rebuild_targets: A set of jar or srcjar files which do not exist.
     """
-    logging.info(('Ready to build the jar or srcjar files.'))
-    targets = ['-k']
-    targets.extend(list(rebuild_targets))
-    if not atest_utils.build(targets, verbose, _DIS_ROBO_BUILD_ENV_VAR):
+    logging.info('Ready to build the jar or srcjar files. Files count = %s',
+                 str(len(rebuild_targets)))
+    arg_max = os.sysconf("SC_PAGE_SIZE") * 32 - _CMD_LENGTH_BUFFER
+    rebuild_targets = list(rebuild_targets)
+    for start, end in iter(_separate_build_targets(
+            rebuild_targets, arg_max)):
+        _build_target(rebuild_targets[start: end], verbose)
+
+
+def _build_target(targets, verbose):
+    """Build the jar or srcjar files.
+
+    Use -k to keep going when some targets can't be built or build failed.
+    Use -j to speed up building.
+
+    Args:
+        targets: A list of jar or srcjar files which need to build.
+        verbose: A boolean, if true displays full build output.
+    """
+    build_cmd = ['-k', '-j']
+    build_cmd.extend(list(targets))
+    if not atest_utils.build(build_cmd, verbose, _DIS_ROBO_BUILD_ENV_VAR):
         message = ('Build failed!\n{}\nAIDEGen will proceed but dependency '
                    'correctness is not guaranteed if not all targets being '
                    'built successfully.'.format('\n'.join(targets)))
         print('\n{} {}\n'.format(COLORED_INFO('Warning:'), message))
 
 
+def _separate_build_targets(build_targets, max_length):
+    """Separate the build_targets by limit the command size to max command
+    length.
+
+    Args:
+        build_targets: A list to be separated.
+        max_length: The max number of each build command length.
+
+    Yields:
+        The start index and end index of build_targets.
+    """
+    arg_len = 0
+    first_item_index = 0
+    for i, item in enumerate(build_targets):
+        arg_len = arg_len + len(item) + _BLANK_SIZE
+        if arg_len > max_length:
+            yield first_item_index, i
+            first_item_index = i
+            arg_len = len(item) + _BLANK_SIZE
+    if first_item_index < len(build_targets):
+        yield first_item_index, len(build_targets)
+
+
 def _generate_moduledata(module_name, module_data, ide_name, project_relpath,
                          depth):
     """Generate a module class to collect dependencies in IntelliJ or Eclipse.
@@ -187,10 +228,8 @@
         A ModuleData class.
     """
     if ide_name == constant.IDE_ECLIPSE:
-        module = EclipseModuleData(module_name, module_data, project_relpath)
-    else:
-        module = ModuleData(module_name, module_data, depth)
-    return module
+        return EclipseModuleData(module_name, module_data, project_relpath)
+    return ModuleData(module_name, module_data, depth)
 
 
 def _append_jars_as_dependencies(dependent_data, module):
@@ -206,9 +245,9 @@
         for jar in list(module.jar_files):
             dependent_data['jar_module_path'].update({jar: module.module_path})
     # Collecting the jar files of default core modules as dependencies.
-    if constant.KEY_DEP in module.module_data:
+    if constant.KEY_DEPENDENCIES in module.module_data:
         dependent_data['jar_path'].update([
-            x for x in module.module_data[constant.KEY_DEP]
+            x for x in module.module_data[constant.KEY_DEPENDENCIES]
             if common_util.is_target(x, _TARGET_LIBS)
         ])
 
@@ -224,6 +263,9 @@
         src_dirs: A set to keep the unique source folder relative paths.
         test_dirs: A set to keep the unique test folder relative paths.
         jar_files: A set to keep the unique jar file relative paths.
+        r_java_paths: A set to keep the R folder paths to use in Eclipse.
+        srcjar_paths: A set to keep the srcjar source root paths to use in
+                      IntelliJ.
         referenced_by_jar: A boolean to check if the module is referenced by a
                            jar file.
         build_targets: A set to keep the unique build target jar or srcjar file
@@ -269,6 +311,8 @@
         self.src_dirs = set()
         self.test_dirs = set()
         self.jar_files = set()
+        self.r_java_paths = set()
+        self.srcjar_paths = set()
         self.referenced_by_jar = False
         self.build_targets = set()
         self.missing_jars = set()
@@ -291,41 +335,75 @@
         """
         return self.module_depth == 0
 
-    def _is_module_in_apps(self):
-        """Check if the current module is under packages/apps."""
-        _apps_path = os.path.join('packages', 'apps')
-        return self.module_path.startswith(_apps_path)
-
     def _collect_r_srcs_paths(self):
         """Collect the source folder of R.java.
 
-        For modules under packages/apps, check if exists an intermediates
-        directory which contains R.java. If it does not exist, build the
-        aapt2.srcjar of the module to generate. Build system will finally copy
-        the R.java from a intermediates directory to the central R directory
-        after building successfully. So set the central R directory
-        out/target/common/R as a default source folder in IntelliJ.
+        Check if the path of aapt2.srcjar or R.jar exists, which is the value of
+        key "srcjars" in module_data. If the path of both 2 cases doesn't exist,
+        build it onto an intermediates directory.
+
+        For IntelliJ, we can set the srcjar file as a source root for
+        dependency. For Eclipse, we still use the R folder as dependencies until
+        we figure out how to set srcjar file as dependency.
+        # TODO(b/135594800): Set aapt2.srcjar or R.jar as a dependency in
+                             Eclipse.
         """
         if (self._is_app_module() and self._is_target_module() and
-                self._is_module_in_apps()):
-            # The directory contains R.java for apps in packages/apps.
-            r_src_dir = _AAPT2_DIR % self.module_name
-            if not os.path.exists(common_util.get_abs_path(r_src_dir)):
-                self.build_targets.add(_AAPT2_SRCJAR % self.module_name)
-            # In case the central R folder been deleted, uses the intermediate
-            # folder as the dependency to R.java.
-            self.src_dirs.add(r_src_dir)
-        # Add the central R as a default source folder.
-        self.src_dirs.add('out/target/common/R')
+                self._check_key(constant.KEY_SRCJARS)):
+            for srcjar in self.module_data[constant.KEY_SRCJARS]:
+                if not os.path.exists(common_util.get_abs_path(srcjar)):
+                    self.build_targets.add(srcjar)
+                self._collect_srcjar_path(srcjar)
+                r_dir = self._get_r_dir(srcjar)
+                if r_dir:
+                    self.r_java_paths.add(r_dir)
+
+    def _collect_srcjar_path(self, srcjar):
+        """Collect the source folders from a srcjar path.
+
+        Set the aapt2.srcjar or R.jar as source root:
+        Case aapt2.srcjar:
+            The source path string is
+            out/.../Bluetooth_intermediates/aapt2.srcjar
+            The source content descriptor is
+            out/.../Bluetooth_intermediates/aapt2.srcjar!/.
+        Case R.jar:
+            The source path string is out/soong/.../gen/R.jar.
+            The source content descriptor is out/soong/.../gen/R.jar!/.
+
+        Args:
+            srcjar: A file path string relative to ANDROID_BUILD_TOP, the build
+                    target of the module to generate R.java.
+        """
+        if os.path.basename(srcjar) in _TARGET_BUILD_FILES:
+            self.srcjar_paths.add('%s!/' % srcjar)
+
+    @staticmethod
+    def _get_r_dir(srcjar):
+        """Get the source folder of R.java for Eclipse.
+
+        Args:
+            srcjar: A file path string, the build target of the module to
+                    generate R.java.
+
+        Returns:
+            A relative source folder path string, and return None if the target
+            file name is not aapt2.srcjar or R.jar.
+        """
+        target_folder, target_file = os.path.split(srcjar)
+        if target_file == _TARGET_AAPT2_SRCJAR:
+            return os.path.join(target_folder, _NAME_AAPT2)
+        if target_file == _TARGET_R_JAR:
+            return os.path.join(target_folder, _NAME_AAPT2, 'R')
+        return None
 
     def _init_module_path(self):
         """Inintialize self.module_path."""
-        self.module_path = (self.module_data[_KEY_PATH][0]
-                            if _KEY_PATH in self.module_data
-                            and self.module_data[_KEY_PATH] else '')
+        self.module_path = (self.module_data[constant.KEY_PATH][0]
+                            if self._check_key(constant.KEY_PATH) else '')
 
     def _init_module_depth(self, depth):
-        """Inintialize module depth's settings.
+        """Initialize module depth's settings.
 
         Set the module's depth from module info when user have -d parameter.
         Set the -d value from user input, default to 0.
@@ -348,16 +426,20 @@
 
     def _check_jars_exist(self):
         """Check if jars exist."""
-        return _KEY_JARS in self.module_data and self.module_data[_KEY_JARS]
+        return self._check_key(_KEY_JARS)
+
+    def _check_classes_jar_exist(self):
+        """Check if classes_jar exist."""
+        return self._check_key(constant.KEY_CLASSES_JAR)
 
     def _collect_srcs_paths(self):
         """Collect source folder paths in src_dirs from module_data['srcs']."""
-        if self._check_key(_KEY_SRCS):
+        if self._check_key(constant.KEY_SRCS):
             scanned_dirs = set()
-            for src_item in self.module_data[_KEY_SRCS]:
+            for src_item in self.module_data[constant.KEY_SRCS]:
                 src_dir = None
                 src_item = os.path.relpath(src_item)
-                if src_item.endswith(_SRCJAR):
+                if src_item.endswith(_SRCJAR_EXT):
                     self._append_jar_from_installed(self.specific_soong_path)
                 elif common_util.is_target(src_item, _TARGET_FILES):
                     # Only scan one java file in each source directories.
@@ -386,10 +468,6 @@
             src_dir: the directory to be added.
         """
         if not any(path in src_dir for path in _IGNORE_DIRS):
-            # Build the module if the source path not exists. The java is
-            # normally generated for AIDL or logtags file.
-            if not os.path.exists(common_util.get_abs_path(src_dir)):
-                self.build_targets.add(self.module_name)
             if self._is_test_module(src_dir):
                 self.test_dirs.add(src_dir)
             else:
@@ -407,26 +485,12 @@
         """
         return _KEY_TESTS in src_dir.split(os.sep)
 
-    # pylint: disable=inconsistent-return-statements
-    @staticmethod
-    def _get_source_folder(java_file):
+    def _get_source_folder(self, java_file):
         """Parsing a java to get the package name to filter out source path.
 
-        There are 3 steps to get the source path from a java.
-        1. Parsing a java to get package name.
-           For example:
-               The java_file is:path/to/the/module/src/main/java/com/android/
-                                first.java
-               The package name of java_file is com.android.
-        2. Transfer package name to package path:
-           For example:
-               The package path of com.android is com/android.
-        3. Remove the package path and file name from the java path.
-           For example:
-               The path after removing package path and file name is
-               path/to/the/module/src/main/java.
-        As a result, path/to/the/module/src/main/java is the source path parsed
-        from path/to/the/module/src/main/java/com/android/first.java.
+        Args:
+            java_file: A string, the java file with relative path.
+                       e.g. path/to/the/java/file.java
 
         Returns:
             source_folder: A string of path to source folder(e.g. src/main/java)
@@ -434,14 +498,73 @@
         """
         abs_java_path = common_util.get_abs_path(java_file)
         if os.path.exists(abs_java_path):
-            with open(abs_java_path) as data:
-                for line in data.read().splitlines():
-                    match = _PACKAGE_RE.match(line)
-                    if match:
-                        package_name = match.group('package')
-                        package_path = package_name.replace(os.extsep, os.sep)
-                        source_folder, _, _ = java_file.rpartition(package_path)
-                        return source_folder.strip(os.sep)
+            package_name = self._get_package_name(abs_java_path)
+            if package_name:
+                return self._parse_source_path(java_file, package_name)
+        return None
+
+    @staticmethod
+    def _parse_source_path(java_file, package_name):
+        """Parse the source path by filter out the package name.
+
+        Case 1:
+        java file: a/b/c/d/e.java
+        package name: c.d
+        The source folder is a/b.
+
+        Case 2:
+        java file: a/b/c.d/e.java
+        package name: c.d
+        The source folder is a/b.
+
+        Case 3:
+        java file: a/b/c/d/e.java
+        package name: x.y
+        The source folder is a/b/c/d.
+
+        Case 4:
+        java file: a/b/c.d/e/c/d/f.java
+        package name: c.d
+        The source folder is a/b/c.d/e.
+
+        Case 5:
+        java file: a/b/c.d/e/c.d/e/f.java
+        package name: c.d.e
+        The source folder is a/b/c.d/e.
+
+        Args:
+            java_file: A string of the java file relative path.
+            package_name: A string of the java file's package name.
+
+        Returns:
+            A string, the source folder path.
+        """
+        java_file_name = os.path.basename(java_file)
+        pattern = r'%s/%s$' % (package_name, java_file_name)
+        search_result = re.search(pattern, java_file)
+        if search_result:
+            return java_file[:search_result.start()].strip(os.sep)
+        return os.path.dirname(java_file)
+
+    @staticmethod
+    def _get_package_name(abs_java_path):
+        """Get the package name by parsing a java file.
+
+        Args:
+            abs_java_path: A string of the java file with absolute path.
+                           e.g. /root/path/to/the/java/file.java
+
+        Returns:
+            package_name: A string of package name.
+        """
+        package_name = None
+        with open(abs_java_path) as data:
+            for line in data.read().splitlines():
+                match = _PACKAGE_RE.match(line)
+                if match:
+                    package_name = match.group('package')
+                    break
+        return package_name
 
     def _append_jar_file(self, jar_path):
         """Append a path to the jar file into self.jar_files if it's exists.
@@ -459,6 +582,13 @@
             else:
                 self.missing_jars.add(jar_path)
             return True
+        return False
+
+    def _append_classes_jar(self):
+        """Append the jar file as dependency for prebuilt modules."""
+        for jar in self.module_data[constant.KEY_CLASSES_JAR]:
+            if self._append_jar_file(jar):
+                break
 
     def _append_jar_from_installed(self, specific_dir=None):
         """Append a jar file's path to the list of jar_files with matching
@@ -471,9 +601,8 @@
         Args:
             specific_dir: A string of path.
         """
-        if (_KEY_INSTALLED in self.module_data
-                and self.module_data[_KEY_INSTALLED]):
-            for jar in self.module_data[_KEY_INSTALLED]:
+        if self._check_key(constant.KEY_INSTALLED):
+            for jar in self.module_data[constant.KEY_INSTALLED]:
                 if specific_dir and not jar.startswith(specific_dir):
                     continue
                 if self._append_jar_file(jar):
@@ -498,15 +627,15 @@
         },
         Path to the jar file is prebuilts/misc/common/asm/asm-6.0.jar.
         """
-        if _KEY_JARS in self.module_data and self.module_data[_KEY_JARS]:
+        if self._check_key(_KEY_JARS):
             for jar_name in self.module_data[_KEY_JARS]:
-                if self._check_key(_KEY_INSTALLED):
+                if self._check_key(constant.KEY_INSTALLED):
                     self._append_jar_from_installed()
                 else:
                     jar_path = os.path.join(self.module_path, jar_name)
                     jar_abs = common_util.get_abs_path(jar_path)
-                    if not os.path.isfile(
-                            jar_abs) and jar_name.endswith('prebuilt.jar'):
+                    if not os.path.isfile(jar_abs) and jar_name.endswith(
+                            'prebuilt.jar'):
                         rel_path = self._get_jar_path_from_prebuilts(jar_name)
                         if rel_path:
                             jar_path = rel_path
@@ -540,12 +669,12 @@
         """
         rel_path = ''
         search = os.sep.join(
-            [constant.ANDROID_ROOT_PATH, 'prebuilts/**', jar_name])
+            [common_util.get_android_root_dir(), 'prebuilts/**', jar_name])
         results = glob.glob(search, recursive=True)
         if results:
             jar_abs = results[0]
             rel_path = os.path.relpath(
-                jar_abs, os.environ.get(constants.ANDROID_BUILD_TOP, os.sep))
+                jar_abs, common_util.get_android_root_dir())
         return rel_path
 
     def locate_sources_path(self):
@@ -563,7 +692,14 @@
             # If there is no source/tests folder of the module, reference the
             # module by jar.
             if not self.src_dirs and not self.test_dirs:
-                self._append_jar_from_installed()
+                # Add the classes.jar from the classes_jar attribute as
+                # dependency if it exists. If the classes.jar doesn't exist,
+                # find the jar file from the installed attribute and add the jar
+                # as dependency.
+                if self._check_classes_jar_exist():
+                    self._append_classes_jar()
+                else:
+                    self._append_jar_from_installed()
             self._collect_r_srcs_paths()
         if self.referenced_by_jar and self.missing_jars:
             self.build_targets |= self.missing_jars
@@ -628,5 +764,7 @@
             self._append_jar_from_installed(self.specific_soong_path)
         elif self._check_jars_exist():
             self._set_jars_jarfile()
+        elif self._check_classes_jar_exist():
+            self._append_classes_jar()
         else:
             self._append_jar_from_installed()
diff --git a/aidegen/lib/source_locator_unittest.py b/aidegen/lib/source_locator_unittest.py
index 99a2e9e..6cd0fce 100644
--- a/aidegen/lib/source_locator_unittest.py
+++ b/aidegen/lib/source_locator_unittest.py
@@ -46,23 +46,40 @@
 class SourceLocatorUnittests(unittest.TestCase):
     """Unit tests for source_locator.py"""
 
-    def test_collect_srcs_paths(self):
+    @mock.patch('aidegen.lib.common_util.get_android_root_dir')
+    def test_collect_srcs_paths(self, mock_android_root_dir):
         """Test _collect_srcs_paths create the source path list."""
         result_source = set(['packages/apps/test/src/main/java'])
         result_test = set(['packages/apps/test/tests'])
-        constant.ANDROID_ROOT_PATH = uc.TEST_DATA_PATH
+        mock_android_root_dir.return_value = uc.TEST_DATA_PATH
         module_data = source_locator.ModuleData(_MODULE_NAME, _MODULE_INFO,
                                                 _MODULE_DEPTH)
         module_data._collect_srcs_paths()
         self.assertEqual(module_data.src_dirs, result_source)
         self.assertEqual(module_data.test_dirs, result_test)
 
-    def test_get_source_folder(self):
+    def test_get_package_name(self):
+        """test get the package name from a java file."""
+        result_package_name = 'com.android'
+        test_java = os.path.join(uc.TEST_DATA_PATH, _MODULE_PATH,
+                                 'src/main/java/com/android/java.java')
+        package_name = source_locator.ModuleData._get_package_name(test_java)
+        self.assertEqual(package_name, result_package_name)
+
+        # Test on java file with no package name.
+        result_package_name = None
+        test_java = os.path.join(uc.TEST_DATA_PATH, _MODULE_PATH,
+                                 'src/main/java/com/android/no_package.java')
+        package_name = source_locator.ModuleData._get_package_name(test_java)
+        self.assertEqual(package_name, result_package_name)
+
+    @mock.patch('aidegen.lib.common_util.get_android_root_dir')
+    def test_get_source_folder(self, mock_android_root_dir):
         """Test _get_source_folder process."""
         # Test for getting the source path by parse package name from a java.
         test_java = 'packages/apps/test/src/main/java/com/android/java.java'
         result_source = 'packages/apps/test/src/main/java'
-        constant.ANDROID_ROOT_PATH = uc.TEST_DATA_PATH
+        mock_android_root_dir.return_value = uc.TEST_DATA_PATH
         module_data = source_locator.ModuleData(_MODULE_NAME, _MODULE_INFO,
                                                 _MODULE_DEPTH)
         src_path = module_data._get_source_folder(test_java)
@@ -75,16 +92,122 @@
 
         # Return path is None on the java file without package name.
         test_java = ('packages/apps/test/src/main/java/com/android/'
-                     'wrong_package.java')
+                     'no_package.java')
         src_path = module_data._get_source_folder(test_java)
         self.assertEqual(src_path, None)
 
-    def test_append_jar_file(self):
+    def test_get_r_dir(self):
+        """Test get_r_dir."""
+        module_data = source_locator.ModuleData(_MODULE_NAME, _MODULE_INFO,
+                                                _MODULE_DEPTH)
+        # Test for aapt2.srcjar
+        test_aapt2_srcjar = 'a/aapt2.srcjar'
+        expect_result = 'a/aapt2'
+        r_dir = module_data._get_r_dir(test_aapt2_srcjar)
+        self.assertEqual(r_dir, expect_result)
+
+        # Test for R.jar
+        test_r_jar = 'b/R.jar'
+        expect_result = 'b/aapt2/R'
+        r_dir = module_data._get_r_dir(test_r_jar)
+        self.assertEqual(r_dir, expect_result)
+
+        # Test for the target file is not aapt2.srcjar or R.jar
+        test_unknown_target = 'c/proto.srcjar'
+        expect_result = None
+        r_dir = module_data._get_r_dir(test_unknown_target)
+        self.assertEqual(r_dir, expect_result)
+
+    @mock.patch('aidegen.lib.common_util.get_android_root_dir')
+    def test_collect_r_src_path(self, mock_android_root_dir):
+        """Test collect_r_src_path."""
+        # Test on target srcjar exists in srcjars.
+        test_module = dict(_MODULE_INFO)
+        test_module['srcs'] = []
+        mock_android_root_dir.return_value = uc.TEST_DATA_PATH
+        module_data = source_locator.ModuleData(_MODULE_NAME, test_module,
+                                                _MODULE_DEPTH)
+        # Test the module is not APPS.
+        module_data._collect_r_srcs_paths()
+        expect_result = set()
+        self.assertEqual(module_data.r_java_paths, expect_result)
+
+        # Test the module is not a target module.
+        test_module['depth'] = 1
+        module_data = source_locator.ModuleData(_MODULE_NAME, test_module, 1)
+        module_data._collect_r_srcs_paths()
+        expect_result = set()
+        self.assertEqual(module_data.r_java_paths, expect_result)
+
+        # Test the srcjar target doesn't exist.
+        test_module['class'] = ['APPS']
+        test_module['srcjars'] = []
+        module_data = source_locator.ModuleData(_MODULE_NAME, test_module,
+                                                _MODULE_DEPTH)
+        module_data._collect_r_srcs_paths()
+        expect_result = set()
+        self.assertEqual(module_data.r_java_paths, expect_result)
+
+        # Test the srcjar target exists.
+        test_module['srcjars'] = [('out/soong/.intermediates/packages/apps/'
+                                   'test_aapt2/aapt2.srcjar')]
+        module_data = source_locator.ModuleData(_MODULE_NAME, test_module,
+                                                _MODULE_DEPTH)
+        module_data._collect_r_srcs_paths()
+        expect_result = {
+            'out/soong/.intermediates/packages/apps/test_aapt2/aapt2'
+        }
+        self.assertEqual(module_data.r_java_paths, expect_result)
+
+    def test_parse_source_path(self):
+        """Test _parse_source_path."""
+        # The package name of e.java is c.d.
+        test_java = 'a/b/c/d/e.java'
+        package_name = 'c.d'
+        expect_result = 'a/b'
+        src_path = source_locator.ModuleData._parse_source_path(test_java,
+                                                                package_name)
+        self.assertEqual(src_path, expect_result)
+
+        # The package name of e.java is c.d.
+        test_java = 'a/b/c.d/e.java'
+        package_name = 'c.d'
+        expect_result = 'a/b'
+        src_path = source_locator.ModuleData._parse_source_path(test_java,
+                                                                package_name)
+        self.assertEqual(src_path, expect_result)
+
+        # The package name of e.java is x.y.
+        test_java = 'a/b/c/d/e.java'
+        package_name = 'x.y'
+        expect_result = 'a/b/c/d'
+        src_path = source_locator.ModuleData._parse_source_path(test_java,
+                                                                package_name)
+        self.assertEqual(src_path, expect_result)
+
+        # The package name of f.java is c.d.
+        test_java = 'a/b/c.d/e/c/d/f.java'
+        package_name = 'c.d'
+        expect_result = 'a/b/c.d/e'
+        src_path = source_locator.ModuleData._parse_source_path(test_java,
+                                                                package_name)
+        self.assertEqual(src_path, expect_result)
+
+        # The package name of f.java is c.d.e.
+        test_java = 'a/b/c.d/e/c.d/e/f.java'
+        package_name = 'c.d.e'
+        expect_result = 'a/b/c.d/e'
+        src_path = source_locator.ModuleData._parse_source_path(test_java,
+                                                                package_name)
+        self.assertEqual(src_path, expect_result)
+
+    @mock.patch('aidegen.lib.common_util.get_android_root_dir')
+    def test_append_jar_file(self, mock_android_root_dir):
         """Test _append_jar_file process."""
         # Append an existing jar file path to module_data.jar_files.
         test_jar_file = os.path.join(_MODULE_PATH, 'test.jar')
         result_jar_list = set([test_jar_file])
-        constant.ANDROID_ROOT_PATH = uc.TEST_DATA_PATH
+        mock_android_root_dir.return_value = uc.TEST_DATA_PATH
         module_data = source_locator.ModuleData(_MODULE_NAME, _MODULE_INFO,
                                                 _MODULE_DEPTH)
         module_data._append_jar_file(test_jar_file)
@@ -102,7 +225,8 @@
         module_data._append_jar_file(test_jar_file)
         self.assertEqual(module_data.jar_files, set())
 
-    def test_append_jar_from_installed(self):
+    @mock.patch('aidegen.lib.common_util.get_android_root_dir')
+    def test_append_jar_from_installed(self, mock_android_root_dir):
         """Test _append_jar_from_installed handling."""
         # Test appends the first jar file of 'installed'.
         module_info = dict(_MODULE_INFO)
@@ -112,7 +236,7 @@
             os.path.join(_MODULE_PATH, 'tests/test_second.jar')
         ]
         result_jar_list = set([os.path.join(_MODULE_PATH, 'test.jar')])
-        constant.ANDROID_ROOT_PATH = uc.TEST_DATA_PATH
+        mock_android_root_dir.return_value = uc.TEST_DATA_PATH
         module_data = source_locator.ModuleData(_MODULE_NAME, module_info,
                                                 _MODULE_DEPTH)
         module_data._append_jar_from_installed()
@@ -126,7 +250,8 @@
             os.path.join(_MODULE_PATH, 'tests/'))
         self.assertEqual(module_data.jar_files, result_jar_list)
 
-    def test_set_jars_jarfile(self):
+    @mock.patch('aidegen.lib.common_util.get_android_root_dir')
+    def test_set_jars_jarfile(self, mock_android_root_dir):
         """Test _set_jars_jarfile handling."""
         # Combine the module path with jar file name in 'jars' and then append
         # it to module_data.jar_files.
@@ -140,27 +265,29 @@
             os.path.join(_MODULE_PATH, 'test.jar'),
             os.path.join(_MODULE_PATH, 'tests/test_second.jar')
         ])
-        constant.ANDROID_ROOT_PATH = uc.TEST_DATA_PATH
+        mock_android_root_dir.return_value = uc.TEST_DATA_PATH
         module_data = source_locator.ModuleData(_MODULE_NAME, module_info,
                                                 _MODULE_DEPTH)
         module_data._set_jars_jarfile()
         self.assertEqual(module_data.jar_files, result_jar_list)
 
-    def test_locate_sources_path(self):
+    @mock.patch('aidegen.lib.common_util.get_android_root_dir')
+    def test_locate_sources_path(self, mock_android_root_dir):
         """Test locate_sources_path handling."""
         # Test collect source path.
         module_info = dict(_MODULE_INFO)
-        result_src_list = set(['packages/apps/test/src/main/java',
-                               'out/target/common/R'])
+        result_src_list = set(['packages/apps/test/src/main/java'])
         result_test_list = set(['packages/apps/test/tests'])
         result_jar_list = set()
-        constant.ANDROID_ROOT_PATH = uc.TEST_DATA_PATH
+        result_r_path = set()
+        mock_android_root_dir.return_value = uc.TEST_DATA_PATH
         module_data = source_locator.ModuleData(_MODULE_NAME, module_info,
                                                 _MODULE_DEPTH)
         module_data.locate_sources_path()
         self.assertEqual(module_data.src_dirs, result_src_list)
         self.assertEqual(module_data.test_dirs, result_test_list)
         self.assertEqual(module_data.jar_files, result_jar_list)
+        self.assertEqual(module_data.r_java_paths, result_r_path)
 
         # Test find jar files.
         jar_file = ('out/soong/.intermediates/packages/apps/test/test/'
@@ -192,7 +319,8 @@
         module_data.locate_sources_path()
         self.assertEqual(module_data.jar_files, result_jar_list)
 
-    def test_collect_jar_by_depth_value(self):
+    @mock.patch('aidegen.lib.common_util.get_android_root_dir')
+    def test_collect_jar_by_depth_value(self, mock_android_root_dir):
         """Test parameter --depth handling."""
         # Test find jar by module's depth greater than the --depth value from
         # command line.
@@ -207,7 +335,7 @@
         result_jar_list = set(
             [('out/soong/.intermediates/packages/apps/test/test/'
               'android_common/test.jar')])
-        constant.ANDROID_ROOT_PATH = uc.TEST_DATA_PATH
+        mock_android_root_dir.return_value = uc.TEST_DATA_PATH
         module_data = source_locator.ModuleData(_MODULE_NAME, module_info,
                                                 depth_by_source)
         module_data.locate_sources_path()
@@ -219,41 +347,45 @@
         depth_by_source = 2
         module_info = dict(_MODULE_INFO)
         module_info['depth'] = 2
-        result_src_list = set(['packages/apps/test/src/main/java',
-                               'out/target/common/R'])
+        result_src_list = set(['packages/apps/test/src/main/java'])
         result_test_list = set(['packages/apps/test/tests'])
         result_jar_list = set()
+        result_r_path = set()
         module_data = source_locator.ModuleData(_MODULE_NAME, module_info,
                                                 depth_by_source)
         module_data.locate_sources_path()
         self.assertEqual(module_data.src_dirs, result_src_list)
         self.assertEqual(module_data.test_dirs, result_test_list)
         self.assertEqual(module_data.jar_files, result_jar_list)
+        self.assertEqual(module_data.r_java_paths, result_r_path)
 
         # Test find source folder when module's depth smaller than the --depth
         # value from command line.
         depth_by_source = 3
         module_info = dict(_MODULE_INFO)
         module_info['depth'] = 2
-        result_src_list = set(['packages/apps/test/src/main/java',
-                               'out/target/common/R'])
+        result_src_list = set(['packages/apps/test/src/main/java'])
         result_test_list = set(['packages/apps/test/tests'])
         result_jar_list = set()
+        result_r_path = set()
         module_data = source_locator.ModuleData(_MODULE_NAME, module_info,
                                                 depth_by_source)
         module_data.locate_sources_path()
         self.assertEqual(module_data.src_dirs, result_src_list)
         self.assertEqual(module_data.test_dirs, result_test_list)
         self.assertEqual(module_data.jar_files, result_jar_list)
+        self.assertEqual(module_data.r_java_paths, result_r_path)
 
+    @mock.patch('aidegen.lib.common_util.get_android_root_dir')
     @mock.patch('aidegen.lib.project_info.ProjectInfo')
     @mock.patch('atest.atest_utils.build')
-    def test_locate_source(self, mock_atest_utils_build, mock_project_info):
+    def test_locate_source(self, mock_atest_utils_build, mock_project_info,
+                           mock_android_root_dir):
         """Test locate_source handling."""
         mock_atest_utils_build.build.return_value = True
         test_root_path = os.path.join(tempfile.mkdtemp(), 'test')
         shutil.copytree(uc.TEST_DATA_PATH, test_root_path)
-        constant.ANDROID_ROOT_PATH = test_root_path
+        mock_android_root_dir.return_value = test_root_path
         generated_jar = ('out/soong/.intermediates/packages/apps/test/test/'
                          'android_common/generated.jar')
         module_info = dict(_MODULE_INFO)
@@ -267,10 +399,13 @@
             'test_folder_path': set(),
             'jar_path': set(),
             'jar_module_path': dict(),
+            'srcjar_path': set(),
+            'r_java_path': set(),
         }
         # Show warning when the jar not exists after build the module.
         result_jar = set()
-        source_locator.locate_source(mock_project_info, False, 0, True)
+        source_locator.locate_source(mock_project_info, False, 0,
+                                     constant.IDE_INTELLIJ, True)
         self.assertEqual(mock_project_info.source_path['jar_path'], result_jar)
 
         # Test on jar exists.
@@ -279,7 +414,8 @@
         result_jar_module_path = dict({generated_jar: module_info['path'][0]})
         try:
             open(jar_abspath, 'w').close()
-            source_locator.locate_source(mock_project_info, False, 0, False)
+            source_locator.locate_source(mock_project_info, False, 0,
+                                         constant.IDE_INTELLIJ, False)
             self.assertEqual(mock_project_info.source_path['jar_path'],
                              result_jar)
             self.assertEqual(mock_project_info.source_path['jar_module_path'],
@@ -288,21 +424,45 @@
             shutil.rmtree(test_root_path)
 
         # Test collects source and test folders.
-        result_source = set(['packages/apps/test/src/main/java',
-                             'out/target/common/R'])
+        result_source = set(['packages/apps/test/src/main/java'])
         result_test = set(['packages/apps/test/tests'])
+        result_r_path = set()
         self.assertEqual(mock_project_info.source_path['source_folder_path'],
                          result_source)
         self.assertEqual(mock_project_info.source_path['test_folder_path'],
                          result_test)
+        self.assertEqual(mock_project_info.source_path['r_java_path'],
+                         result_r_path)
 
         # Test loading jar from dependencies parameter.
         default_jar = os.path.join(_MODULE_PATH, 'test.jar')
         module_info['dependencies'] = [default_jar]
         result_jar = set([generated_jar, default_jar])
-        source_locator.locate_source(mock_project_info, False, 0, False)
+        source_locator.locate_source(mock_project_info, False, 0,
+                                     constant.IDE_INTELLIJ, False)
         self.assertEqual(mock_project_info.source_path['jar_path'], result_jar)
 
+    def test_separate_build_target(self):
+        """Test separate_build_target."""
+        test_list = ['1', '22', '333', '4444', '55555', '1', '7777777']
+        target = []
+        sample = [['1', '22', '333'], ['4444'], ['55555', '1'], ['7777777']]
+        for start, end in iter(
+                source_locator._separate_build_targets(test_list, 9)):
+            target.append(test_list[start: end])
+        self.assertEqual(target, sample)
+
+    def test_collect_srcjar_path(self):
+        """Test collect srcjar path."""
+        srcjar_path = 'a/b/aapt2.srcjar'
+        test_module = dict(_MODULE_INFO)
+        test_module['srcjars'] = [srcjar_path]
+        expacted_result = set(['%s!/' % srcjar_path])
+        module_data = source_locator.ModuleData(_MODULE_NAME, test_module,
+                                                _MODULE_DEPTH)
+        module_data._collect_srcjar_path(srcjar_path)
+        self.assertEqual(module_data.srcjar_paths, expacted_result)
+
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/aidegen/run_tests.sh b/aidegen/run_tests.sh
index b3e8008..b698ab4 100755
--- a/aidegen/run_tests.sh
+++ b/aidegen/run_tests.sh
@@ -39,6 +39,7 @@
     PYTHONPATH=$(get_python_path) python3 -m coverage erase
     for t in $tests_to_run;
     do
+        echo "Test" $t
         if ! PYTHONPATH=$(get_python_path) python3 -m coverage run --append --rcfile=$rc_file $t; then
             rc=1
             echo -e "${RED}$t failed${NC}"
diff --git a/aidegen/templates/eclipse/classpath.xml b/aidegen/templates/eclipse/classpath.xml
new file mode 100644
index 0000000..698a6f6
--- /dev/null
+++ b/aidegen/templates/eclipse/classpath.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+{SRC}
+{LIB}
+    <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+</classpath>
diff --git a/aidegen/templates/eclipse/eclipse.xml b/aidegen/templates/eclipse/eclipse.xml
deleted file mode 100644
index 74735b8..0000000
--- a/aidegen/templates/eclipse/eclipse.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-@SRC@
-@LIB@
-</classpath>
diff --git a/aidegen/templates/eclipse/project.xml b/aidegen/templates/eclipse/project.xml
index 2b19250..a41fa48 100644
--- a/aidegen/templates/eclipse/project.xml
+++ b/aidegen/templates/eclipse/project.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-        <name>@PROJECTNAME@</name>
+        <name>{PROJECTNAME}</name>
         <comment></comment>
         <projects>
         </projects>
@@ -14,4 +14,7 @@
         <natures>
                 <nature>org.eclipse.jdt.core.javanature</nature>
         </natures>
+        <linkedResources>
+{LINKEDRESOURCES}
+        </linkedResources>
 </projectDescription>
diff --git a/aidegen/templates/enable_debugger/enable_debugger.iml b/aidegen/templates/enable_debugger/enable_debugger.iml
new file mode 100644
index 0000000..3fb3ed8
--- /dev/null
+++ b/aidegen/templates/enable_debugger/enable_debugger.iml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="FacetManager">
+    <facet type="android" name="Android">
+      <configuration>
+        <proGuardCfgFiles />
+      </configuration>
+    </facet>
+  </component>
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" generated="true" />
+    </content>
+    <orderEntry type="jdk" jdkName="Android API {API_LEVEL} Platform" jdkType="Android SDK" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>
diff --git a/aidegen/templates/idea/copyright/Apache_2.xml b/aidegen/templates/idea/copyright/Apache_2.xml
index 1900c4e..e5101e3 100644
--- a/aidegen/templates/idea/copyright/Apache_2.xml
+++ b/aidegen/templates/idea/copyright/Apache_2.xml
@@ -1,7 +1,7 @@
 <component name="CopyrightManager">
     <copyright>
         <option name="notice"
-                value="Copyright (C) &amp;#36;today.year The Android Open Source Project&#10;&#10;Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);&#10;you may not use this file except in compliance with the License.&#10;You may obtain a copy of the License at&#10;&#10;     http://www.apache.org/licenses/LICENSE-2.0&#10;&#10;Unless required by applicable law or agreed to in writing, software&#10;distributed under the License is distributed on an &quot;AS IS&quot; BASIS,&#10;WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&#10;See the License for the specific language governing permissions and&#10;limitations under the License"/>
+                value="Copyright (C) &amp;#36;today.year The Android Open Source Project&#10;&#10;Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);&#10;you may not use this file except in compliance with the License.&#10;You may obtain a copy of the License at&#10;&#10;     http://www.apache.org/licenses/LICENSE-2.0&#10;&#10;Unless required by applicable law or agreed to in writing, software&#10;distributed under the License is distributed on an &quot;AS IS&quot; BASIS,&#10;WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&#10;See the License for the specific language governing permissions and&#10;limitations under the License."/>
         <option name="keyword" value="Copyright"/>
         <option name="allowReplaceKeyword" value=""/>
         <option name="myName" value="Apache 2"/>
diff --git a/aidegen/templates/idea/modules.xml b/aidegen/templates/idea/modules.xml
index 3f99631..a35c26e 100644
--- a/aidegen/templates/idea/modules.xml
+++ b/aidegen/templates/idea/modules.xml
@@ -3,6 +3,7 @@
     <component name="ProjectModuleManager">
         <modules>
 @MODULES@
+@ENABLE_DEBUGGER_MODULE@
         </modules>
     </component>
 </project>
diff --git a/aidegen/templates/jdkTable/jdk.table.xml b/aidegen/templates/jdkTable/jdk.table.xml
deleted file mode 100644
index 38e1289..0000000
--- a/aidegen/templates/jdkTable/jdk.table.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<application>
-  <component name="ProjectJdkTable">
-    <jdk version="2">
-      <name value="JDK18" />
-      <type value="JavaSDK" />
-      <version value="java version &quot;1.8.0_152&quot;" />
-      <homePath value="@JDKpath" />
-      <roots>
-        <annotationsPath>
-          <root type="composite">
-            <root url="jar://$APPLICATION_HOME_DIR$/lib/jdkAnnotations.jar!/" type="simple" />
-          </root>
-        </annotationsPath>
-        <classPath>
-          <root type="composite">
-            <root url="jar://@JDKpath/jre/lib/charsets.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/cldrdata.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/dnsns.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/jaccess.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/localedata.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/nashorn.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/sunec.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/sunjce_provider.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/sunpkcs11.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/zipfs.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/jce.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/jsse.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/management-agent.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/resources.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/rt.jar!/" type="simple" />
-          </root>
-        </classPath>
-        <javadocPath>
-          <root type="composite" />
-        </javadocPath>
-        <sourcePath>
-          <root type="composite">
-            <root url="jar://@JDKpath/src.zip!/" type="simple" />
-          </root>
-        </sourcePath>
-      </roots>
-      <additional />
-    </jdk>
-  </component>
-</application>
\ No newline at end of file
diff --git a/aidegen/templates/jdkTable/mac.jdk.table.xml b/aidegen/templates/jdkTable/mac.jdk.table.xml
deleted file mode 100644
index 62f05ee..0000000
--- a/aidegen/templates/jdkTable/mac.jdk.table.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-<application>
-  <component name="ProjectJdkTable">
-    <jdk version="2">
-      <name value="JDK18" />
-      <type value="JavaSDK" />
-      <version value="java version &quot;1.8.0_152&quot;" />
-      <homePath value="@JDKpath" />
-      <roots>
-        <annotationsPath>
-          <root type="composite">
-            <root url="jar://$APPLICATION_HOME_DIR$/lib/jdkAnnotations.jar!/" type="simple" />
-          </root>
-        </annotationsPath>
-        <classPath>
-          <root type="composite">
-            <root url="jar://@JDKpath/jre/lib/charsets.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/cldrdata.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/dnsns.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/jaccess.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/localedata.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/nashorn.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/sunec.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/sunjce_provider.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/sunpkcs11.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/zipfs.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/jce.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/jsse.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/management-agent.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/resources.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/rt.jar!/" type="simple" />
-            <root url="jar://@JDKpath/lib/dt.jar!/" type="simple" />
-            <root url="jar://@JDKpath/lib/jconsole.jar!/" type="simple" />
-            <root url="jar://@JDKpath/lib/sa-jdi.jar!/" type="simple" />
-            <root url="jar://@JDKpath/lib/tools.jar!/" type="simple" />
-          </root>
-        </classPath>
-        <javadocPath>
-          <root type="composite" />
-        </javadocPath>
-        <sourcePath>
-          <root type="composite">
-            <root url="jar://@JDKpath/src.zip!/" type="simple" />
-          </root>
-        </sourcePath>
-      </roots>
-      <additional />
-    </jdk>
-  </component>
-</application>
\ No newline at end of file
diff --git a/aidegen/templates/jdkTable/part.android.sdk.xml b/aidegen/templates/jdkTable/part.android.sdk.xml
new file mode 100644
index 0000000..305ff39
--- /dev/null
+++ b/aidegen/templates/jdkTable/part.android.sdk.xml
@@ -0,0 +1,23 @@
+    <jdk version="2">
+      <name value="Android API {API_LEVEL} Platform" />
+      <type value="Android SDK" />
+      <version value="java version &quot;1.8.0_152&quot;" />
+      <homePath value="{ANDROID_SDK_PATH}" />
+      <roots>
+        <annotationsPath>
+          <root type="composite" />
+        </annotationsPath>
+        <classPath>
+          <root type="composite">
+            <root url="file://{ANDROID_SDK_PATH}/platforms/android-{API_LEVEL}/data/res" type="simple" />
+          </root>
+        </classPath>
+        <javadocPath>
+          <root type="composite" />
+        </javadocPath>
+        <sourcePath>
+          <root type="composite" />
+        </sourcePath>
+      </roots>
+      <additional jdk="JDK18" sdk="android-{API_LEVEL}" />
+    </jdk>
diff --git a/aidegen/templates/jdkTable/part.jdk.table.xml b/aidegen/templates/jdkTable/part.jdk.table.xml
index 3e7020a..5ac784f 100644
--- a/aidegen/templates/jdkTable/part.jdk.table.xml
+++ b/aidegen/templates/jdkTable/part.jdk.table.xml
@@ -2,7 +2,7 @@
       <name value="JDK18" />
       <type value="JavaSDK" />
       <version value="java version &quot;1.8.0_152&quot;" />
-      <homePath value="@JDKpath" />
+      <homePath value="{JDKpath}" />
       <roots>
         <annotationsPath>
           <root type="composite">
@@ -11,21 +11,21 @@
         </annotationsPath>
         <classPath>
           <root type="composite">
-            <root url="jar://@JDKpath/jre/lib/charsets.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/cldrdata.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/dnsns.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/jaccess.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/localedata.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/nashorn.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/sunec.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/sunjce_provider.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/sunpkcs11.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/zipfs.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/jce.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/jsse.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/management-agent.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/resources.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/rt.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/charsets.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/ext/cldrdata.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/ext/dnsns.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/ext/jaccess.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/ext/localedata.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/ext/nashorn.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/ext/sunec.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/ext/sunjce_provider.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/ext/sunpkcs11.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/ext/zipfs.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/jce.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/jsse.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/management-agent.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/resources.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/rt.jar!/" type="simple" />
           </root>
         </classPath>
         <javadocPath>
@@ -33,10 +33,9 @@
         </javadocPath>
         <sourcePath>
           <root type="composite">
-            <root url="jar://@JDKpath/src.zip!/" type="simple" />
+            <root url="jar://{JDKpath}/src.zip!/" type="simple" />
           </root>
         </sourcePath>
       </roots>
       <additional />
     </jdk>
-  </component>
\ No newline at end of file
diff --git a/aidegen/templates/jdkTable/part.mac.jdk.table.xml b/aidegen/templates/jdkTable/part.mac.jdk.table.xml
index 0c9483b..9a2a1d4 100644
--- a/aidegen/templates/jdkTable/part.mac.jdk.table.xml
+++ b/aidegen/templates/jdkTable/part.mac.jdk.table.xml
@@ -2,7 +2,7 @@
       <name value="JDK18" />
       <type value="JavaSDK" />
       <version value="java version &quot;1.8.0_152&quot;" />
-      <homePath value="@JDKpath" />
+      <homePath value="{JDKpath}" />
       <roots>
         <annotationsPath>
           <root type="composite">
@@ -11,28 +11,28 @@
         </annotationsPath>
         <classPath>
           <root type="composite">
-            <root url="jar://@JDKpath/jre/lib/charsets.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/cldrdata.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/dnsns.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/jaccess.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/localedata.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/nashorn.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/sunec.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/sunjce_provider.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/sunpkcs11.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/ext/zipfs.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/jce.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/jsse.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/management-agent.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/resources.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/rt.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/management-agent.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/resources.jar!/" type="simple" />
-            <root url="jar://@JDKpath/jre/lib/rt.jar!/" type="simple" />
-            <root url="jar://@JDKpath/lib/dt.jar!/" type="simple" />
-            <root url="jar://@JDKpath/lib/jconsole.jar!/" type="simple" />
-            <root url="jar://@JDKpath/lib/sa-jdi.jar!/" type="simple" />
-            <root url="jar://@JDKpath/lib/tools.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/charsets.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/ext/cldrdata.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/ext/dnsns.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/ext/jaccess.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/ext/localedata.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/ext/nashorn.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/ext/sunec.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/ext/sunjce_provider.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/ext/sunpkcs11.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/ext/zipfs.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/jce.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/jsse.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/management-agent.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/resources.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/rt.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/management-agent.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/resources.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/jre/lib/rt.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/lib/dt.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/lib/jconsole.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/lib/sa-jdi.jar!/" type="simple" />
+            <root url="jar://{JDKpath}/lib/tools.jar!/" type="simple" />
           </root>
         </classPath>
         <javadocPath>
@@ -40,10 +40,9 @@
         </javadocPath>
         <sourcePath>
           <root type="composite">
-            <root url="jar://@JDKpath/src.zip!/" type="simple" />
+            <root url="jar://{JDKpath}/src.zip!/" type="simple" />
           </root>
         </sourcePath>
       </roots>
       <additional />
     </jdk>
-  </component>
\ No newline at end of file
diff --git a/aidegen/templates/module-template.iml b/aidegen/templates/module-template.iml
index 5dd867b..248d56b 100644
--- a/aidegen/templates/module-template.iml
+++ b/aidegen/templates/module-template.iml
@@ -1,9 +1,14 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!-- Deprecated, please refer to the definition of constant.FILE_IML, and leave
+     this file for backward compatibility.
+     TODO: Remove this file when the CL 1012154 is merged in AIDEGen prebuilt.
+-->
 <module type="JAVA_MODULE" version="4">
 @FACETS@
     <component name="NewModuleRootManager" inherit-compiler-output="true">
         <exclude-output />
 @SOURCES@
+@SRCJAR@
         <orderEntry type="sourceFolder" forTests="false" />
 @MODULE_DEPENDENCIES@
         <orderEntry type="inheritedJdk" />
diff --git a/aidegen/test_data/Android/Sdk/platforms/android-28/.gitignore b/aidegen/test_data/Android/Sdk/platforms/android-28/.gitignore
new file mode 100644
index 0000000..5e7d273
--- /dev/null
+++ b/aidegen/test_data/Android/Sdk/platforms/android-28/.gitignore
@@ -0,0 +1,4 @@
+# Ignore everything in this directory
+*
+# Except this file
+!.gitignore
diff --git a/aidegen/test_data/android_facet.iml b/aidegen/test_data/android_facet.iml
index 0a8d190..561adf7 100644
--- a/aidegen/test_data/android_facet.iml
+++ b/aidegen/test_data/android_facet.iml
@@ -6,6 +6,7 @@
     <component name="NewModuleRootManager" inherit-compiler-output="true">
         <exclude-output />
 @SOURCES@
+@SRCJAR@
         <orderEntry type="sourceFolder" forTests="false" />
 @MODULE_DEPENDENCIES@
         <orderEntry type="inheritedJdk" />
diff --git a/aidegen/test_data/dependencies.iml b/aidegen/test_data/dependencies.iml
index a22917c..19bc2ac 100644
--- a/aidegen/test_data/dependencies.iml
+++ b/aidegen/test_data/dependencies.iml
@@ -9,6 +9,7 @@
         <content url="file:///aosp/test_data/project/level11/level22/level31">
             <sourceFolder url="file:///aosp/test_data/project/level11/level22/level31" isTestSource="False" />
         </content>
+@SRCJAR@
 
         <orderEntry type="sourceFolder" forTests="false" />
 @MODULE_DEPENDENCIES@
diff --git a/aidegen/test_data/eclipse.project b/aidegen/test_data/eclipse.project
new file mode 100644
index 0000000..819e1dd
--- /dev/null
+++ b/aidegen/test_data/eclipse.project
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+        <name>test</name>
+        <comment></comment>
+        <projects>
+        </projects>
+        <buildSpec>
+                <buildCommand>
+                        <name>org.eclipse.jdt.core.javabuilder</name>
+                        <arguments>
+                        </arguments>
+                </buildCommand>
+        </buildSpec>
+        <natures>
+                <nature>org.eclipse.jdt.core.javanature</nature>
+        </natures>
+        <linkedResources>
+                <link><name>dependencies/module/path</name><type>2</type><location>/android/root/module/path</location></link>
+
+        </linkedResources>
+</projectDescription>
diff --git a/aidegen/test_data/enable_debugger.iml b/aidegen/test_data/enable_debugger.iml
new file mode 100644
index 0000000..fca1692
--- /dev/null
+++ b/aidegen/test_data/enable_debugger.iml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="FacetManager">
+    <facet type="android" name="Android">
+      <configuration>
+        <proGuardCfgFiles />
+      </configuration>
+    </facet>
+  </component>
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" generated="true" />
+    </content>
+    <orderEntry type="jdk" jdkName="Android API 28 Platform" jdkType="Android SDK" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>
diff --git a/aidegen/test_data/jdk_table_xml/android_sdk.xml b/aidegen/test_data/jdk_table_xml/android_sdk.xml
new file mode 100644
index 0000000..b1a8ba6
--- /dev/null
+++ b/aidegen/test_data/jdk_table_xml/android_sdk.xml
@@ -0,0 +1,27 @@
+<application>
+  <component name="ProjectJdkTable">
+    <jdk version="2">
+      <name value="Android API 28 Platform" />
+      <type value="Android SDK" />
+      <version value="java version &quot;1.8.0_152&quot;" />
+      <homePath value="/path/to/Android/Sdk" />
+      <roots>
+        <annotationsPath>
+          <root type="composite" />
+        </annotationsPath>
+        <classPath>
+          <root type="composite">
+            <root url="file:///path/to/Android/Sdk/platforms/android-28/data/res" type="simple" />
+          </root>
+        </classPath>
+        <javadocPath>
+          <root type="composite" />
+        </javadocPath>
+        <sourcePath>
+          <root type="composite" />
+        </sourcePath>
+      </roots>
+      <additional jdk="JDK18" sdk="android-28" />
+    </jdk>
+  </component>
+</application>
diff --git a/aidegen/test_data/jdk_table_xml/android_sdk_nonexistent.xml b/aidegen/test_data/jdk_table_xml/android_sdk_nonexistent.xml
new file mode 100644
index 0000000..1619b6c
--- /dev/null
+++ b/aidegen/test_data/jdk_table_xml/android_sdk_nonexistent.xml
@@ -0,0 +1,27 @@
+<application>
+  <component name="ProjectJdkTable">
+    <jdk version="2">
+      <name value="Android API 28 Platform" />
+      <type value="Fake Android SDK" />
+      <version value="java version &quot;1.8.0_152&quot;" />
+      <homePath value="/path/to/Android/Sdk" />
+      <roots>
+        <annotationsPath>
+          <root type="composite" />
+        </annotationsPath>
+        <classPath>
+          <root type="composite">
+            <root url="file:///path/to/Android/Sdk/platforms/android-28/data/res" type="simple" />
+          </root>
+        </classPath>
+        <javadocPath>
+          <root type="composite" />
+        </javadocPath>
+        <sourcePath>
+          <root type="composite" />
+        </sourcePath>
+      </roots>
+      <additional jdk="JDK18" sdk="android-28" />
+    </jdk>
+  </component>
+</application>
diff --git a/aidegen/test_data/jdk_table_xml/jdk18.xml b/aidegen/test_data/jdk_table_xml/jdk18.xml
new file mode 100644
index 0000000..ea9a52e
--- /dev/null
+++ b/aidegen/test_data/jdk_table_xml/jdk18.xml
@@ -0,0 +1,45 @@
+<application>
+  <component name="ProjectJdkTable">
+    <jdk version="2">
+      <name value="JDK18" />
+      <type value="JavaSDK" />
+      <version value="java version &quot;1.8.0_152&quot;" />
+      <homePath value="/path/to/android/root/prebuilts/jdk/jdk8/linux-x86" />
+      <roots>
+        <annotationsPath>
+          <root type="composite">
+            <root url="jar://$APPLICATION_HOME_DIR$/lib/jdkAnnotations.jar!/" type="simple" />
+          </root>
+        </annotationsPath>
+        <classPath>
+          <root type="composite">
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/charsets.jar!/" type="simple" />
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/ext/cldrdata.jar!/" type="simple" />
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/ext/dnsns.jar!/" type="simple" />
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/ext/jaccess.jar!/" type="simple" />
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/ext/localedata.jar!/" type="simple" />
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/ext/nashorn.jar!/" type="simple" />
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/ext/sunec.jar!/" type="simple" />
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/ext/sunjce_provider.jar!/" type="simple" />
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/ext/sunpkcs11.jar!/" type="simple" />
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/ext/zipfs.jar!/" type="simple" />
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/jce.jar!/" type="simple" />
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/jsse.jar!/" type="simple" />
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/management-agent.jar!/" type="simple" />
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/resources.jar!/" type="simple" />
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/rt.jar!/" type="simple" />
+          </root>
+        </classPath>
+        <javadocPath>
+          <root type="composite" />
+        </javadocPath>
+        <sourcePath>
+          <root type="composite">
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/src.zip!/" type="simple" />
+          </root>
+        </sourcePath>
+      </roots>
+      <additional />
+    </jdk>
+  </component>
+</application>
diff --git a/aidegen/test_data/jdk_table_xml/jdk_nonexistent.xml b/aidegen/test_data/jdk_table_xml/jdk_nonexistent.xml
new file mode 100644
index 0000000..2cc65c6
--- /dev/null
+++ b/aidegen/test_data/jdk_table_xml/jdk_nonexistent.xml
@@ -0,0 +1,49 @@
+<application>
+  <component name="ProjectJdkTable">
+    <jdk version="2">
+      <name value="JDK_OTHER" />
+      <type value="JavaSDK" />
+    </jdk>
+    <jdk version="2">
+      <name value="JDK18" />
+      <type value="JavaSDK" />
+      <version value="java version &quot;1.8.0_152&quot;" />
+      <homePath value="/path/to/android/root/prebuilts/jdk/jdk8/linux-x86" />
+      <roots>
+        <annotationsPath>
+          <root type="composite">
+            <root url="jar://$APPLICATION_HOME_DIR$/lib/jdkAnnotations.jar!/" type="simple" />
+          </root>
+        </annotationsPath>
+        <classPath>
+          <root type="composite">
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/charsets.jar!/" type="simple" />
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/ext/cldrdata.jar!/" type="simple" />
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/ext/dnsns.jar!/" type="simple" />
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/ext/jaccess.jar!/" type="simple" />
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/ext/localedata.jar!/" type="simple" />
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/ext/nashorn.jar!/" type="simple" />
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/ext/sunec.jar!/" type="simple" />
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/ext/sunjce_provider.jar!/" type="simple" />
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/ext/sunpkcs11.jar!/" type="simple" />
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/ext/zipfs.jar!/" type="simple" />
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/jce.jar!/" type="simple" />
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/jsse.jar!/" type="simple" />
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/management-agent.jar!/" type="simple" />
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/resources.jar!/" type="simple" />
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/jre/lib/rt.jar!/" type="simple" />
+          </root>
+        </classPath>
+        <javadocPath>
+          <root type="composite" />
+        </javadocPath>
+        <sourcePath>
+          <root type="composite">
+            <root url="jar:///path/to/android/root/prebuilts/jdk/jdk8/linux-x86/src.zip!/" type="simple" />
+          </root>
+        </sourcePath>
+      </roots>
+      <additional />
+    </jdk>
+  </component>
+</application>
diff --git a/aidegen/test_data/module_dependency.iml b/aidegen/test_data/module_dependency.iml
index 7914c00..5e5bf43 100644
--- a/aidegen/test_data/module_dependency.iml
+++ b/aidegen/test_data/module_dependency.iml
@@ -4,6 +4,7 @@
     <component name="NewModuleRootManager" inherit-compiler-output="true">
         <exclude-output />
 @SOURCES@
+@SRCJAR@
         <orderEntry type="sourceFolder" forTests="false" />
 
         <orderEntry type="inheritedJdk" />
diff --git a/aidegen/test_data/modules.xml b/aidegen/test_data/modules.xml
index 101b7b9..c591862 100644
--- a/aidegen/test_data/modules.xml
+++ b/aidegen/test_data/modules.xml
@@ -3,7 +3,8 @@
     <component name="ProjectModuleManager">
         <modules>
             <module fileurl="file:///$PROJECT_DIR$/android_project.iml" filepath="$PROJECT_DIR$/android_project.iml" />
-            <module fileurl="file:///$PROJECT_DIR$/dependencies-android_project.iml" filepath="$PROJECT_DIR$/dependencies-android_project.iml" />
+            <module fileurl="file:///$PROJECT_DIR$/dependencies.iml" filepath="$PROJECT_DIR$/dependencies.iml" />
+
         </modules>
     </component>
 </project>
diff --git a/aidegen/test_data/modules_only_self_module.xml b/aidegen/test_data/modules_only_self_module.xml
new file mode 100644
index 0000000..e5fdf25
--- /dev/null
+++ b/aidegen/test_data/modules_only_self_module.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+    <component name="ProjectModuleManager">
+        <modules>
+            <module fileurl="file:///$PROJECT_DIR$/android_project.iml" filepath="$PROJECT_DIR$/android_project.iml" />
+
+        </modules>
+    </component>
+</project>
diff --git a/aidegen/test_data/modules_with_enable_debugger.xml b/aidegen/test_data/modules_with_enable_debugger.xml
new file mode 100644
index 0000000..5421060
--- /dev/null
+++ b/aidegen/test_data/modules_with_enable_debugger.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+    <component name="ProjectModuleManager">
+        <modules>
+            <module fileurl="file:///$PROJECT_DIR$/android_project.iml" filepath="$PROJECT_DIR$/android_project.iml" />
+            <module fileurl="file:///$PROJECT_DIR$/dependencies.iml" filepath="$PROJECT_DIR$/dependencies.iml" />
+            <module fileurl="file:////path/to/enable_debugger/enable_debugger.iml" filepath="/path/to/enable_debugger/enable_debugger.iml" />
+        </modules>
+    </component>
+</project>
diff --git a/aidegen/test_data/out/soong/.intermediates/packages/apps/test_R/R.jar b/aidegen/test_data/out/soong/.intermediates/packages/apps/test_R/R.jar
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/aidegen/test_data/out/soong/.intermediates/packages/apps/test_R/R.jar
diff --git a/aidegen/test_data/out/soong/.intermediates/packages/apps/test_aapt2/aapt2.srcjar b/aidegen/test_data/out/soong/.intermediates/packages/apps/test_aapt2/aapt2.srcjar
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/aidegen/test_data/out/soong/.intermediates/packages/apps/test_aapt2/aapt2.srcjar
diff --git a/aidegen/test_data/packages/apps/test/src/java.java b/aidegen/test_data/packages/apps/test/src/java.java
new file mode 100644
index 0000000..9c57962
--- /dev/null
+++ b/aidegen/test_data/packages/apps/test/src/java.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+package tests.packages;
+
+/** Dummy Class file for unit tests. */
+public class SomeClassForTesting {
+    private static final String SOME_DUMMY_VAR = "For testing purposes";
+}
diff --git a/aidegen/test_data/packages/apps/test/src/main/java/com/android/wrong_package.java b/aidegen/test_data/packages/apps/test/src/main/java/com/android/no_package.java
similarity index 100%
rename from aidegen/test_data/packages/apps/test/src/main/java/com/android/wrong_package.java
rename to aidegen/test_data/packages/apps/test/src/main/java/com/android/no_package.java
diff --git a/aidegen/test_data/packages/apps/test/src/tests.packages/java.java b/aidegen/test_data/packages/apps/test/src/tests.packages/java.java
new file mode 100644
index 0000000..9c57962
--- /dev/null
+++ b/aidegen/test_data/packages/apps/test/src/tests.packages/java.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+package tests.packages;
+
+/** Dummy Class file for unit tests. */
+public class SomeClassForTesting {
+    private static final String SOME_DUMMY_VAR = "For testing purposes";
+}
diff --git a/aidegen/test_data/packages/apps/test/src/tests.packages/test/java.java b/aidegen/test_data/packages/apps/test/src/tests.packages/test/java.java
new file mode 100644
index 0000000..3b73d9e
--- /dev/null
+++ b/aidegen/test_data/packages/apps/test/src/tests.packages/test/java.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+package tests.packages.test;
+
+/** Dummy Class file for unit tests. */
+public class SomeClassForTesting {
+    private static final String SOME_DUMMY_VAR = "For testing purposes";
+}
diff --git a/aidegen/test_data/project_facet.iml b/aidegen/test_data/project_facet.iml
index 2948c63..2378007 100644
--- a/aidegen/test_data/project_facet.iml
+++ b/aidegen/test_data/project_facet.iml
@@ -4,6 +4,7 @@
     <component name="NewModuleRootManager" inherit-compiler-output="true">
         <exclude-output />
 @SOURCES@
+@SRCJAR@
         <orderEntry type="sourceFolder" forTests="false" />
 @MODULE_DEPENDENCIES@
         <orderEntry type="inheritedJdk" />
diff --git a/aidegen/test_data/source.iml b/aidegen/test_data/source.iml
index 115f5b7..c1cc0c9 100644
--- a/aidegen/test_data/source.iml
+++ b/aidegen/test_data/source.iml
@@ -9,6 +9,7 @@
             <sourceFolder url="file:///aosp/test_data/project/level12/level22" isTestSource="False" />
         </content>
 
+@SRCJAR@
         <orderEntry type="sourceFolder" forTests="false" />
 @MODULE_DEPENDENCIES@
         <orderEntry type="inheritedJdk" />
diff --git a/aidegen/test_data/srcjar.iml b/aidegen/test_data/srcjar.iml
new file mode 100644
index 0000000..a813829
--- /dev/null
+++ b/aidegen/test_data/srcjar.iml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+@FACETS@
+    <component name="NewModuleRootManager" inherit-compiler-output="true">
+        <exclude-output />
+@SOURCES@
+        <content url="jar:///aosp/out/aapt2.srcjar!/">
+            <sourceFolder url="jar:///aosp/out/aapt2.srcjar!/" isTestSource="False" />
+        </content>
+
+        <orderEntry type="sourceFolder" forTests="false" />
+@MODULE_DEPENDENCIES@
+        <orderEntry type="inheritedJdk" />
+    </component>
+</module>
diff --git a/aidegen/test_data/test.iml b/aidegen/test_data/test.iml
index 4085790..5ab6d03 100644
--- a/aidegen/test_data/test.iml
+++ b/aidegen/test_data/test.iml
@@ -11,8 +11,9 @@
             <sourceFolder url="file:///aosp/test_data/project/level12/level22" isTestSource="False" />
         </content>
 
+
         <orderEntry type="sourceFolder" forTests="false" />
-        <orderEntry type="module" module-name="dependencies-android_project" />
+        <orderEntry type="module" module-name="dependencies" />
         <orderEntry type="inheritedJdk" />
     </component>
 </module>
diff --git a/aidegen/unittest_constants.py b/aidegen/unittest_constants.py
index e68688b..78be494 100644
--- a/aidegen/unittest_constants.py
+++ b/aidegen/unittest_constants.py
@@ -24,10 +24,10 @@
 
 import os
 
-from aidegen.constant import AIDEGEN_ROOT_PATH as rp
+from aidegen.lib.common_util import get_aidegen_root_dir
 
 # The data below is only for test usage.
-TEST_DATA_PATH = os.path.join(rp, "test_data")  # folder test_data path
+TEST_DATA_PATH = os.path.join(get_aidegen_root_dir(), "test_data")
 IDEA_SH_FIND = [
     '/opt/intellij-ce-2018.1/bin/idea.sh', '/opt/intellij-ce-2017.2/bin/idea.sh'
 ]  # script path data
@@ -37,3 +37,10 @@
 IDEA_SH_FIND_NONE = ''  # Neither IntelliJ CE nor UE script exists.
 TEST_PATH = 'path'
 TEST_MODULE = 'test'
+ANDROID_SOURCE_DICT = {
+    'test_data/project/level11/level21': True,
+    'test_data/project/level11/level22/level31': False,
+    'test_data/project/level12/level22': False,
+}
+JAR_DEP_LIST = ['test1.jar', 'test2.jar']
+ANDROID_PROJECT_PATH = os.path.join(TEST_DATA_PATH, 'android_project')
diff --git a/aidegen_functional_test/Android.bp b/aidegen_functional_test/Android.bp
index 3e9a862..92b4c6a 100644
--- a/aidegen_functional_test/Android.bp
+++ b/aidegen_functional_test/Android.bp
@@ -29,13 +29,15 @@
 
 python_binary_host {
     name: "aidegen_functional_test",
+    stem: "aidegen_functional_test-dev",
     defaults: ["aidegen_functional_test_default"],
     main: "aidegen_functional_test_main.py",
     srcs: [
-        "aidegen_functional_test_main.py",
+        "**/*.py",
     ],
     libs: [
         "aidegen_lib",
         "atest_module_info",
+        "asuite_cc_client",
     ],
 }
diff --git a/aidegen_functional_test/aidegen_functional_test_main.py b/aidegen_functional_test/aidegen_functional_test_main.py
index 4c78b10..fb16dd7 100644
--- a/aidegen_functional_test/aidegen_functional_test_main.py
+++ b/aidegen_functional_test/aidegen_functional_test_main.py
@@ -29,14 +29,15 @@
 import aidegen.lib.errors
 
 from aidegen import aidegen_main
+from aidegen.lib import common_util
+from aidegen.lib.common_util import COLORED_PASS
+from aidegen.lib.common_util import COLORED_FAIL
+from aidegen.lib.common_util import get_blueprint_json_path
 from aidegen.lib.common_util import get_related_paths
 from aidegen.lib.common_util import time_logged
-from atest import constants
 from atest import module_info
-from atest import atest_utils
 
-_ANDROID_ROOT_PATH = os.environ.get(constants.ANDROID_BUILD_TOP)
-_ROOT_DIR = os.path.join(_ANDROID_ROOT_PATH,
+_ROOT_DIR = os.path.join(common_util.get_android_root_dir(),
                          'tools/asuite/aidegen_functional_test')
 _TEST_DATA_PATH = os.path.join(_ROOT_DIR, 'test_data')
 _ANDROID_SINGLE_PROJECT_JSON = os.path.join(_TEST_DATA_PATH,
@@ -96,6 +97,9 @@
 
     Args:
         filename: The input project file name.
+
+    Returns:
+        A dictionary contains json data.
     """
     data = {}
     try:
@@ -103,11 +107,13 @@
         data[_SRCS] = []
         root = tree.getroot()
         for element in root.iter('sourceFolder'):
-            src = element.get(_URL).replace(_ANDROID_ROOT_PATH, _PRODUCT_DIR)
+            src = element.get(_URL).replace(common_util.get_android_root_dir(),
+                                            _PRODUCT_DIR)
             data[_SRCS].append(src)
         data[_JARS] = []
         for element in root.iter('root'):
-            jar = element.get(_URL).replace(_ANDROID_ROOT_PATH, _PRODUCT_DIR)
+            jar = element.get(_URL).replace(common_util.get_android_root_dir(),
+                                            _PRODUCT_DIR)
             data[_JARS].append(jar)
     except (EnvironmentError, ValueError, LookupError,
             xml.parsers.expat.ExpatError) as err:
@@ -117,7 +123,11 @@
 
 
 def _generate_sample_json():
-    """Generate sample iml data and write into a json file."""
+    """Generate sample iml data from a iml file into a dictionary.
+
+    Returns:
+        A dictionary contains sample iml data.
+    """
     atest_module_info = module_info.ModuleInfo()
     data = {}
     for target, filelist in _TEST_IML_DICT.items():
@@ -153,15 +163,14 @@
             if set(s_items) != set(r_items):
                 diff_iter = _compare_content(name, item, s_items, r_items)
                 if diff_iter:
-                    print('\n%s\n%s' % (atest_utils.colorize(
-                        'Test error...', constants.RED), _TEST_ERROR %
-                                        (name, item)))
+                    print('\n%s\n%s' % (COLORED_FAIL('Test error...'),
+                                        _TEST_ERROR % (name, item)))
                     print('%s %s contents are different:' % (name, item))
                     for diff in diff_iter:
                         print(diff)
                     test_successful = False
     if test_successful:
-        print(atest_utils.colorize(_ALL_PASS, constants.GREEN))
+        print(COLORED_PASS(_ALL_PASS))
 
 
 def _compare_content(module_name, item_type, s_items, r_items):
@@ -209,6 +218,10 @@
 def _compare_jars_content(module_name, s_items, r_items, msg):
     """Compare src or jar files' data of two dictionaries.
 
+    AIDEGen treats the jars in folder names 'linux_glib_common' and
+    'android_common' as the same content. If the paths are different only
+    because of these two names, we ignore it.
+
     Args:
         module_name: the test module name.
         s_items: sample jars' items.
@@ -234,9 +247,12 @@
 @time_logged
 def _verify_aidegen():
     """Verify various use cases of executing aidegen."""
+    bp_json_path = get_blueprint_json_path()
     with open(_VERIFY_COMMANDS_JSON, 'r') as jsfile:
         data = json.load(jsfile)
     for use_case in data:
+        if os.path.exists(bp_json_path):
+            os.remove(bp_json_path)
         for cmd in data[use_case]:
             try:
                 eval(cmd)
@@ -245,16 +261,15 @@
                     aidegen.lib.errors.NoModuleDefinedInModuleInfoError,
                     aidegen.lib.errors.IDENotExistError) as err:
                 print('{} command has raise error: {}.'.format(use_case, err))
-            except Exception as exp:
+            except BaseException:
+                exc_type, _, _ = sys.exc_info()
                 print('{}.{} command {}.'.format(
-                    use_case, cmd,
-                    atest_utils.colorize('executes failed', constants.RED)))
-                raise Exception(
-                    'Unexpected command {} exception {}.'.format(use_case, exp))
-        print('{} command {}!'.format(
-            use_case, atest_utils.colorize('test passed', constants.GREEN)))
-    print(atest_utils.colorize(_ALL_PASS, constants.GREEN))
-
+                    use_case, cmd, COLORED_FAIL('executes failed')))
+                raise BaseException(
+                    'Unexpected command {} exception {}.'.format(
+                        use_case, exc_type))
+        print('{} command {}!'.format(use_case, COLORED_PASS('test passed')))
+    print(COLORED_PASS(_ALL_PASS))
 
 def main(argv):
     """Main entry.
diff --git a/aidegen_functional_test/test_data/verify_commands.json b/aidegen_functional_test/test_data/verify_commands.json
index 1ec215e..88da032 100644
--- a/aidegen_functional_test/test_data/verify_commands.json
+++ b/aidegen_functional_test/test_data/verify_commands.json
@@ -1,69 +1,68 @@
 {
     "aidegen tradefed": [
-        "os.remove('out/soong/module_bp_java_deps.json') if os.path.exists('out/soong/module_bp_java_deps.json') else None",
         "aidegen_main.main(['tradefed', '-n', '-v'])"
     ],
     "aidegen tools/tradefederation/core": [
         "aidegen_main.main(['tools/tradefederation/core', '-n', '-v'])"
     ],
     "cd tools/tradefederation/core;aidegen": [
-        "os.remove('out/soong/module_bp_java_deps.json') if os.path.exists('out/soong/module_bp_java_deps.json') else None",
         "os.chdir('tools/tradefederation/core')",
         "aidegen_main.main(['-n', '-v'])",
         "os.chdir('../../..')"
     ],
     "cd tools/tradefederation;aidegen": [
-        "os.remove('out/soong/module_bp_java_deps.json') if os.path.exists('out/soong/module_bp_java_deps.json') else None",
         "os.chdir('tools/tradefederation')",
         "aidegen_main.main(['-n', '-v'])",
         "os.chdir('../..')"
     ],
     "aidegen tradefed cts-tradefed": [
-        "os.remove('out/soong/module_bp_java_deps.json') if os.path.exists('out/soong/module_bp_java_deps.json') else None",
         "aidegen_main.main(['tradefed', 'cts-tradefed', '-n', '-v'])"
     ],
     "aidegen tradefed test/suite_harness/tools/cts-tradefed": [
-        "os.remove('out/soong/module_bp_java_deps.json') if os.path.exists('out/soong/module_bp_java_deps.json') else None",
         "aidegen_main.main(['tradefed', 'test/suite_harness/tools/cts-tradefed', '-n', '-v'])"
     ],
     "aidegen tools/tradefederation/core test/suite_harness/tools/cts-tradefed": [
-        "os.remove('out/soong/module_bp_java_deps.json') if os.path.exists('out/soong/module_bp_java_deps.json') else None",
         "aidegen_main.main(['tools/tradefederation/core', 'test/suite_harness/tools/cts-tradefed', '-n', '-v'])"
     ],
     "aidegen cts frameworks/base frameworks/native": [
-        "os.remove('out/soong/module_bp_java_deps.json') if os.path.exists('out/soong/module_bp_java_deps.json') else None",
         "aidegen_main.main(['cts', 'frameworks/base', 'frameworks/native', '-n', '-v'])"
     ],
     "aidegen nonexist/lib/path": [
-        "os.remove('out/soong/module_bp_java_deps.json') if os.path.exists('out/soong/module_bp_java_deps.json') else None",
         "aidegen_main.main(['nonexist/lib/path', '-n', '-v'])"
     ],
     "aidegen no_module_defined": [
-        "os.remove('out/soong/module_bp_java_deps.json') if os.path.exists('out/soong/module_bp_java_deps.json') else None",
         "aidegen_main.main(['no_module_defined', '-n', '-v'])"
     ],
     "aidegen tradefed -p /opt/": [
-        "os.remove('out/soong/module_bp_java_deps.json') if os.path.exists('out/soong/module_bp_java_deps.json') else None",
         "aidegen_main.main(['tradefed', '-p', '/opt/', '-n', '-v'])"
     ],
     "aidegen tradefed -p /home/": [
-        "os.remove('out/soong/module_bp_java_deps.json') if os.path.exists('out/soong/module_bp_java_deps.json') else None",
         "aidegen_main.main(['tradefed', '-p', '/home/', '-n', '-v'])"
     ],
     "aidegen tradefed -i s": [
-        "os.remove('out/soong/module_bp_java_deps.json') if os.path.exists('out/soong/module_bp_java_deps.json') else None",
         "aidegen_main.main(['tradefed', '-i', 's', '-n', '-v'])"
     ],
     "aidegen tradefed -i e": [
-        "os.remove('out/soong/module_bp_java_deps.json') if os.path.exists('out/soong/module_bp_java_deps.json') else None",
         "aidegen_main.main(['tradefed', '-i', 'e', '-n', '-v'])"
     ],
     "aidegen tradefed -s": [
-        "os.remove('out/soong/module_bp_java_deps.json') if os.path.exists('out/soong/module_bp_java_deps.json') else None",
         "aidegen_main.main(['tradefed', '-s', '-n', '-v'])"
     ],
     "aidegen(whole aosp)": [
-        "os.remove('out/soong/module_bp_java_deps.json') if os.path.exists('out/soong/module_bp_java_deps.json') else None",
         "aidegen_main.main(['-n', '-v'])"
+    ],
+    "aidegen(whole aosp) with -a": [
+        "aidegen_main.main(['-n', '-v', '-a'])"
+    ],
+    "aidegen(whole aosp) with tools/tradefederation and -a": [
+        "aidegen_main.main(['tools/tradefederation/core', '-n', '-v', '-a'])"
+    ],
+    "aidegen(whole aosp) in local with -a": [
+        "os.chdir('tools/tradefederation')",
+        "aidegen_main.main(['-n', '-v', '-a'])",
+        "os.chdir('../..')"
+    ],
+    "aidegen -h": [
+        "aidegen_main.main(['-h'])"
     ]
 }
diff --git a/pylintrc b/pylintrc
index 7eedeae..84284ba 100644
--- a/pylintrc
+++ b/pylintrc
@@ -19,3 +19,8 @@
 
 # Maximum number of characters on a single line.
 max-line-length=80
+
+[SIMILARITIES]
+
+# Ignore imports when computing similarities.
+ignore-imports=yes