am b25ebf3f: am 9bb23fd7: Use new PowerManager API.

* commit 'b25ebf3fc78c98b508f1b51278d017c070998ea8':
  Use new PowerManager API.
diff --git a/cmds/monkey/src/com/android/commands/monkey/MonkeySourceScript.java b/cmds/monkey/src/com/android/commands/monkey/MonkeySourceScript.java
index b2b9b4f..59aeb55 100644
--- a/cmds/monkey/src/com/android/commands/monkey/MonkeySourceScript.java
+++ b/cmds/monkey/src/com/android/commands/monkey/MonkeySourceScript.java
@@ -71,12 +71,18 @@
 
     private long mLastRecordedEventTime = -1;
 
+    // process scripts in line-by-line mode (true) or batch processing mode (false)
+    private boolean mReadScriptLineByLine = false;
+
     private static final boolean THIS_DEBUG = false;
 
     // a parameter that compensates the difference of real elapsed time and
     // time in theory
     private static final long SLEEP_COMPENSATE_DIFF = 16;
 
+    // if this header is present, scripts are read and processed in line-by-line mode
+    private static final String HEADER_LINE_BY_LINE = "linebyline";
+
     // maximum number of events that we read at one time
     private static final int MAX_ONE_TIME_READS = 100;
 
@@ -147,6 +153,15 @@
 
     BufferedReader mBufferedReader;
 
+    // X and Y coordincates of last touch event. Array Index is the pointerId
+    private float mLastX[] = new float[2];
+
+    private float mLastY[] = new float[2];
+
+    private long mScriptStartTime = -1;
+
+    private long mMonkeyStartTime = -1;
+
     /**
      * Creates a MonkeySourceScript instance.
      *
@@ -207,6 +222,8 @@
                     System.err.println(e);
                     return false;
                 }
+            } else if (line.indexOf(HEADER_LINE_BY_LINE) >= 0) {
+                mReadScriptLineByLine = true;
             } else if (line.indexOf(STARTING_DATA_LINE) >= 0) {
                 return true;
             }
@@ -234,6 +251,21 @@
         return MAX_ONE_TIME_READS;
     }
 
+     /**
+      * Reads one line and processes it.
+      *
+      * @return the number of lines read
+      * @throws IOException If there was an error reading the file.
+      */
+    private int readOneLine() throws IOException {
+        String line = mBufferedReader.readLine();
+        if (line == null) {
+            return 0;
+        }
+        line.trim();
+        processLine(line);
+        return 1;
+    }
 
 
 
@@ -306,6 +338,84 @@
             return;
         }
 
+        // Handle trackball or multi-touch  pointer events. pointer ID is the 13th parameter
+        if ((s.indexOf(EVENT_KEYWORD_POINTER) >= 0 || s.indexOf(EVENT_KEYWORD_TRACKBALL) >= 0)
+                && args.length == 13) {
+            try {
+                long downTime = Long.parseLong(args[0]);
+                long eventTime = Long.parseLong(args[1]);
+                int action = Integer.parseInt(args[2]);
+                float x = Float.parseFloat(args[3]);
+                float y = Float.parseFloat(args[4]);
+                float pressure = Float.parseFloat(args[5]);
+                float size = Float.parseFloat(args[6]);
+                int metaState = Integer.parseInt(args[7]);
+                float xPrecision = Float.parseFloat(args[8]);
+                float yPrecision = Float.parseFloat(args[9]);
+                int device = Integer.parseInt(args[10]);
+                int edgeFlags = Integer.parseInt(args[11]);
+                int pointerId = Integer.parseInt(args[12]);
+
+                MonkeyMotionEvent e;
+                if (s.indexOf("Pointer") > 0) {
+                    if (action == MotionEvent.ACTION_POINTER_DOWN) {
+                        e = new MonkeyTouchEvent(MotionEvent.ACTION_POINTER_DOWN
+                                | (pointerId << MotionEvent.ACTION_POINTER_INDEX_SHIFT))
+                                        .setIntermediateNote(true);
+                    } else {
+                        e = new MonkeyTouchEvent(action);
+                    }
+                    if (mScriptStartTime < 0) {
+                        mMonkeyStartTime = SystemClock.uptimeMillis();
+                        mScriptStartTime = eventTime;
+                    }
+                } else {
+                    e = new MonkeyTrackballEvent(action);
+                }
+
+                if (pointerId == 1) {
+                    e.setDownTime(downTime)
+                            .setEventTime(eventTime)
+                            .setMetaState(metaState)
+                            .setPrecision(xPrecision, yPrecision)
+                            .setDeviceId(device)
+                            .setEdgeFlags(edgeFlags)
+                            .addPointer(0, mLastX[0], mLastY[0], pressure, size)
+                            .addPointer(1, x, y, pressure, size);
+                    mLastX[1] = x;
+                    mLastY[1] = y;
+                } else if (pointerId == 0) {
+                    e.setDownTime(downTime)
+                            .setEventTime(eventTime)
+                            .setMetaState(metaState)
+                            .setPrecision(xPrecision, yPrecision)
+                            .setDeviceId(device)
+                            .setEdgeFlags(edgeFlags)
+                            .addPointer(0, x, y, pressure, size);
+                     if(action == MotionEvent.ACTION_POINTER_UP) {
+                         e.addPointer(1, mLastX[1], mLastY[1]);
+                     }
+                     mLastX[0] = x;
+                     mLastY[0] = y;
+                }
+
+                // Dynamically adjust waiting time to ensure that simulated evnets follow
+                // the time tap specified in the script
+                if (mReadScriptLineByLine) {
+                    long curUpTime = SystemClock.uptimeMillis();
+                    long realElapsedTime = curUpTime - mMonkeyStartTime;
+                    long scriptElapsedTime = eventTime - mScriptStartTime;
+                    if (realElapsedTime < scriptElapsedTime) {
+                        long waitDuration = scriptElapsedTime - realElapsedTime;
+                        mQ.addLast(new MonkeyWaitEvent(waitDuration));
+                    }
+                }
+                mQ.addLast(e);
+            } catch (NumberFormatException e) {
+            }
+            return;
+        }
+
         // Handle screen rotation events
         if ((s.indexOf(EVENT_KEYWORD_ROTATION) >= 0) && args.length == 2) {
             try {
@@ -719,7 +829,11 @@
             readHeader();
         }
 
-        linesRead = readLines();
+        if (mReadScriptLineByLine) {
+            linesRead = readOneLine();
+        } else {
+            linesRead = readLines();
+        }
 
         if (linesRead == 0) {
             closeFile();
diff --git a/testrunner/test_defs.xml b/testrunner/test_defs.xml
index 35aea0f..f3928f1 100644
--- a/testrunner/test_defs.xml
+++ b/testrunner/test_defs.xml
@@ -514,12 +514,6 @@
     build_path="external/skia/tests"
     description="Skia tests." />
 
-<!--  Android STL tests -->
-<test-native name="astl"
-    build_path="external/astl/tests"
-    description="Android STL."
-    extra_build_args="ASTL_TESTS=1" />
-
 <!-- Google Test -->
 <test-native name="gtest"
     build_path="external/gtest"
diff --git a/tools/idegen/Android.mk b/tools/idegen/Android.mk
index e76e5b4..d424ab6 100644
--- a/tools/idegen/Android.mk
+++ b/tools/idegen/Android.mk
@@ -2,6 +2,9 @@
 
 include $(CLEAR_VARS)
 
+LOCAL_STATIC_JAVA_LIBRARIES := \
+        guavalib \
+
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_MODULE:= idegen
diff --git a/tools/idegen/index-gen.sh b/tools/idegen/index-gen.sh
new file mode 100755
index 0000000..eadaa98
--- /dev/null
+++ b/tools/idegen/index-gen.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+#
+# Copyright (C) 2012 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.
+#
+# Generates a module index file by searching through android source tree for make files.  The
+# intellij-gen.sh script automatically calls this script the first time or if you delete the
+# generated indexed file.  The only time you need to run this manually is if modules are added or
+# deleted.
+#
+# To use:
+#   index-gen.sh
+#
+# Only tested on linux.  Should work for macs but have not tried.
+#
+set -e
+
+script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+root_dir=`readlink -f -n $script_dir/../../..`
+tmp_file=tmp.txt
+dest_file=module-index.txt
+
+echo "Generating index file $dest_file..."
+start=$(($(date +%s%N) / 1000000))
+find $root_dir -name '*.mk' \( ! -path "$root_dir/build*" -prune \) \
+  \( -exec grep -H '^LOCAL_PACKAGE_NAME ' {} \; \
+  -false -o -exec grep -H '^LOCAL_MODULE ' {} \; \) \
+  > $tmp_file
+sed -e 's/LOCAL_PACKAGE_NAME *:= *//g' -e 's/LOCAL_MODULE *:= *//g' -e 's/\^M*$//g' < $tmp_file > $dest_file
+
+mv $dest_file $tmp_file
+# Exclude specific directories from index here.
+# TODO: make excludes more generic and configurable
+grep -v "^$root_dir/vendor/google" $tmp_file > $dest_file
+
+rm $tmp_file
+end=$(($(date +%s%N) / 1000000))
+elapse=$(($end - $start))
+echo "Took ${elapse}ms"
diff --git a/tools/idegen/intellij-gen.sh b/tools/idegen/intellij-gen.sh
new file mode 100755
index 0000000..7f00eba
--- /dev/null
+++ b/tools/idegen/intellij-gen.sh
@@ -0,0 +1,60 @@
+#!/bin/bash
+#
+# Copyright (C) 2012 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.
+#
+# To use:
+#   intellij-gen.sh <module name>
+#
+# where module name is the LOCAL_PACKAGE_NAME in Android.mk for the project.
+#
+# For example, to generate a project for Contacts, use:
+#   intellij-gen.sh Contacts
+#
+# The project directory (.idea) will be put in the root directory of the module.  Sharable iml
+# files will be put into each respective module directory.
+#
+# Only tested on linux.  Should work for macs but have not tried.
+#
+set -e
+
+script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+root_dir=`readlink -f -n $script_dir/../../..`
+index_file=$script_dir/module-index.txt
+idegenjar=$root_dir/out/host/common/obj/JAVA_LIBRARIES/idegen_intermediates/javalib.jar
+
+progname=`basename $0`
+if [ $# -ne 1 ]
+then
+    echo "Usage: $progname <module_name>"
+    exit 1
+fi
+
+module_name=$1
+
+if [ ! -e "$index_file" ]; then
+  echo "Module index file missing; generating this is only done the first time."
+  echo "If any dependencies change, you should generate a new index file by running index-gen.sh."
+  $script_dir/index-gen.sh
+fi
+
+echo "Checking for $idegenjar"
+if [ -e "$idegenjar" ]; then
+  echo "Generating project files for $module_name"
+  cmd="java -cp $idegenjar com.android.idegen.IntellijProject $index_file $module_name"
+  echo $cmd
+  $cmd
+else
+  echo "Couldn't find idegen.jar. Please run make first."
+fi
diff --git a/tools/idegen/src/com/android/idegen/AggregatedModule.java b/tools/idegen/src/com/android/idegen/AggregatedModule.java
new file mode 100644
index 0000000..1497da7
--- /dev/null
+++ b/tools/idegen/src/com/android/idegen/AggregatedModule.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2012 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 com.android.idegen;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.logging.Logger;
+
+/**
+ * Module while is a composition of many other modules.
+ * <p>
+ * This is needed since intellij does not allow two modules to share the same content root.
+ */
+public class AggregatedModule extends Module {
+
+    private static final Logger logger = Logger.getLogger(AggregatedModule.class.getName());
+
+    private String aggregatedModuleName;
+    private Set<Module> modules;
+    private HashSet<String> directDependencies = Sets.newHashSet();
+
+    public AggregatedModule(String aggregatedName, Set<Module> modules) {
+        this.aggregatedModuleName = Preconditions.checkNotNull(aggregatedName);
+        this.modules = Preconditions.checkNotNull(modules);
+    }
+
+    public void build() throws IOException {
+        // Create an iml file that contains all the srcs of modules.
+        buildDependentModules();
+        buildDirectDependencies();
+        //buildImlFile();
+    }
+
+    @Override
+    protected File getDir() {
+        // All modules should be in the same directory so just pull the first.
+        return modules.iterator().next().getDir();
+    }
+
+    @Override
+    protected boolean isAndroidModule() {
+        for (Module module : modules) {
+            if (module.isAndroidModule()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    protected List<File> getIntermediatesDirs() {
+        List<File> result = Lists.newArrayList();
+        for (Module module : modules) {
+            Iterables.addAll(result, module.getIntermediatesDirs());
+        }
+        return result;
+    }
+
+    public void buildDirectDependencies() {
+        for (Module module : modules) {
+            Set<String> deps = module.getDirectDependencies();
+            directDependencies.addAll(deps);
+        }
+    }
+
+    @Override
+    public Set<String> getDirectDependencies() {
+        return directDependencies;
+    }
+
+    @Override
+    protected ImmutableList<File> getSourceDirs() {
+        ImmutableList.Builder<File> builder = ImmutableList.builder();
+        for (Module module : modules) {
+            builder.addAll(module.getSourceDirs());
+        }
+        return builder.build();
+    }
+
+
+    @Override
+    protected ImmutableList<File> getExcludeDirs() {
+        ImmutableList.Builder<File> builder = ImmutableList.builder();
+        for (Module module : modules) {
+            builder.addAll(module.getExcludeDirs());
+        }
+        return builder.build();
+    }
+
+    @Override
+    public Set<File> getAllDependentImlFiles() {
+        Set<File> result = Sets.newHashSet();
+        for (Module module : modules) {
+            result.addAll(module.getAllDependentImlFiles());
+        }
+        return result;
+    }
+
+    @Override
+    public File getRepoRoot() {
+        return modules.iterator().next().getRepoRoot();
+    }
+
+    @Override
+    public String getName() {
+        return aggregatedModuleName;
+    }
+
+}
diff --git a/tools/idegen/src/com/android/idegen/Constants.java b/tools/idegen/src/com/android/idegen/Constants.java
new file mode 100644
index 0000000..f541dd4
--- /dev/null
+++ b/tools/idegen/src/com/android/idegen/Constants.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2012 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 com.android.idegen;
+
+import java.nio.charset.Charset;
+
+/**
+ * Constants
+ */
+public class Constants {
+
+    public static final Charset CHARSET = Charset.forName("UTF-8");
+
+    public static final String REL_TEMPLATE_DIR = "development/tools/idegen/templates";
+    public static final String REL_MODULES_TEMPLATE = REL_TEMPLATE_DIR + "/idea/modules.xml";
+    public static final String REL_VCS_TEMPLATE = REL_TEMPLATE_DIR + "/idea/vcs.xml";
+    public static final String REL_IML_TEMPLATE = REL_TEMPLATE_DIR + "/module-template.iml";
+
+    public static final String REL_OUT_APP_DIR = "out/target/common/obj/APPS";
+
+    public static final String FRAMEWORK_MODULE = "framework";
+    public static final String[] AUTO_DEPENDENCIES = new String[]{
+            FRAMEWORK_MODULE, "libcore"
+    };
+    public static final String[] DIRS_WITH_AUTO_DEPENDENCIES = new String[] {
+      "packages", "vendor", "frameworks/ex", "frameworks/opt", "frameworks/support"
+    };
+
+    // Framework needs a special constant for it's intermediates because it does not follow
+    // normal conventions.
+    public static final String FRAMEWORK_INTERMEDIATES = "framework-res_intermediates";
+}
diff --git a/tools/idegen/src/com/android/idegen/DirectorySearch.java b/tools/idegen/src/com/android/idegen/DirectorySearch.java
new file mode 100644
index 0000000..1bbf99f
--- /dev/null
+++ b/tools/idegen/src/com/android/idegen/DirectorySearch.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2012 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 com.android.idegen;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.util.HashSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Find directories utility.
+ */
+public class DirectorySearch {
+
+    private static final HashSet<String> SOURCE_DIRS = Sets.newHashSet();
+    static {
+        SOURCE_DIRS.add("src");
+        SOURCE_DIRS.add("java");
+    }
+
+    private static final Pattern EXCLUDE_PATTERN = Pattern.compile("values-..(-.*)*");
+
+    private static File repoRoot = null;
+
+    /**
+     * Find the repo root.  This is the root branch directory of a full repo checkout.
+     *
+     * @param file any file inside the root.
+     * @return the root directory.
+     */
+    public static File findRepoRoot(File file) {
+        Preconditions.checkNotNull(file);
+        if (repoRoot != null) {
+            return repoRoot;
+        }
+
+        if (file.isDirectory()) {
+            File[] files = file.listFiles(new FilenameFilter() {
+                @Override
+                public boolean accept(File dir, String name) {
+                   if (".repo".equals(name)) {
+                       return true;
+                   }
+                   return false;
+                }
+            });
+            if (files.length > 0) {
+                repoRoot = file;
+                return file;
+            }
+        }
+        File parent = file.getParentFile();
+        if (parent == null) {
+            return null;
+        }
+        return findRepoRoot(parent);
+    }
+
+    /**
+     * Find all source directories from a given root file.
+     *
+     * If the root file is a file, the directory of that file will be used as the starting
+     * location.
+     *
+     * @param file The starting location. Can be a file or directory.
+     * @return List of
+     */
+    public static ImmutableList<File> findSourceDirs(File file) {
+        Preconditions.checkNotNull(file);
+        if (!file.exists()) {
+            return ImmutableList.of();
+        }
+        if (!file.isDirectory()) {
+            file = file.getParentFile();
+        }
+        ImmutableList.Builder<File> builder = ImmutableList.builder();
+        File[] children = file.listFiles();
+        for (File child : children) {
+            if (child.isDirectory()) {
+                if (SOURCE_DIRS.contains(child.getName())) {
+                    builder.add(child);
+                } else {
+                    ImmutableList<File> dirs = findSourceDirs(child);
+                    builder.addAll(dirs);
+                }
+            }
+        }
+
+        return builder.build();
+    }
+
+    public static ImmutableList<File> findExcludeDirs(File file) {
+        Preconditions.checkNotNull(file);
+        if (!file.exists()) {
+            return ImmutableList.of();
+        }
+        if (!file.isDirectory()) {
+            file = file.getParentFile();
+        }
+        ImmutableList.Builder<File> builder = ImmutableList.builder();
+        // Go into the res folder
+        File resFile = new File(file, "res");
+        if (resFile.exists()) {
+
+            File[] children = resFile.listFiles();
+            for (File child : children) {
+                if (child.isDirectory()) {
+                    Matcher matcher = EXCLUDE_PATTERN.matcher(child.getName());
+                    if (matcher.matches()) {
+                        // Exclude internationalization language folders.
+                        // ex: values-zh
+                        // But don't exclude values-land.  Assume all language folders are two
+                        // letters.
+                        builder.add(child);
+                    }
+                }
+            }
+        }
+
+        return builder.build();
+    }
+}
diff --git a/tools/idegen/src/com/android/idegen/FrameworkModule.java b/tools/idegen/src/com/android/idegen/FrameworkModule.java
new file mode 100644
index 0000000..bfcd1fa
--- /dev/null
+++ b/tools/idegen/src/com/android/idegen/FrameworkModule.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2012 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 com.android.idegen;
+
+import com.google.common.collect.ImmutableList;
+
+import java.io.File;
+
+/**
+ * Special module used for framework to build one off resource directory.
+ */
+public class FrameworkModule extends StandardModule {
+
+    public FrameworkModule(String moduleName, String makeFile) {
+        super(Constants.FRAMEWORK_MODULE, makeFile, true);
+    }
+
+    @Override
+    protected String buildIntermediates() {
+        StringBuilder sb = new StringBuilder();
+        File intermediates = new File(repoRoot,
+                Constants.REL_OUT_APP_DIR + File.separator +  Constants.FRAMEWORK_INTERMEDIATES);
+        ImmutableList<File> intermediateSrcDirs = DirectorySearch.findSourceDirs(intermediates);
+        sb.append("    <content url=\"file://").append(intermediates).append("\">\n");
+        for (File src : intermediateSrcDirs) {
+            sb.append("      <sourceFolder url=\"file://")
+                    .append(src.getAbsolutePath()).append("\" isTestSource=\"false\" />\n");
+        }
+        sb.append("    </content>\n");
+        return sb.toString();
+    }
+}
diff --git a/tools/idegen/src/com/android/idegen/IntellijProject.java b/tools/idegen/src/com/android/idegen/IntellijProject.java
new file mode 100644
index 0000000..d5564d5
--- /dev/null
+++ b/tools/idegen/src/com/android/idegen/IntellijProject.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2012 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 com.android.idegen;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Sets;
+import com.google.common.io.Files;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.logging.Logger;
+
+/**
+ * Build Intellij projects.
+ */
+public class IntellijProject {
+
+    private static final Logger logger = Logger.getLogger(IntellijProject.class.getName());
+
+    ModuleCache cache = ModuleCache.getInstance();
+    File indexFile;
+    File repoRoot;
+    File projectIdeaDir;
+    String moduleName;
+
+    public IntellijProject(String indexFile, String moduleName) {
+        this.indexFile = new File(Preconditions.checkNotNull(indexFile));
+        this.moduleName = Preconditions.checkNotNull(moduleName);
+    }
+
+    private void init() throws IOException {
+        repoRoot = DirectorySearch.findRepoRoot(indexFile);
+        cache.init(indexFile);
+    }
+
+    public void build() throws IOException {
+        init();
+        buildFrameWorkModule();
+
+        // First pass, find all dependencies and cache them.
+        Module module = cache.getAndCache(moduleName);
+        projectIdeaDir = new File(module.getDir(), ".idea");
+        projectIdeaDir.mkdir();
+        copyTemplates();
+
+        // Second phase, build aggregate modules.
+        Set<String> deps = module.getAllDependencies();
+        for (String dep : deps) {
+            cache.buildAndCacheAggregatedModule(dep);
+        }
+
+        // Third phase, replace individual modules with aggregated modules
+        Iterable<Module> modules = cache.getModules();
+        for (Module mod : modules) {
+            replaceWithAggregate(mod);
+        }
+
+        // Finally create iml files for dependencies
+        for (Module mod : modules) {
+            mod.buildImlFile();
+        }
+
+        createModulesFile(module);
+        createVcsFile(module);
+        createNameFile(moduleName);
+    }
+
+    private void replaceWithAggregate(Module module) {
+        replaceWithAggregate(module.getDirectDependencies(), module.getName());
+        replaceWithAggregate(module.getAllDependencies(), module.getName());
+
+    }
+
+    private void replaceWithAggregate(Set<String> deps, String moduleName) {
+        for (String dep : Sets.newHashSet(deps)) {
+            String replacement = cache.getAggregateReplacementName(dep);
+            if (replacement != null) {
+
+                deps.remove(dep);
+                // There could be dependencies on self due to aggregation.
+                // Only add if the replacement is not self.
+                if (!replacement.equals(moduleName)) {
+                    deps.add(replacement);
+                }
+            }
+        }
+    }
+
+    /**
+     * Framework module needs special handling due to one off resource path:
+     *   frameworks/base/Android.mk
+     */
+    private void buildFrameWorkModule() throws IOException {
+        String makeFile = cache.getMakeFile(Constants.FRAMEWORK_MODULE);
+        logger.info("makefile: " + makeFile);
+        StandardModule frameworkModule = new FrameworkModule(Constants.FRAMEWORK_MODULE, makeFile);
+        frameworkModule.build();
+        cache.put(frameworkModule);
+    }
+
+    private void createModulesFile(Module module) throws IOException {
+        String modulesContent = Files.toString(new File(repoRoot, Constants.REL_MODULES_TEMPLATE),
+                Constants.CHARSET);
+        StringBuilder sb = new StringBuilder();
+        File moduleIml = module.getImlFile();
+        sb.append("      <module fileurl=\"file://").append(moduleIml.getAbsolutePath())
+                .append("\" filepath=\"").append(moduleIml.getAbsolutePath()).append("\" />\n");
+        for (String name : module.getAllDependencies()) {
+            Module mod = cache.getAndCache(name);
+            File iml = mod.getImlFile();
+            sb.append("      <module fileurl=\"file://").append(iml.getAbsolutePath())
+                    .append("\" filepath=\"").append(iml.getAbsolutePath()).append("\" />\n");
+        }
+        modulesContent = modulesContent.replace("@MODULES@", sb.toString());
+
+        File out = new File(projectIdeaDir, "modules.xml");
+        logger.info("Creating " + out.getAbsolutePath());
+        Files.write(modulesContent, out, Constants.CHARSET);
+    }
+
+    private void createVcsFile(Module module) throws IOException {
+        String vcsTemplate = Files.toString(new File(module.getRepoRoot(),
+                Constants.REL_VCS_TEMPLATE), Constants.CHARSET);
+
+        StringBuilder sb = new StringBuilder();
+        for (String name : module.getAllDependencies()) {
+            Module mod = cache.getAndCache(name);
+            File dir = mod.getDir();
+            File gitRoot = new File(dir, ".git");
+            if (gitRoot.exists()) {
+                sb.append("    <mapping directory=\"").append(dir.getAbsolutePath())
+                        .append("\" vcs=\"Git\" />\n");
+            }
+        }
+        vcsTemplate = vcsTemplate.replace("@VCS@", sb.toString());
+        Files.write(vcsTemplate, new File(projectIdeaDir, "vcs.xml"), Constants.CHARSET);
+    }
+
+    private void createNameFile(String name) throws IOException {
+        File out =  new File(projectIdeaDir, ".name");
+        Files.write(name, out, Constants.CHARSET);
+    }
+
+    private void copyTemplates() throws IOException {
+        File templateDir = new File(repoRoot,
+                Constants.REL_TEMPLATE_DIR + File.separatorChar + "idea");
+        copyTemplates(templateDir, projectIdeaDir);
+    }
+
+    private void copyTemplates(File fromDir, File toDir) throws IOException {
+        toDir.mkdir();
+        File[] files = fromDir.listFiles();
+        for (File file : files) {
+            if (file.isDirectory()) {
+                File destDir = new File(toDir, file.getName());
+                copyTemplates(file, destDir);
+            } else {
+                File toFile = new File(toDir, file.getName());
+                logger.info("copying " + file.getAbsolutePath() + " to " +
+                        toFile.getAbsolutePath());
+                Files.copy(file, toFile);
+            }
+        }
+    }
+
+    public static void main(String[] args) {
+        logger.info("Args: " + Arrays.toString(args));
+
+        String indexFile = args[0];
+        String module = args[1];
+
+        IntellijProject intellij = new IntellijProject(indexFile, module);
+        try {
+            intellij.build();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/tools/idegen/src/com/android/idegen/MakeFileParser.java b/tools/idegen/src/com/android/idegen/MakeFileParser.java
new file mode 100644
index 0000000..3594d50
--- /dev/null
+++ b/tools/idegen/src/com/android/idegen/MakeFileParser.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2012 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 com.android.idegen;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.io.Files;
+import com.google.common.io.LineProcessor;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.logging.Logger;
+
+/**
+ * Parses the make files and finds the appropriate section a given module.
+ */
+public class MakeFileParser {
+
+    private static final Logger logger = Logger.getLogger(MakeFileParser.class.getName());
+    public static final String VALUE_DELIMITER = "|";
+
+    private enum State {
+        NEW, CONTINUE
+    }
+    private enum ModuleNameKey {
+        LOCAL_PACKAGE_NAME,
+        LOCAL_MODULE
+    };
+
+    private File makeFile;
+    private String moduleName;
+    private HashMap<String, String> values;
+
+    /**
+     * Create a parser for a given make file and module name.
+     * <p>
+     * A make file may contain multiple modules.
+     *
+     * @param makeFile The make file to parse.
+     * @param moduleName The module to extract.
+     */
+    public MakeFileParser(File makeFile, String moduleName) {
+        this.makeFile = Preconditions.checkNotNull(makeFile);
+        this.moduleName = Preconditions.checkNotNull(moduleName);
+    }
+
+    public Iterable<String> getValues(String key) {
+        String str = values.get(key);
+        if (str == null) {
+            return null;
+        }
+        return Splitter.on(VALUE_DELIMITER)
+                .trimResults()
+                .omitEmptyStrings()
+                .split(str);
+    }
+
+    /**
+     * Extracts the relevant portion of the make file and converts into key value pairs.
+     * <p>
+     * Since each make file may contain multiple build targets (modules), this method will determine
+     * which part is the correct portion for the given module name.
+     *
+     * @throws IOException
+     */
+    public void parse() throws IOException {
+        values = Maps.newHashMap();
+        logger.info("Parsing " + makeFile.getAbsolutePath() + " for module " + moduleName);
+
+        Files.readLines(makeFile, Charset.forName("UTF-8"), new LineProcessor<Object>() {
+
+            private String key;
+
+            private State state = State.NEW;
+
+            @Override
+            public boolean processLine(String line) throws IOException {
+                String trimmed = line.trim();
+                if (Strings.isNullOrEmpty(trimmed)) {
+                    state = State.NEW;
+                    return true;
+                }
+                if (trimmed.equals("include $(CLEAR_VARS)")) {
+                    // See if we are in the right module.
+                    if (moduleName.equals(getModuleName())) {
+                        return false;
+                    } else {
+                        values.clear();
+                    }
+                } else {
+                    switch (state) {
+                        case NEW:
+                            trimmed = checkContinue(trimmed);
+                            if (trimmed.contains("=")) {
+                                String[] arr;
+                                if (trimmed.contains(":")) {
+                                    arr = trimmed.split(":=");
+                                } else {
+                                    arr = trimmed.split("\\+=");
+                                }
+                                if (arr.length > 2) {
+                                    logger.info("Malformed line " + line);
+                                } else {
+                                    // Store the key in case the line continues
+                                    this.key = arr[0].trim();
+                                    if (arr.length == 2) {
+                                        // There may be multiple values on one line.
+                                        List<String> valuesArr = tokenizeValue(arr[1].trim());
+                                        for (String value : valuesArr) {
+                                            appendValue(this.key, value);
+                                        }
+
+                                    }
+                                }
+                            } else {
+                                //logger.info("Skipping line " + line);
+                            }
+                            break;
+                        case CONTINUE:
+                            // append
+                            trimmed = checkContinue(trimmed);
+                            appendValue(key, trimmed);
+                            break;
+                        default:
+
+                    }
+                }
+                return true;
+            }
+
+            private List<String> tokenizeValue(String value) {
+                // Value may contain function calls such as "$(call all-java-files-under)".
+                // Tokens are separated by spaces unless it's between parens.
+                StringBuilder token = new StringBuilder();
+                ArrayList<String> tokens = Lists.newArrayList();
+                int parenCount = 0;
+                for (int i = 0; i < value.length(); i++) {
+                    char ch = value.charAt(i);
+                    if (parenCount == 0 && ch == ' ') {
+                        // Not in a paren and delimiter encountered.
+                        // end token
+                        if (token.length() > 0) {
+                            tokens.add(token.toString());
+                            token = new StringBuilder();
+                        }
+                    } else {
+                        token.append(ch);
+                    }
+                    if (ch == '(') {
+                        parenCount++;
+                    } else if (ch == ')') {
+                        parenCount--;
+                    }
+                }
+                // end of line check
+                if (token.length() > 0) {
+                    tokens.add(token.toString());
+                }
+                return tokens;
+            }
+
+            private String getModuleName() {
+                for (ModuleNameKey key : ModuleNameKey.values()) {
+                    String name = values.get(key.name());
+                    if (name != null) {
+                        return name;
+                    }
+                }
+                return null;
+            }
+
+            @Override
+            public Object getResult() {
+                return null;
+            }
+
+            private String checkContinue(String value) {
+                // Check for continuation character
+                if (value.charAt(value.length() - 1) == '\\') {
+                    state = State.CONTINUE;
+                    return value.substring(0, value.length() - 1);
+                }
+                state = State.NEW;
+                return value;
+            }
+
+            /**
+             * Add a value to the hash map. If the key already exists, will append instead of
+             * over-writing the existing value.
+             *
+             * @param key The hashmap key
+             * @param newValue The value to append.
+             */
+            private void appendValue(String key, String newValue) {
+                String value = values.get(key);
+                if (value == null) {
+                    values.put(key, newValue);
+                } else {
+                    values.put(key, value + VALUE_DELIMITER + newValue);
+                }
+            }
+        });
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(this)
+                .add("values", values)
+                .toString();
+    }
+}
diff --git a/tools/idegen/src/com/android/idegen/Module.java b/tools/idegen/src/com/android/idegen/Module.java
new file mode 100644
index 0000000..7ba42a3
--- /dev/null
+++ b/tools/idegen/src/com/android/idegen/Module.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2012 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 com.android.idegen;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
+import com.google.common.io.Files;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+import java.util.logging.Logger;
+
+/**
+ * Super class for all modules.
+ */
+public abstract class Module {
+
+    private static final Logger logger = Logger.getLogger(Module.class.getName());
+
+    /**
+     * All possible attributes for the make file.
+     */
+    protected enum Key {
+        LOCAL_STATIC_JAVA_LIBRARIES,
+        LOCAL_JAVA_LIBRARIES,
+        LOCAL_SRC_FILES
+    }
+
+    ModuleCache moduleCache = ModuleCache.getInstance();
+    private File imlFile;
+    private Set<String> allDependencies = Sets.newHashSet(); // direct + indirect
+    private Set<File> allDependentImlFiles = Sets.newHashSet();
+
+    protected abstract void build() throws IOException;
+    protected abstract String getName();
+    protected abstract File getDir();
+    protected abstract boolean isAndroidModule();
+    protected abstract List<File> getIntermediatesDirs();
+    public abstract Set<String> getDirectDependencies();
+    protected abstract ImmutableList<File> getSourceDirs();
+    protected abstract ImmutableList<File> getExcludeDirs();
+    public abstract File getRepoRoot();
+
+    public void buildImlFile() throws IOException {
+        String imlTemplate = Files.toString(new File(getRepoRoot(), Constants.REL_IML_TEMPLATE),
+                Constants.CHARSET);
+
+        String facetXml = "";
+        if (isAndroidModule()) {
+            facetXml = buildAndroidFacet();
+        }
+        imlTemplate = imlTemplate.replace("@FACETS@", facetXml);
+
+        String moduleDir = getDir().getAbsolutePath();
+
+        StringBuilder sourceDirectories = new StringBuilder();
+        sourceDirectories.append("    <content url=\"file://$MODULE_DIR$\">\n");
+        ImmutableList<File> srcDirs = getSourceDirs();
+        for (File src : srcDirs) {
+            String relative = src.getAbsolutePath().substring(moduleDir.length());
+            sourceDirectories.append("      <sourceFolder url=\"file://$MODULE_DIR$")
+                    .append(relative).append("\" isTestSource=\"false\" />\n");
+        }
+        ImmutableList<File> excludeDirs = getExcludeDirs();
+        for (File src : excludeDirs) {
+            String relative = src.getAbsolutePath().substring(moduleDir.length());
+            sourceDirectories.append("      <excludeFolder url=\"file://$MODULE_DIR$")
+                    .append(relative).append("\"/>\n");
+        }
+        sourceDirectories.append("    </content>\n");
+
+        // Intermediates.
+        sourceDirectories.append(buildIntermediates());
+
+        imlTemplate = imlTemplate.replace("@SOURCES@", sourceDirectories.toString());
+
+        StringBuilder moduleDependencies = new StringBuilder();
+        for (String dependency : getDirectDependencies()) {
+            moduleDependencies.append("    <orderEntry type=\"module\" module-name=\"")
+                    .append(dependency).append("\" />\n");
+        }
+        imlTemplate = imlTemplate.replace("@MODULE_DEPENDENCIES@", moduleDependencies.toString());
+
+        imlFile = new File(moduleDir, getName() + ".iml");
+        logger.info("Creating " + imlFile.getAbsolutePath());
+        Files.write(imlTemplate, imlFile, Constants.CHARSET);
+    }
+
+    protected String buildIntermediates() {
+        StringBuilder sb = new StringBuilder();
+        for (File intermediatesDir : getIntermediatesDirs()) {
+            sb.append("    <content url=\"file://").append(intermediatesDir).append("\">\n");
+            sb.append("      <sourceFolder url=\"file://")
+                    .append(intermediatesDir.getAbsolutePath())
+                    .append("\" isTestSource=\"false\" />\n");
+            sb.append("    </content>\n");
+        }
+        return sb.toString();
+    }
+
+    protected void buildDependentModules() throws IOException {
+        Set<String> directDependencies = getDirectDependencies();
+        String[] copy = directDependencies.toArray(new String[directDependencies.size()]);
+        for (String dependency : copy) {
+
+            Module child = moduleCache.getAndCache(dependency);
+            if (child == null) {
+                directDependencies.remove(dependency);
+            } else {
+                addAllDependencies(dependency);
+                addAllDependencies(child.getAllDependencies());
+                //logger.info("Adding iml " + child.getName() + " " + child.getImlFile());
+                allDependentImlFiles.add(child.getImlFile());
+                allDependentImlFiles.addAll(child.getAllDependentImlFiles());
+            }
+        }
+    }
+
+    public File getImlFile() {
+        return imlFile;
+    }
+
+    public Set<String> getAllDependencies() {
+        return allDependencies;
+    }
+
+    public void addAllDependencies(String dependency) {
+        this.allDependencies.add(dependency);
+    }
+
+    public void addAllDependencies(Set<String> dependencies) {
+        this.allDependencies.addAll(dependencies);
+    }
+
+    public Set<File> getAllDependentImlFiles() {
+        return allDependentImlFiles;
+    }
+
+    private String buildAndroidFacet() {
+        // Not sure how to handle android facet for multi-module since there could be more than
+        // one intermediates directory.
+        String dir = getIntermediatesDirs().get(0).getAbsolutePath();
+        String xml = ""
+                + "  <component name=\"FacetManager\">\n"
+                + "    <facet type=\"android\" name=\"Android\">\n"
+                + "      <configuration>\n"
+                + "        <option name=\"GEN_FOLDER_RELATIVE_PATH_APT\" value=\"" + dir + "\" />\n"
+                + "        <option name=\"GEN_FOLDER_RELATIVE_PATH_AIDL\" value=\"" + dir
+                + "\" />\n"
+                + "        <option name=\"MANIFEST_FILE_RELATIVE_PATH\" value=\""
+                + "/AndroidManifest.xml\" />\n"
+                + "        <option name=\"RES_FOLDER_RELATIVE_PATH\" value=\"/res\" />\n"
+                + "        <option name=\"ASSETS_FOLDER_RELATIVE_PATH\" value=\"/assets\" />\n"
+                + "        <option name=\"LIBS_FOLDER_RELATIVE_PATH\" value=\"/libs\" />\n"
+                + "        <option name=\"REGENERATE_R_JAVA\" value=\"true\" />\n"
+                + "        <option name=\"REGENERATE_JAVA_BY_AIDL\" value=\"true\" />\n"
+                + "        <option name=\"USE_CUSTOM_APK_RESOURCE_FOLDER\" value=\"false\" />\n"
+                + "        <option name=\"CUSTOM_APK_RESOURCE_FOLDER\" value=\"\" />\n"
+                + "        <option name=\"USE_CUSTOM_COMPILER_MANIFEST\" value=\"false\" />\n"
+                + "        <option name=\"CUSTOM_COMPILER_MANIFEST\" value=\"\" />\n"
+                + "        <option name=\"APK_PATH\" value=\"\" />\n"
+                + "        <option name=\"LIBRARY_PROJECT\" value=\"false\" />\n"
+                + "        <option name=\"RUN_PROCESS_RESOURCES_MAVEN_TASK\" value=\"true\" />\n"
+                + "        <option name=\"GENERATE_UNSIGNED_APK\" value=\"false\" />\n"
+                + "      </configuration>\n"
+                + "    </facet>\n"
+                + "  </component>\n";
+        return xml;
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(Module.class)
+                .add("name", getName())
+                .add("allDependencies", allDependencies)
+                .add("iml files", allDependentImlFiles)
+                .add("imlFile", imlFile)
+                .toString();
+    }
+}
+
diff --git a/tools/idegen/src/com/android/idegen/ModuleCache.java b/tools/idegen/src/com/android/idegen/ModuleCache.java
new file mode 100644
index 0000000..093242a
--- /dev/null
+++ b/tools/idegen/src/com/android/idegen/ModuleCache.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2012 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 com.android.idegen;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.logging.Logger;
+
+/**
+ * Cache to hold built modules.
+ */
+public class ModuleCache {
+
+    private static final Logger logger = Logger.getLogger(ModuleCache.class.getName());
+
+    private static ModuleCache cache = new ModuleCache();
+
+    ModuleIndexes indexes;
+
+    HashMap<String, Module> modulesByName = Maps.newHashMap();
+
+    private ModuleCache() {
+    }
+
+    public static ModuleCache getInstance() {
+        return cache;
+    }
+
+    public void init(File indexFile) throws IOException {
+        indexes = new ModuleIndexes(indexFile);
+        indexes.build();
+    }
+
+    public Module getAndCache(String moduleName) throws IOException {
+        Preconditions.checkState(indexes != null, "You must call init() first.");
+
+        Module module = modulesByName.get(moduleName);
+        if (module == null) {
+            String makeFile = indexes.getMakeFile(moduleName);
+            if (makeFile == null) {
+                logger.warning("Unable to find make file for module: " + moduleName);
+            } else {
+                module = new StandardModule(moduleName, makeFile);
+                module.build();
+                modulesByName.put(moduleName, module);
+            }
+        }
+        return module;
+    }
+
+    public void buildAndCacheAggregatedModule(String moduleName) throws IOException {
+        if (indexes.isPartOfAggregatedModule(moduleName)) {
+            Set<String> moduleNames = indexes.getAggregatedModules(moduleName);
+            Set<Module> modules = Sets.newHashSet();
+            for (String name : moduleNames) {
+                Module m = modulesByName.get(name);
+                if (m != null) {
+                    modules.add(m);
+                }
+            }
+            String aggregatedName = indexes.getAggregateName(moduleName);
+            AggregatedModule module = new AggregatedModule(aggregatedName, modules);
+            module.build();
+            modulesByName.put(aggregatedName, module);
+        }
+    }
+
+    public Iterable<Module> getModules() {
+        return modulesByName.values();
+    }
+
+    public String getMakeFile(String moduleName) {
+        return indexes.getMakeFile(moduleName);
+    }
+
+    public void put(StandardModule module) {
+        Preconditions.checkNotNull(module);
+        modulesByName.put(module.getName(), module);
+    }
+
+    public String getAggregateReplacementName(String moduleName) {
+        if (indexes.isPartOfAggregatedModule(moduleName)) {
+            return indexes.getAggregateName(moduleName);
+        }
+        return null;
+    }
+}
diff --git a/tools/idegen/src/com/android/idegen/ModuleIndexes.java b/tools/idegen/src/com/android/idegen/ModuleIndexes.java
new file mode 100644
index 0000000..890389f
--- /dev/null
+++ b/tools/idegen/src/com/android/idegen/ModuleIndexes.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2012 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 com.android.idegen;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.common.io.Files;
+import com.google.common.io.LineProcessor;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Mapping of module names to make files.
+ */
+public class ModuleIndexes {
+
+    private static final Logger logger = Logger.getLogger(ModuleIndexes.class.getName());
+
+    private File indexFile;
+    private HashMap<String, String> moduleNameToMakeFileMap;
+    private HashMap<String, Set<String>> makeFileToModuleNamesMap;
+
+    public ModuleIndexes(File indexFile) {
+        this.indexFile = indexFile;
+    }
+
+    public void build() throws IOException {
+
+        moduleNameToMakeFileMap = Maps.newHashMap();
+        makeFileToModuleNamesMap = Maps.newHashMap();
+        logger.info("Building index from " + indexFile.getAbsolutePath());
+        Files.readLines(indexFile, Charset.forName("UTF-8"),
+                new LineProcessor<Object>() {
+                    int count = 0;
+
+                    @Override
+                    public boolean processLine(String line) throws IOException {
+                        count++;
+                        String[] arr = line.split(":");
+                        if (arr.length < 2) {
+                            logger.log(Level.WARNING,
+                                    "Ignoring index line " + count + ". Bad format: " + line);
+                        } else {
+                            String makeFile = arr[0];
+                            String moduleName = arr[1];
+                            moduleNameToMakeFileMap.put(moduleName, makeFile);
+                            append(makeFile, moduleName);
+                        }
+                        return true;
+                    }
+
+                    @Override
+                    public Object getResult() {
+                        return null;
+                    }
+                });
+    }
+
+    private void append(String makeFile, String moduleName) {
+        Set<String> moduleNames = makeFileToModuleNamesMap.get(makeFile);
+        if (moduleNames == null) {
+            moduleNames = Sets.newHashSet();
+            makeFileToModuleNamesMap.put(makeFile, moduleNames);
+        } else {
+            // Create a aggregate module place holder.
+            //moduleNameToMakeFileMap.put(getAggregateName(moduleName), makeFile);
+        }
+        moduleNames.add(moduleName);
+    }
+
+    public String getMakeFile(String moduleName) {
+        Preconditions.checkState(moduleNameToMakeFileMap != null,
+                "Index not built. Call build() first.");
+        return moduleNameToMakeFileMap.get(moduleName);
+    }
+
+    public Set<String> getAggregatedModules(String moduleName) {
+        Preconditions.checkState(makeFileToModuleNamesMap != null,
+                "Index not built. Call build() first.");
+        String makeFile = getMakeFile(moduleName);
+        return makeFileToModuleNamesMap.get(makeFile);
+    }
+
+    public boolean isPartOfAggregatedModule(String moduleName) {
+        String makeFile = getMakeFile(moduleName);
+        if (makeFile == null) {
+            return false;
+        }
+        Set<String> moduleNames = makeFileToModuleNamesMap.get(makeFile);
+        if (moduleNames == null) {
+            return false;
+        }
+        return moduleNames.size() > 1;
+    }
+
+    public String getAggregateName(String moduleName) {
+        String fileName = getMakeFile(moduleName);
+        File file = new File(fileName);
+        return file.getParentFile().getName() + "-aggregate";
+    }
+}
diff --git a/tools/idegen/src/com/android/idegen/StandardModule.java b/tools/idegen/src/com/android/idegen/StandardModule.java
new file mode 100644
index 0000000..dffb95e
--- /dev/null
+++ b/tools/idegen/src/com/android/idegen/StandardModule.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2012 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 com.android.idegen;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Module constructed from a make file.
+ *
+ * TODO: read the make file and understand included source dirs in addition to searching
+ * sub-directories.  Make files can include sources that are not sub-directories.  For example,
+ * the framework module includes sources from:
+ *
+ *   external/libphonenumber/java/src
+ *
+ *   to provide:
+ *
+ *   com.android.i18n.phonenumbers.PhoneNumberUtil;
+ */
+public class StandardModule extends Module {
+
+    private static final Logger logger = Logger.getLogger(StandardModule.class.getName());
+
+    private static final Pattern SRC_PATTERN = Pattern.compile(
+            ".*\\(call all-java-files-under, (.*)\\)");
+
+    String moduleName;
+    File makeFile;
+    File moduleRoot;
+    File repoRoot;
+
+    Set<String> directDependencies = Sets.newHashSet();
+
+    File intermediatesDir;
+    MakeFileParser makeFileParser;
+    boolean searchForSrc;
+
+    public StandardModule(String moduleName, String makeFile) {
+        this(moduleName, new File(makeFile), false);
+    }
+
+    public StandardModule(String moduleName, String makeFile, boolean searchForSrc) {
+        this(moduleName, new File(makeFile), searchForSrc);
+    }
+
+    public StandardModule(String moduleName, File makeFile, boolean searchForSrc) {
+        this.moduleName = moduleName;
+        this.makeFile = makeFile;
+        this.moduleRoot = makeFile.getParentFile();
+        this.repoRoot = DirectorySearch.findRepoRoot(makeFile);
+        this.intermediatesDir = new File(repoRoot.getAbsolutePath() + File.separator +
+                Constants.REL_OUT_APP_DIR + File.separator + getName() + "_intermediates" +
+                File.separator + "src");
+        this.searchForSrc = searchForSrc;
+
+        // TODO: auto-detect when framework dependency is needed instead of using coded list.
+        for (String dir : Constants.DIRS_WITH_AUTO_DEPENDENCIES) {
+            // length + 2 to account for slash
+            boolean isDir = makeFile.getAbsolutePath().startsWith(repoRoot + "/" + dir);
+            if (isDir) {
+                for (String dependency : Constants.AUTO_DEPENDENCIES) {
+                    this.directDependencies.add(dependency);
+                }
+            }
+        }
+
+        makeFileParser = new MakeFileParser(makeFile, moduleName);
+    }
+
+    protected void build() throws IOException {
+        makeFileParser.parse();
+        buildDependencyList();
+        buildDependentModules();
+        //buildImlFile();
+        logger.info("Done building module " + moduleName);
+        logger.info(toString());
+    }
+
+    @Override
+    protected File getDir() {
+        return moduleRoot;
+    }
+
+    @Override
+    protected String getName() {
+        return moduleName;
+    }
+
+    @Override
+    protected List<File> getIntermediatesDirs() {
+        return Lists.newArrayList(intermediatesDir);
+    }
+
+    @Override
+    public File getRepoRoot() {
+        return this.repoRoot;
+    }
+
+    public Set<String> getDirectDependencies() {
+        return this.directDependencies;
+    }
+
+    @Override
+    protected ImmutableList<File> getSourceDirs() {
+        ImmutableList<File> srcDirs;
+        if (searchForSrc) {
+            srcDirs = DirectorySearch.findSourceDirs(makeFile);
+        } else {
+            srcDirs = parseSourceFiles(makeFile);
+        }
+        return srcDirs;
+    }
+
+    @Override
+    protected ImmutableList<File> getExcludeDirs() {
+        return DirectorySearch.findExcludeDirs(makeFile);
+    }
+
+    @Override
+    protected boolean isAndroidModule() {
+        File manifest = new File(moduleRoot, "AndroidManifest.xml");
+        return manifest.exists();
+    }
+
+    private ImmutableList<File> parseSourceFiles(File root) {
+        ImmutableList.Builder<File> builder = ImmutableList.builder();
+        File rootDir;
+        if (root.isFile()) {
+            rootDir = root.getParentFile();
+        } else {
+            rootDir = root;
+        }
+
+        Iterable<String> values = makeFileParser.getValues(Key.LOCAL_SRC_FILES.name());
+        if (values != null) {
+            for (String value : values) {
+                Matcher matcher = SRC_PATTERN.matcher(value);
+                if (matcher.matches()) {
+                    String dir = matcher.group(1);
+                    builder.add(new File(rootDir, dir));
+                } else if (value.contains("/")) {
+                    // Treat as individual file.
+                    builder.add(new File(rootDir, value));
+                }
+            }
+        }
+        return builder.build();
+    }
+
+    private void buildDependencyList() {
+        parseDirectDependencies(Key.LOCAL_STATIC_JAVA_LIBRARIES);
+        parseDirectDependencies(Key.LOCAL_JAVA_LIBRARIES);
+    }
+
+    private void parseDirectDependencies(Key key) {
+        Iterable<String> names = makeFileParser.getValues(key.name());
+        if (names != null) {
+            for (String dependency : names) {
+                directDependencies.add(dependency);
+            }
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(getName());
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        StandardModule other = (StandardModule) obj;
+        return Objects.equal(getName(), other.getName());
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(this)
+                .add("super", super.toString())
+                .add("makeFileParser", makeFileParser)
+                .add("directDependencies", Iterables.toString(directDependencies))
+                .toString();
+    }
+}
diff --git a/tools/idegen/templates/idea/compiler.xml b/tools/idegen/templates/idea/compiler.xml
new file mode 100644
index 0000000..9d29283
--- /dev/null
+++ b/tools/idegen/templates/idea/compiler.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="CompilerConfiguration">
+    <option name="DEFAULT_COMPILER" value="Javac" />
+    <resourceExtensions />
+    <wildcardResourcePatterns>
+      <entry name="?*.properties" />
+      <entry name="?*.xml" />
+      <entry name="?*.gif" />
+      <entry name="?*.png" />
+      <entry name="?*.jpeg" />
+      <entry name="?*.jpg" />
+      <entry name="?*.html" />
+      <entry name="?*.dtd" />
+      <entry name="?*.tld" />
+      <entry name="?*.ftl" />
+    </wildcardResourcePatterns>
+    <annotationProcessing enabled="false" useClasspath="true" />
+  </component>
+  <component name="JavacSettings">
+    <option name="MAXIMUM_HEAP_SIZE" value="1024" />
+  </component>
+</project>
diff --git a/tools/idegen/templates/idea/copyright/Apache_2.xml b/tools/idegen/templates/idea/copyright/Apache_2.xml
new file mode 100644
index 0000000..fd0c79b
--- /dev/null
+++ b/tools/idegen/templates/idea/copyright/Apache_2.xml
@@ -0,0 +1,9 @@
+<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" />
+    <option name="keyword" value="Copyright" />
+    <option name="allowReplaceKeyword" value="" />
+    <option name="myName" value="Apache 2" />
+    <option name="myLocal" value="true" />
+  </copyright>
+</component>
\ No newline at end of file
diff --git a/tools/idegen/templates/idea/copyright/profiles_settings.xml b/tools/idegen/templates/idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..7a6be8d
--- /dev/null
+++ b/tools/idegen/templates/idea/copyright/profiles_settings.xml
@@ -0,0 +1,7 @@
+<component name="CopyrightManager">
+  <settings default="">
+    <module2copyright>
+      <element module="Project Files" copyright="Apache 2" />
+    </module2copyright>
+  </settings>
+</component>
\ No newline at end of file
diff --git a/tools/idegen/templates/idea/misc.xml b/tools/idegen/templates/idea/misc.xml
new file mode 100644
index 0000000..d3edec0
--- /dev/null
+++ b/tools/idegen/templates/idea/misc.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ConfigCheckProjectState">
+    <option name="disabledCheckers">
+      <list>
+        <option value="com.google.devtools.intellig.configcheck.JavacHeapChecker" />
+        <option value="com.google.devtools.intellig.configcheck.VcsMappingsChecker" />
+      </list>
+    </option>
+  </component>
+  <component name="ContinuousBuildConfigurationComponent">
+    <builds>
+      <build intervalToCheckBuild="1" buildKey="" buildLabel="" enabled="false" tapBuild="false" />
+    </builds>
+  </component>
+  <component name="DependencyValidationManager">
+    <option name="SKIP_IMPORT_STATEMENTS" value="false" />
+  </component>
+  <component name="EntryPointsManager">
+    <entry_points version="2.0" />
+  </component>
+  <component name="JavadocGenerationManager">
+    <option name="OUTPUT_DIRECTORY" />
+    <option name="OPTION_SCOPE" value="protected" />
+    <option name="OPTION_HIERARCHY" value="true" />
+    <option name="OPTION_NAVIGATOR" value="true" />
+    <option name="OPTION_INDEX" value="true" />
+    <option name="OPTION_SEPARATE_INDEX" value="true" />
+    <option name="OPTION_DOCUMENT_TAG_USE" value="false" />
+    <option name="OPTION_DOCUMENT_TAG_AUTHOR" value="false" />
+    <option name="OPTION_DOCUMENT_TAG_VERSION" value="false" />
+    <option name="OPTION_DOCUMENT_TAG_DEPRECATED" value="true" />
+    <option name="OPTION_DEPRECATED_LIST" value="true" />
+    <option name="OTHER_OPTIONS" value="" />
+    <option name="HEAP_SIZE" />
+    <option name="LOCALE" />
+    <option name="OPEN_IN_BROWSER" value="true" />
+  </component>
+  <component name="Mach LOCAL_PREFIX stripper" stripping="true" />
+  <component name="ProjectResources">
+    <default-html-doctype>http://www.w3.org/1999/xhtml</default-html-doctype>
+  </component>
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="1.6" project-jdk-type="JavaSDK" />
+  <component name="WebServicesPlugin" addRequiredLibraries="true" />
+</project>
+
diff --git a/tools/idegen/templates/idea/modules.xml b/tools/idegen/templates/idea/modules.xml
new file mode 100644
index 0000000..588cc09
--- /dev/null
+++ b/tools/idegen/templates/idea/modules.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+@MODULES@
+    </modules>
+  </component>
+</project>
+
diff --git a/tools/idegen/templates/idea/vcs.xml b/tools/idegen/templates/idea/vcs.xml
new file mode 100644
index 0000000..176af35
--- /dev/null
+++ b/tools/idegen/templates/idea/vcs.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="" vcs="Git" />
+@VCS@
+  </component>
+</project>
diff --git a/tools/idegen/templates/module-template.iml b/tools/idegen/templates/module-template.iml
new file mode 100644
index 0000000..02301f7
--- /dev/null
+++ b/tools/idegen/templates/module-template.iml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+@FACETS@
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+@SOURCES@
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+@MODULE_DEPENDENCIES@
+  </component>
+</module>
+