Snap for 5626642 from 31ead9c0ba696cdec698f08ea841133e0e89581f to qt-c2f2-release

Change-Id: I70624ab349cbf5c5173b00fb407ba20cccca2700
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 75cdfc0..29dc0af 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -2,4 +2,4 @@
 pylint = true
 
 [Hook Scripts]
-aidegen_unittests = ${REPO_ROOT}/prebuilts/asuite/atest/linux-x86/atest aidegen_unittests
+aidegen_unittests = ${REPO_ROOT}/prebuilts/asuite/atest/linux-x86/atest aidegen_unittests --host
diff --git a/aidegen/Android.bp b/aidegen/Android.bp
index 093f181..04442d3 100644
--- a/aidegen/Android.bp
+++ b/aidegen/Android.bp
@@ -41,7 +41,11 @@
     ],
     libs: [
         "atest_module_info",
+        "asuite_cc_client",
     ],
+    dist: {
+        targets: ["droidcore"],
+    },
 }
 
 python_library_host {
@@ -68,6 +72,7 @@
     ],
     libs: [
         "py-mock",
+        "asuite_cc_client",
     ],
     test_config: "aidegen_unittests.xml",
     test_suites: ["general-tests"],
diff --git a/aidegen/Android.mk b/aidegen/Android.mk
deleted file mode 100644
index 1547d31..0000000
--- a/aidegen/Android.mk
+++ /dev/null
@@ -1,15 +0,0 @@
-# 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.
-
-$(call dist-for-goals,droidcore,$(HOST_OUT_EXECUTABLES)/aidegen-dev)
diff --git a/aidegen/README.md b/aidegen/README.md
index 8cdf1ce..738bca3 100755
--- a/aidegen/README.md
+++ b/aidegen/README.md
@@ -1,25 +1,63 @@
 AIDEgen aims to automate the project setup process for developers to work on
 Java project in popular IDE environment. Developers no longer need to
 manually configure an IntelliJ project, such as all the project dependencies.
-AIDEgen offers the following features:
+It's a **command line tool** that offers the following features:
 
-* Configures Intellij or Android Studio project files with indexing of
-  API/Object references.
+* Configure Intellij or Android Studio project files with the relevant module
+  dependencies resolved.
 
-* Can be launched for a specified sub-project (V1), i.e. frameworks/base.
+* Launch IDE for a specified sub-project or build target, i.e. frameworks/base
+  or Settings.
 
-* Can be launched for a specified module (V1), i.e. cts/ or tradefed.
+* Launch IDE for a specified folder which contains build targets, i.e. cts.
 
-* Integration with ASuite features to provide better user experience.
+* Auto configure JDK and Android coding style for Intellij.
 
-#1. Compilation:
+## 1. Prerequisites:
 
-    $ 'make aidegen' # this produces aidegen in out/host/linux-x86/bin.
+    IDE installed and run $ '. build/envsetup.sh && lunch' under Android source
+    root folder.
 
-#2. Execution:
+## 2. Execution:
 
     $ 'aidegen <module_name>... <module_path>...'
+      Example to generate and launch IntelliJ project for framework and
+      Settings:
+        $ aidegen Settings framework
+        $ aidegen packages/apps/Settings frameworks/base
+        $ aidegen packages/apps/Settings framework
 
-#3. More argument:
+    $ 'aidegen <module> -i s'
+      Example to generate and launch Android Studio project for framework:
+        $ aidegen framework -i s
 
-    $ 'aidegen --help'
\ No newline at end of file
+## 3. More argument:
+
+    $ aidegen --help
+
+## 4. FAQ:
+
+    1. Q: If I already have an IDE project file, and I run command AIDEGen to
+          generate the same project file again, what’ll happen?
+       A: The former IDEA project file will be overwritten by the newly
+          generated one from the aidegen command.
+
+    2. Q: When do I need to re-run AIDEGen?
+       A: Re-run AIDEGen after repo sync.
+
+    3. Q: Does AIDEGen support debug log dump?
+       A: Use aidegen -v to get more debug information.
+
+    4. Q: After the aidegen command run locally, if there’s no IDEA with
+          project shown up, what can I do ?
+       A: Basic steps to do troubleshooting:
+          - Make sure development environment is set up, please refer to
+            prerequisites section.
+          - Check error message in the aidegen command output.
+
+# Hint
+    1. In Intellij, uses [File] > [Invalidate Caches / Restart…] to force
+       project panel updated when your IDE didn't sync.
+
+    2. If you run aidegen on a remote desktop, make sure there is no IntelliJ
+       running in a different desktop session.
diff --git a/aidegen/aidegen_main.py b/aidegen/aidegen_main.py
index f4d48e2..186c1e0 100644
--- a/aidegen/aidegen_main.py
+++ b/aidegen/aidegen_main.py
@@ -43,24 +43,26 @@
 import logging
 import os
 import sys
+import traceback
 
 from aidegen import constant
 from aidegen.lib.android_dev_os import AndroidDevOS
 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 check_modules
 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.project_info import ProjectInfo
 from aidegen.lib.source_locator import multi_projects_locate_source
-from atest import module_info
 
 AIDEGEN_REPORT_LINK = ('To report the AIDEGen tool problem, please use this '
                        'link: https://goto.google.com/aidegen-bug')
@@ -160,7 +162,7 @@
         '--android-tree',
         dest='android_tree',
         action='store_true',
-        help=('Generate whole Android source tree project file for IDE.'))
+        help='Generate whole Android source tree project file for IDE.')
     return parser.parse_args(args)
 
 
@@ -214,10 +216,10 @@
     """Generate project files by IDE type.
 
     Args:
-        ide: IDE type.
+        ide: A character to represent IDE type.
         projects: A list of ProjectInfo instances.
     """
-    if ide == 'e':
+    if ide.lower() == 'e':
         generate_eclipse_project_files(projects)
     else:
         generate_ide_project_files(projects)
@@ -325,6 +327,7 @@
     aidegen_main(args)
 
 
+# pylint: disable=broad-except
 def main(argv):
     """Main entry.
 
@@ -333,11 +336,34 @@
     Args:
         argv: A list of system arguments.
     """
-    args = _parse_args(argv)
-    if args.skip_build:
-        main_without_message(args)
-    else:
-        main_with_message(args)
+    exit_code = constant.EXIT_CODE_NORMAL
+    try:
+        args = _parse_args(argv)
+        _configure_logging(args.verbose)
+        starts_asuite_metrics()
+        if args.skip_build:
+            main_without_message(args)
+        else:
+            main_with_message(args)
+    except BaseException as err:
+        exit_code = constant.EXIT_CODE_EXCEPTION
+        _, exc_value, exc_traceback = sys.exc_info()
+        if isinstance(err, AIDEgenError):
+            exit_code = constant.EXIT_CODE_AIDEGEN_EXCEPTION
+        # 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)
+            # print out the trackback message for developers to debug
+            print(traceback_str)
+            ends_asuite_metrics(exit_code, traceback_str, error_message)
+        else:
+            ends_asuite_metrics(exit_code)
 
 
 def aidegen_main(args):
@@ -349,18 +375,17 @@
         args: A list of system arguments.
     """
     log_usage()
-    _configure_logging(args.verbose)
     # Pre-check for IDE relevant case, then handle dependency graph job.
     ide_util_obj = _get_ide_util_instance(args)
     _check_skip_build(args)
-    atest_module_info = module_info.ModuleInfo()
-    check_modules(atest_module_info, args.targets)
+    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)
     multi_projects_locate_source(projects, args.verbose, args.depth,
+                                 constant.IDE_NAME_DICT[args.ide[0]],
                                  args.skip_build)
     _generate_project_files(args.ide[0], projects)
     if ide_util_obj:
diff --git a/aidegen/aidegen_main_unittest.py b/aidegen/aidegen_main_unittest.py
index cf449b0..cc10d01 100644
--- a/aidegen/aidegen_main_unittest.py
+++ b/aidegen/aidegen_main_unittest.py
@@ -123,12 +123,15 @@
         aidegen_main._generate_project_files('j', 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):
+    def test_show_collect_data_notice(self, mock_log, mock_get):
         """Test main process always run through the target test function."""
         target = 'nothing'
         args = aidegen_main._parse_args([target, '-s', '-n'])
         with self.assertRaises(ProjectPathNotExistError):
+            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)
 
diff --git a/aidegen/constant.py b/aidegen/constant.py
index ef5ddec..b9e7e71 100644
--- a/aidegen/constant.py
+++ b/aidegen/constant.py
@@ -21,19 +21,34 @@
 
 # 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)
-ROOT_DIR = os.path.join(ANDROID_ROOT_PATH, 'tools/asuite/aidegen')
+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)
-ANDROID_OUT = os.path.join(ANDROID_ROOT_PATH, 'out')
-OUT_DIR = ANDROID_OUT_DIR or ANDROID_OUT_DIR_COMMON_BASE or ANDROID_OUT
-BLUEPRINT_JSONFILE_OUTDIR = os.path.join(OUT_DIR, 'soong')
+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)
+
+# Constants for module's info.
 KEY_PATH = 'path'
 KEY_DEP = 'dependencies'
 KEY_DEPTH = 'depth'
-RELATIVE_HOST_OUT = os.path.relpath(ANDROID_HOST_OUT, ANDROID_ROOT_PATH)
+
+# 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'
diff --git a/aidegen/data/gitignore_template b/aidegen/data/gitignore_template
new file mode 100644
index 0000000..86dfa9c
--- /dev/null
+++ b/aidegen/data/gitignore_template
@@ -0,0 +1,11 @@
+# Eclipse project
+**/.classpath
+**/.project
+
+# IntelliJ project
+**/.idea
+**/*.iml
+**/*.ipr
+
+# Misc files
+**/.gitignore
diff --git a/aidegen/lib/common_util.py b/aidegen/lib/common_util.py
index 0aae331..730b442 100644
--- a/aidegen/lib/common_util.py
+++ b/aidegen/lib/common_util.py
@@ -33,6 +33,7 @@
 from aidegen.lib.errors import ProjectOutsideAndroidRootError
 from aidegen.lib.errors import ProjectPathNotExistError
 from atest import constants
+from atest import module_info
 from atest.atest_utils import colorize
 
 COLORED_INFO = partial(colorize, color=constants.MAGENTA, highlight=False)
@@ -50,6 +51,7 @@
 ]
 TARGET_CLASSES = JAVA_TARGET_CLASSES
 TARGET_CLASSES.extend(NATIVE_TARGET_CLASSES)
+_REBUILD_MODULE_INFO = '%s We should rebuild module-info.json file for it.'
 
 
 def time_logged(func=None, *, message='', maximum=1):
@@ -76,20 +78,19 @@
             return func(*args, **kwargs)
         finally:
             timestamp = time.time() - start
-            logging.debug('{}.{} time consumes: {:.2f}s'.format(
-                func.__module__, func.__name__,
-                timestamp))
+            logging.debug('{}.{} takes: {:.2f}s'.format(
+                func.__module__, func.__name__, timestamp))
             if message and timestamp > maximum * 60:
                 print(message)
 
     return wrapper
 
 
-def get_related_paths(module_info, target=None):
+def get_related_paths(atest_module_info, target=None):
     """Get the relative and absolute paths of target from module-info.
 
     Args:
-        module_info: A ModuleInfo instance contains data of module-info.json.
+        atest_module_info: A ModuleInfo instance.
         target: A string user input from command line. It could be several cases
                 such as:
                 1. Module name, e.g. Settings
@@ -107,14 +108,14 @@
     abs_path = None
     if target:
         # User inputs a module name.
-        if module_info.is_module(target):
-            paths = module_info.get_paths(target)
+        if atest_module_info.is_module(target):
+            paths = atest_module_info.get_paths(target)
             if paths:
                 rel_path = paths[0]
                 abs_path = os.path.join(constant.ANDROID_ROOT_PATH, rel_path)
         # User inputs a module path or a relative path of android root folder.
-        elif (module_info.get_module_names(target) or
-              os.path.isdir(os.path.join(constant.ANDROID_ROOT_PATH, target))):
+        elif (atest_module_info.get_module_names(target) or os.path.isdir(
+                os.path.join(constant.ANDROID_ROOT_PATH, target))):
             rel_path = target.strip(os.sep)
             abs_path = os.path.join(constant.ANDROID_ROOT_PATH, rel_path)
         # User inputs a relative path of current directory.
@@ -177,37 +178,68 @@
         for mod_path in atest_module_info.path_to_module_info)
 
 
-def check_modules(atest_module_info, targets):
-    """Check if all targets are valid build targets."""
-    for target in targets:
-        check_module(atest_module_info, target)
-
-
-def check_module(atest_module_info, target):
-    """Check if a target is a valid build target or a project path containing
-       build target.
-
-    The rules:
-        1. If module's absolute path is None, raise FakeModuleError.
-        2. If the module doesn't exist in android root,
-           raise ProjectOutsideAndroidRootError.
-        3. If module's absolute path is not a directory,
-           raise ProjectPathNotExistError.
-        4. If it contains any build target continue checking, else:
-           1) If it's android root, continue checking.
-           2) If none of above, raise NoModuleDefinedInModuleInfoError.
+def _check_modules(atest_module_info, targets, raise_on_lost_module=True):
+    """Check if all targets are valid build targets.
 
     Args:
         atest_module_info: A ModuleInfo instance contains data of
                            module-info.json.
-        target: A target module or project path from user input, when locating
-                the target, project with matched module name of the target has a
-                higher priority than project path. It could be several cases
-                such as:
+        targets: A list of target modules or project paths from user input.
+                When locating the path of the target, given a matched module
+                name has priority over path. Below is the priority of locating a
+                target:
                 1. Module name, e.g. Settings
                 2. Module path, e.g. packages/apps/Settings
                 3. Relative path, e.g. ../../packages/apps/Settings
                 4. Current directory, e.g. . or no argument
+        raise_on_lost_module: A boolean, pass to _check_module to determine if
+                ProjectPathNotExistError or NoModuleDefinedInModuleInfoError
+                should be raised.
+
+    Returns:
+        True if any _check_module return flip the True/False.
+    """
+    for target in targets:
+        if not _check_module(atest_module_info, target, raise_on_lost_module):
+            return False
+    return True
+
+
+def _check_module(atest_module_info, target, raise_on_lost_module=True):
+    """Check if a target is valid or it's a path containing build target.
+
+    Args:
+        atest_module_info: A ModuleInfo instance contains the data of
+                module-info.json.
+        target: A target module or project path from user input.
+                When locating the path of the target, given a matched module
+                name has priority over path. Below is the priority of locating a
+                target:
+                1. Module name, e.g. Settings
+                2. Module path, e.g. packages/apps/Settings
+                3. Relative path, e.g. ../../packages/apps/Settings
+                4. Current directory, e.g. . or no argument
+        raise_on_lost_module: A boolean, handles if ProjectPathNotExistError or
+                NoModuleDefinedInModuleInfoError should be raised.
+
+    Returns:
+        1. If there is no error _check_module always return True.
+        2. If there is a error,
+            a. When raise_on_lost_module is False, _check_module will raise the
+               error.
+            b. When raise_on_lost_module is True, _check_module will return
+               False if module's error is ProjectPathNotExistError or
+               NoModuleDefinedInModuleInfoError else raise the error.
+
+    Raises:
+        Raise ProjectPathNotExistError and NoModuleDefinedInModuleInfoError only
+        when raise_on_lost_module is True, others don't subject to the limit.
+        The rules for raising exceptions:
+        1. Absolute path of a module is None -> FakeModuleError
+        2. Module doesn't exist in repo root -> ProjectOutsideAndroidRootError
+        3. The given absolute path is not a dir -> ProjectPathNotExistError
+        4. If the given abs path doesn't contain any target and not repo root
+           -> NoModuleDefinedInModuleInfoError
     """
     rel_path, abs_path = get_related_paths(atest_module_info, target)
     if not abs_path:
@@ -220,13 +252,20 @@
         raise ProjectOutsideAndroidRootError(err)
     if not os.path.isdir(abs_path):
         err = PATH_NOT_EXISTS_ERROR.format(rel_path)
-        logging.error(err)
-        raise ProjectPathNotExistError(err)
+        if raise_on_lost_module:
+            logging.error(err)
+            raise ProjectPathNotExistError(err)
+        logging.debug(_REBUILD_MODULE_INFO, err)
+        return False
     if (not has_build_target(atest_module_info, rel_path)
-            and abs_path != constant.ANDROID_ROOT_PATH):
+            and not is_android_root(abs_path)):
         err = NO_MODULE_DEFINED_ERROR.format(rel_path)
-        logging.error(err)
-        raise NoModuleDefinedInModuleInfoError(err)
+        if raise_on_lost_module:
+            logging.error(err)
+            raise NoModuleDefinedInModuleInfoError(err)
+        logging.debug(_REBUILD_MODULE_INFO, err)
+        return False
+    return True
 
 
 def get_abs_path(rel_path):
@@ -287,3 +326,24 @@
         False.
     """
     return any(src_file.endswith(x) for x in src_file_extensions)
+
+
+def get_atest_module_info(targets):
+    """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.
+
+    Args:
+        targets: A list of targets to be built.
+
+    Returns:
+        An atest ModuleInfo instance.
+    """
+    amodule_info = module_info.ModuleInfo()
+    if 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
diff --git a/aidegen/lib/common_util_unittest.py b/aidegen/lib/common_util_unittest.py
index 4a6f36e..bea77a1 100644
--- a/aidegen/lib/common_util_unittest.py
+++ b/aidegen/lib/common_util_unittest.py
@@ -30,6 +30,7 @@
 from atest import module_info
 
 
+#pylint: disable=protected-access
 #pylint: disable=invalid-name
 class AidegenCommonUtilUnittests(unittest.TestCase):
     """Unit tests for common_util.py"""
@@ -75,38 +76,45 @@
         mod_info = module_info.ModuleInfo()
         mock_get.return_value = None, None
         with self.assertRaises(FakeModuleError) as ctx:
-            common_util.check_module(mod_info, uc.TEST_MODULE)
+            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.return_value = None, uc.TEST_MODULE
         with self.assertRaises(ProjectOutsideAndroidRootError) as ctx:
-            common_util.check_module(mod_info, uc.TEST_MODULE)
+            common_util._check_module(mod_info, uc.TEST_MODULE)
             expected = common_util.OUTSIDE_ROOT_ERROR.format(uc.TEST_MODULE)
             self.assertEqual(expected, str(ctx.exception))
         mock_get.return_value = None, uc.TEST_PATH
         mock_isdir.return_value = False
         with self.assertRaises(ProjectPathNotExistError) as ctx:
-            common_util.check_module(mod_info, uc.TEST_MODULE)
+            common_util._check_module(mod_info, uc.TEST_MODULE)
             expected = common_util.PATH_NOT_EXISTS_ERROR.format(uc.TEST_MODULE)
             self.assertEqual(expected, str(ctx.exception))
         mock_isdir.return_value = True
         mock_has_target.return_value = False
         mock_get.return_value = None, os.path.join(uc.TEST_PATH, 'test.jar')
         with self.assertRaises(NoModuleDefinedInModuleInfoError) as ctx:
-            common_util.check_module(mod_info, uc.TEST_MODULE)
+            common_util._check_module(mod_info, uc.TEST_MODULE)
             expected = common_util.NO_MODULE_DEFINED_ERROR.format(
                 uc.TEST_MODULE)
             self.assertEqual(expected, str(ctx.exception))
+        self.assertEqual(common_util._check_module(mod_info, '', False), False)
+        self.assertEqual(common_util._check_module(mod_info, 'nothing', False),
+                         False)
 
-    @mock.patch.object(common_util, 'check_module')
+    @mock.patch.object(common_util, '_check_module')
     def test_check_modules(self, mock_check):
-        """Test check_modules with different module lists."""
+        """Test _check_modules with different module lists."""
         mod_info = module_info.ModuleInfo()
-        common_util.check_modules(mod_info, [])
+        common_util._check_modules(mod_info, [])
         self.assertEqual(mock_check.call_count, 0)
-        common_util.check_modules(mod_info, ['module1', 'module2'])
+        common_util._check_modules(mod_info, ['module1', 'module2'])
         self.assertEqual(mock_check.call_count, 2)
+        target = 'nothing'
+        mock_check.return_value = False
+        self.assertEqual(common_util._check_modules(mod_info, [target], False),
+                         False)
 
     def test_get_abs_path(self):
         """Test get_abs_path handling."""
@@ -118,12 +126,15 @@
 
     def test_is_target(self):
         """Test is_target handling."""
-        self.assertEqual(common_util.is_target('packages/apps/tests/test.a',
-                                               ['.so', '.a']), True)
-        self.assertEqual(common_util.is_target('packages/apps/tests/test.so',
-                                               ['.so', '.a']), True)
-        self.assertEqual(common_util.is_target('packages/apps/tests/test.jar',
-                                               ['.so', '.a']), False)
+        self.assertEqual(
+            common_util.is_target('packages/apps/tests/test.a', ['.so', '.a']),
+            True)
+        self.assertEqual(
+            common_util.is_target('packages/apps/tests/test.so', ['.so', '.a']),
+            True)
+        self.assertEqual(
+            common_util.is_target('packages/apps/tests/test.jar',
+                                  ['.so', '.a']), False)
 
 
 if __name__ == '__main__':
diff --git a/aidegen/lib/ide_util.py b/aidegen/lib/ide_util.py
index 7e0f871..43c17d0 100644
--- a/aidegen/lib/ide_util.py
+++ b/aidegen/lib/ide_util.py
@@ -42,9 +42,6 @@
 _IGNORE_STD_OUT_ERR_CMD = '2>/dev/null >&2'
 _IDEA_FOLDER = '.idea'
 _IML_EXTENSION = '.iml'
-_IDE_INTELLIJ = 'IntelliJ'
-_IDE_ANDROID_STUDIO = 'Android Studio'
-_IDE_ECLIPSE = 'Eclipse'
 _JDK_PATH_TOKEN = '@JDKpath'
 _TARGET_JDK_NAME_TAG = '<name value="JDK18" />'
 _COMPONENT_END_TAG = '  </component>'
@@ -96,6 +93,10 @@
         """Gets IDE default installed path."""
         return self._ide.default_installed_path
 
+    def ide_name(self):
+        """Gets IDE name."""
+        return self._ide.ide_name
+
 
 class IdeBase():
     """The most base class of IDE, provides interface and partial path init.
@@ -148,6 +149,11 @@
         """Gets IDE default installed path."""
         return ' '.join(self._bin_folders)
 
+    @property
+    def ide_name(self):
+        """Gets IDE name."""
+        return self._ide_name
+
     def _get_ide_cmd(self, project_file):
         """Compose launch IDE command to run a new process and redirect output.
 
@@ -206,7 +212,7 @@
 
     def __init__(self, installed_path=None, config_reset=False):
         super().__init__(installed_path, config_reset)
-        self._ide_name = _IDE_INTELLIJ
+        self._ide_name = constant.IDE_INTELLIJ
         self._ls_ce_path = ''
         self._ls_ue_path = ''
         self._init_installed_path(installed_path)
@@ -359,8 +365,8 @@
     # 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.ROOT_DIR, 'templates/jdkTable/part.jdk.table.xml')
-    _JDK_FULL_TEMPLATE_PATH = os.path.join(constant.ROOT_DIR,
+        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')
 
     def __init__(self, installed_path=None, config_reset=False):
@@ -431,9 +437,9 @@
                              'prebuilts/jdk/jdk8/darwin-x86')
     _IDE_JDK_TABLE_PATH = 'options/jdk.table.xml'
     _JDK_PART_TEMPLATE_PATH = os.path.join(
-        constant.ROOT_DIR, 'templates/jdkTable/part.mac.jdk.table.xml')
+        constant.AIDEGEN_ROOT_PATH, 'templates/jdkTable/part.mac.jdk.table.xml')
     _JDK_FULL_TEMPLATE_PATH = os.path.join(
-        constant.ROOT_DIR, 'templates/jdkTable/mac.jdk.table.xml')
+        constant.AIDEGEN_ROOT_PATH, 'templates/jdkTable/mac.jdk.table.xml')
 
     def __init__(self, installed_path=None, config_reset=False):
         super().__init__(installed_path, config_reset)
@@ -489,7 +495,7 @@
 
     def __init__(self, installed_path=None, config_reset=False):
         super().__init__(installed_path, config_reset)
-        self._ide_name = _IDE_ANDROID_STUDIO
+        self._ide_name = constant.IDE_ANDROID_STUDIO
 
 
 class IdeLinuxStudio(IdeStudio):
@@ -534,9 +540,8 @@
 
     def __init__(self, installed_path=None, config_reset=False):
         super().__init__(installed_path, config_reset)
-        self._ide_name = _IDE_ECLIPSE
+        self._ide_name = constant.IDE_ECLIPSE
         self._bin_file_name = 'eclipse*'
-        self._init_installed_path(installed_path)
 
     def _get_script_from_system(self):
         """Get correct IDE installed path from internal path.
diff --git a/aidegen/lib/metrics.py b/aidegen/lib/metrics.py
index 86384c2..a03db43 100644
--- a/aidegen/lib/metrics.py
+++ b/aidegen/lib/metrics.py
@@ -18,12 +18,17 @@
 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']
@@ -43,6 +48,35 @@
     _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."""
diff --git a/aidegen/lib/module_info_util.py b/aidegen/lib/module_info_util.py
index 98d687a..322f3ec 100644
--- a/aidegen/lib/module_info_util.py
+++ b/aidegen/lib/module_info_util.py
@@ -37,7 +37,6 @@
 from aidegen.lib.common_util import time_logged
 from aidegen.lib.common_util import get_related_paths
 from aidegen.lib import errors
-from atest import constants
 
 _BLUEPRINT_JSONFILE_NAME = 'module_bp_java_deps.json'
 _KEY_CLS = 'class'
@@ -126,7 +125,7 @@
                 cmd, stderr=subprocess.STDOUT, env=full_env_vars, shell=True)
         else:
             subprocess.check_call(cmd, shell=True)
-        logging.info('Build successful: %s.', _GENERATE_JSON_COMMAND)
+        logging.info('Build successfully: %s.', cmd)
     except subprocess.CalledProcessError:
         if not _is_new_json_file_generated(json_path, original_json_mtime):
             if os.path.isfile(json_path):
@@ -197,9 +196,7 @@
     Returns:
         Blueprint json path.
     """
-    return os.path.join(
-        os.environ.get(constants.ANDROID_BUILD_TOP),
-        constant.BLUEPRINT_JSONFILE_OUTDIR, _BLUEPRINT_JSONFILE_NAME)
+    return os.path.join(constant.SOONG_OUT_DIR_PATH, _BLUEPRINT_JSONFILE_NAME)
 
 
 def _merge_module_keys(m_dict, b_dict):
diff --git a/aidegen/lib/module_info_util_unittest.py b/aidegen/lib/module_info_util_unittest.py
index e900004..0cf7f6b 100644
--- a/aidegen/lib/module_info_util_unittest.py
+++ b/aidegen/lib/module_info_util_unittest.py
@@ -24,6 +24,7 @@
 
 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
 
@@ -172,9 +173,10 @@
             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):
+    def test_build_failed_handle(self, mock_glob, mock_input, _send_exit):
         """Test _build_failed_handle with different situations."""
         mock_glob.return_value = ['project/file.iml']
         mock_input.return_value = 'N'
diff --git a/aidegen/lib/project_file_gen.py b/aidegen/lib/project_file_gen.py
index 755ce0d..2535945 100644
--- a/aidegen/lib/project_file_gen.py
+++ b/aidegen/lib/project_file_gen.py
@@ -56,7 +56,7 @@
 _MODULE_TOKEN = '@MODULES@'
 _VCS_TOKEN = '@VCS@'
 _JAVA_FILE_PATTERN = '%s/*.java'
-_ROOT_DIR = constant.ROOT_DIR
+_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_FOLDER = '.idea'
@@ -75,12 +75,18 @@
 _FRAMEWORK_JAR = os.sep + 'framework.jar'
 _HIGH_PRIORITY_JARS = [_FRAMEWORK_JAR]
 _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_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_REL_PATH)
-_ECLIP = 'eclipse'
-_ECLIP_SRC_ENTRY = ('<classpathentry exported="true" kind="src" path="{}"/>\n')
-_ECLIP_LIB_ENTRY = ('<classpathentry exported="true" kind="lib" path="{}"/>\n')
+
+_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@'
@@ -97,9 +103,20 @@
 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 }.
+    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: Full module path string.
@@ -116,16 +133,21 @@
                                       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.
+        # 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 _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 _USED_NAME_CACHE.values():
+                i = i + 1
+                uniq_name = '_'.join([uniq_name_base, str(i)])
     _USED_NAME_CACHE[abs_module_path] = uniq_name
     logging.debug('Unique name for module path of %s is %s.', abs_module_path,
                   uniq_name)
@@ -237,7 +259,7 @@
     IntelliJ, it only logs when an IOError occurred.
 
     Args:
-        target_path: Path of target file.
+        target_path: A folder path to copy content to.
     """
     try:
         _copy_to_idea_folder(target_path, _COPYRIGHT_FOLDER)
@@ -253,6 +275,8 @@
         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))
@@ -319,7 +343,11 @@
         else:
             dependencies.append(jar_path)
 
-    for jar_path in dependencies:
+    # 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)
 
@@ -626,3 +654,26 @@
     main_project_absolute_path = projects[0].project_absolute_path
     git_paths = [project.git_path for project in projects]
     _write_vcs_xml(main_project_absolute_path, git_paths)
+
+
+def _generate_git_ignore(target_folder):
+    """Generate .gitignore file.
+
+    In target_folder, if there's no .gitignore file, uses symlink() to generate
+    one to hide project content files from git.
+
+    Args:
+        target_folder: An absolute path string of target folder.
+    """
+    # TODO(b/133639849): Provide a common method to create symbolic link.
+    # TODO(b/133641803): Move out aidegen artifacts from Android repo.
+    try:
+        gitignore_abs_path = os.path.join(target_folder, _GITIGNORE_FILE_NAME)
+        rel_target = os.path.relpath(gitignore_abs_path, os.getcwd())
+        rel_source = os.path.relpath(_GITIGNORE_ABS_PATH, target_folder)
+        logging.debug('Relative target symlink path: %s.', rel_target)
+        logging.debug('Relative ignore_template source path: %s.', rel_source)
+        if not os.path.exists(gitignore_abs_path):
+            os.symlink(rel_source, rel_target)
+    except OSError as err:
+        logging.error('Not support to run aidegen on Windows.\n %s', err)
diff --git a/aidegen/lib/project_file_gen_unittest.py b/aidegen/lib/project_file_gen_unittest.py
index f82d67f..c3a969a 100644
--- a/aidegen/lib/project_file_gen_unittest.py
+++ b/aidegen/lib/project_file_gen_unittest.py
@@ -21,6 +21,9 @@
 import shutil
 import unittest
 
+from unittest import mock
+
+from aidegen import constant
 from aidegen import unittest_constants
 from aidegen.lib import project_file_gen
 from atest import module_info
@@ -161,9 +164,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 = []
@@ -216,6 +231,14 @@
             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)
+        self.assertFalse(mock_link.called)
+
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/aidegen/lib/project_info.py b/aidegen/lib/project_info.py
index b1e1330..55a600c 100644
--- a/aidegen/lib/project_info.py
+++ b/aidegen/lib/project_info.py
@@ -62,6 +62,14 @@
                      project_module_names.
         git_path: The project's git path.
         iml_path: The project's iml file path.
+        source_path: A dictionary to keep following data:
+                     source_folder_path: A set contains the source folder
+                                         relative paths.
+                     test_folder_path: A set contains the test folder relative
+                                       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.
     """
 
     modules_info = {}
@@ -108,7 +116,8 @@
         self.source_path = {
             'source_folder_path': set(),
             'test_folder_path': set(),
-            'jar_path': set()
+            'jar_path': set(),
+            'jar_module_path': dict()
         }
 
     def _display_convert_make_files_message(self, module_info, target):
diff --git a/aidegen/lib/source_locator.py b/aidegen/lib/source_locator.py
index 167538f..afa3bc2 100644
--- a/aidegen/lib/source_locator.py
+++ b/aidegen/lib/source_locator.py
@@ -62,7 +62,8 @@
     'cause the red lines to appear in IDE tool.')
 
 
-def multi_projects_locate_source(projects, verbose, depth, skip_build):
+def multi_projects_locate_source(projects, verbose, depth, ide_name,
+                                 skip_build=True):
     """Locate the paths of dependent source folders and jar files with projects.
 
     Args:
@@ -72,16 +73,17 @@
         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.
-        skip_build: A boolean, if true skip building jar and AIDL files,
-                    otherwise build them.
+        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, build=not skip_build)
+        locate_source(project, verbose, depth, ide_name, build=not skip_build)
 
 
-def locate_source(project, verbose, depth, 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
@@ -111,7 +113,9 @@
         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.
-        build: A boolean, if true build the modules whose jar doesn't exist.
+        ide_name: A string stands for the IDE name, default is IntelliJ.
+        build: A boolean default to true, if true skip building jar and srcjar
+               files, otherwise build them.
 
     Example usage:
         project.source_path = locate_source(project, verbose, False)
@@ -126,39 +130,34 @@
     if not hasattr(project, 'dep_modules') or not project.dep_modules:
         raise errors.EmptyModuleDependencyError(
             'Dependent modules dictionary is empty.')
+    dependencies = project.source_path
     rebuild_targets = set()
     for module_name, module_data in project.dep_modules.items():
-        module = ModuleData(module_name, module_data, depth)
+        module = _generate_moduledata(module_name, module_data, ide_name,
+                                      project.project_relative_path, depth)
         module.locate_sources_path()
-        project.source_path['source_folder_path'].update(module.src_dirs)
-        project.source_path['test_folder_path'].update(module.test_dirs)
-        project.source_path['jar_path'].update(module.jar_files)
-        # Collecting the jar files of default core modules as dependencies.
-        if constant.KEY_DEP in module_data:
-            project.source_path['jar_path'].update([
-                x for x in module_data[constant.KEY_DEP]
-                if common_util.is_target(x, _TARGET_LIBS)
-            ])
+        dependencies['source_folder_path'].update(module.src_dirs)
+        dependencies['test_folder_path'].update(module.test_dirs)
+        _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)
-            locate_source(project, verbose, depth, build=False)
+            locate_source(project, verbose, depth, ide_name, build=False)
         else:
             logging.warning('Jar files or modules build failed:\n\t%s.',
                             '\n\t'.join(rebuild_targets))
 
 
 def _build_dependencies(verbose, rebuild_targets):
-    """Build given modules when the jars of the modules don't exist.
+    """Build the jar or srcjar files of the modules if it don't exist.
 
     Args:
         verbose: A boolean, if true displays full build output.
         rebuild_targets: A list of jar or srcjar files which do not exist.
     """
-    logging.info(('Ready to build the modules for generating R.java or java '
-                  'file for AIDL/logtags files.'))
+    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):
@@ -168,8 +167,66 @@
         print('\n{} {}\n'.format(COLORED_INFO('Warning:'), message))
 
 
+def _generate_moduledata(module_name, module_data, ide_name, project_relpath,
+                         depth):
+    """Generate a module class to collect dependencies in IntelliJ or Eclipse.
+
+    Args:
+        module_name: Name of the module.
+        module_data: A dictionary holding a module information.
+        ide_name: A string stands for the IDE name.
+        project_relpath: A string stands for the project's relative path.
+        depth: An integer shows the depth of module dependency referenced by
+               source. Zero means the max module depth.
+
+    Returns:
+        A ModuleData class.
+    """
+    if ide_name == constant.IDE_ECLIPSE:
+        return EclipseModuleData(module_name, module_data, project_relpath)
+    return ModuleData(module_name, module_data, depth)
+
+
+def _append_jars_as_dependencies(dependent_data, module):
+    """Add given module's jar files into dependent_data as dependencies.
+
+    Args:
+        dependent_data: A dictionary contains the dependent source paths and
+                        jar files.
+        module: A ModuleData instance.
+    """
+    if module.jar_files:
+        dependent_data['jar_path'].update(module.jar_files)
+        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:
+        dependent_data['jar_path'].update([
+            x for x in module.module_data[constant.KEY_DEP]
+            if common_util.is_target(x, _TARGET_LIBS)
+        ])
+
+
 class ModuleData():
-    """ModuleData class."""
+    """ModuleData class.
+
+    Attributes:
+        All following relative paths stand for the path relative to the android
+        repo root.
+
+        module_path: A string of the relative path to the module.
+        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.
+        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
+                       relative paths which are ready to be rebuld.
+        missing_jars: A set to keep the jar file relative paths if it doesn't
+                      exist.
+        specific_soong_path: A string of the relative path to the module's
+                             intermediates folder under out/.
+    """
 
     def __init__(self, module_name, module_data, depth):
         """Initialize ModuleData.
@@ -437,13 +494,17 @@
         """
         if _KEY_JARS in self.module_data and self.module_data[_KEY_JARS]:
             for jar_name in self.module_data[_KEY_JARS]:
-                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 'prebuilt.jar' in jar_name:
-                    rel_path = self._get_jar_path_from_prebuilts(jar_name)
-                    if rel_path:
-                        jar_path = rel_path
-                self._append_jar_file(jar_path)
+                if self._check_key(_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'):
+                        rel_path = self._get_jar_path_from_prebuilts(jar_name)
+                        if rel_path:
+                            jar_path = rel_path
+                    self._append_jar_file(jar_path)
 
     @staticmethod
     def _get_jar_path_from_prebuilts(jar_name):
@@ -500,3 +561,66 @@
             self._collect_r_srcs_paths()
         if self.referenced_by_jar and self.missing_jars:
             self.build_targets |= self.missing_jars
+
+
+class EclipseModuleData(ModuleData):
+    """Deal with modules data for Eclipse
+
+    Only project target modules use source folder type and the other ones use
+    jar as their source. We'll combine both to establish the whole project's
+    dependencies. If the source folder used to build dependency jar file exists
+    in Android, we should provide the jar file path as <linkedResource> item in
+    source data.
+    """
+
+    def __init__(self, module_name, module_data, project_relpath):
+        """Initialize EclipseModuleData.
+
+        Only project target modules apply source folder type, so set the depth
+        of module referenced by source to 0.
+
+        Args:
+            module_name: String type, name of the module.
+            module_data: A dictionary contains a module information.
+            project_relpath: A string stands for the project's relative path.
+        """
+        super().__init__(module_name, module_data, depth=0)
+        self.is_project = common_util.is_project_path_relative_module(
+            module_data, project_relpath)
+
+    def locate_sources_path(self):
+        """Locate source folders' paths or jar files.
+
+        Only collect source folders for the project modules and collect jar
+        files for the other dependent modules.
+        """
+        if self.is_project:
+            self._locate_project_source_path()
+        else:
+            self._locate_jar_path()
+        if self.referenced_by_jar and self.missing_jars:
+            self.build_targets |= self.missing_jars
+
+    def _locate_project_source_path(self):
+        """Locate the source folder paths of the project module.
+
+        A project module is the target modules or paths that users key in
+        aidegen command. Collecting the source folders is necessary for
+        developers to edit code. And also collect the central R folder for the
+        dependency of resources.
+        """
+        self._collect_srcs_paths()
+        self._collect_r_srcs_paths()
+
+    def _locate_jar_path(self):
+        """Locate the jar path of the module.
+
+        Use jar files for dependency modules for Eclipse. Collect the jar file
+        path with different cases.
+        """
+        if self._check_jarjar_rules_exist():
+            self._append_jar_from_installed(self.specific_soong_path)
+        elif self._check_jars_exist():
+            self._set_jars_jarfile()
+        else:
+            self._append_jar_from_installed()
diff --git a/aidegen/lib/source_locator_unittest.py b/aidegen/lib/source_locator_unittest.py
index e0669e1..af82732 100644
--- a/aidegen/lib/source_locator_unittest.py
+++ b/aidegen/lib/source_locator_unittest.py
@@ -265,21 +265,27 @@
         mock_project_info.source_path = {
             'source_folder_path': set(),
             'test_folder_path': set(),
-            'jar_path': set()
+            'jar_path': set(),
+            'jar_module_path': dict(),
         }
         # 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.
         jar_abspath = os.path.join(test_root_path, generated_jar)
         result_jar = set([generated_jar])
+        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'],
+                             result_jar_module_path)
         finally:
             shutil.rmtree(test_root_path)
 
@@ -296,7 +302,8 @@
         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)
 
 
diff --git a/aidegen/unittest_constants.py b/aidegen/unittest_constants.py
index 8c7f2b5..e68688b 100644
--- a/aidegen/unittest_constants.py
+++ b/aidegen/unittest_constants.py
@@ -24,7 +24,7 @@
 
 import os
 
-from aidegen.constant import ROOT_DIR as rp
+from aidegen.constant import AIDEGEN_ROOT_PATH as rp
 
 # The data below is only for test usage.
 TEST_DATA_PATH = os.path.join(rp, "test_data")  # folder test_data path
diff --git a/pylintrc b/pylintrc
index 59c5cff..7eedeae 100644
--- a/pylintrc
+++ b/pylintrc
@@ -12,7 +12,8 @@
 
 ignored-modules=
     # For sharing modules from atest project.
-    atest
+    atest,
+    metrics
 
 [FORMAT]