Merge "Proguard information in readme file was outdated"
diff --git a/.gitignore b/.gitignore
index 17e4ab3..233369b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,14 +15,18 @@
 tests/dependencies/jarProject/build
 tests/flavorlib/*/build
 tests/flavorlibWithFailedTests/*/build
+tests/libProguardJarDep/*/build
+tests/libProguardLibDep/*/build
 tests/libsTest/*/build
 tests/multiproject/*/build
 tests/localJars/*/build
+tests/ndkJniLib/*/build
 tests/proguardLib/*/build
 tests/renderscriptInLib/*/build
 tests/repo/*/build
 tests/sameNamedLibs/*/build
 tests/sameNamedLibs/*/*/build
 tests/tictactoe/*/build
+lint-results
 /repo
 /out
diff --git a/build.gradle b/build.gradle
index 145db7b..b440c7f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -22,6 +22,8 @@
 // rootProject.buildDir is specific to this gradle build.
 buildDir = new File(file(ext.androidHostOut), "tools/build/build")
 
+ext.localRepo = project.hasProperty('localRepo') ? localRepo : "$ext.androidHostOut/repo"
+
 subprojects {
     // Change buildDir first so that all plugins pick up the new value.
     project.buildDir = project.file("$project.parent.buildDir/../$project.name")
@@ -44,7 +46,7 @@
     }
 
     project.ext {
-        baseVersion = '0.5.0'
+        baseVersion = '0.7.0'
     }
 
     task disableTestFailures << {
diff --git a/builder-model/build.gradle b/builder-model/build.gradle
index 10032d6..3e780df 100644
--- a/builder-model/build.gradle
+++ b/builder-model/build.gradle
@@ -1,110 +1,16 @@
 apply plugin: 'java'
 apply plugin: 'maven'
-apply plugin: 'signing'
 apply plugin: 'clone-artifacts'
 
 dependencies {
     compile "com.android.tools:common:$project.ext.baseAndroidVersion"
 }
 
-def getVersion() {
-    if (project.has("release")) {
-        return project.ext.baseVersion
-    }
-
-    return project.ext.baseVersion + '-SNAPSHOT'
-}
-
-version = getVersion()
 archivesBaseName = 'builder-model'
+project.ext.pomName = 'Android Builder Model library'
+project.ext.pomDesc = 'Model for the Builder library.'
+
+apply from: '../publish.gradle'
+apply from: '../javadoc.gradle'
 
 jar.manifest.attributes("Model-Version": "$version")
-
-task publishLocal(type: Upload) {
-    configuration = configurations.archives
-    repositories {
-        mavenDeployer {
-            repository(url: uri("$rootProject.ext.androidHostOut/repo"))
-        }
-    }
-}
-
-project.ext.sonatypeUsername = hasProperty('sonatypeUsername') ? sonatypeUsername : ""
-project.ext.sonatypePassword = hasProperty('sonatypePassword') ? sonatypePassword : ""
-
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            beforeDeployment { MavenDeployment deployment ->
-                if (!project.has("release")) {
-                    throw new StopExecutionException("uploadArchives must be called with the release.gradle init script")
-                }
-
-                if (project.ext.sonatypeUsername.length() == 0 || project.ext.sonatypePassword.length() == 0) {
-                    throw new StopExecutionException("uploadArchives cannot be called without sonatype username and password")
-                }
-
-                signing.signPom(deployment)
-            }
-
-            repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
-                authentication(userName: project.ext.sonatypeUsername, password: project.ext.sonatypePassword)
-            }
-
-            pom.project {
-                name 'Android Builder Model library'
-                description 'Model for the Builder library.'
-                url 'http://tools.android.com'
-                inceptionYear '2007'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "https://android.googlesource.com/platform/tools/build"
-                    connection "git://android.googlesource.com/platform/tools/build.git"
-                }
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
-}
-
-// custom tasks for creating source/javadoc jars
-task sourcesJar(type: Jar, dependsOn:classes) {
-    classifier = 'sources'
-    from sourceSets.main.allSource
-}
-
-javadoc {
-    exclude               "**/internal/**"
-    options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PROTECTED
-
-    title                 "Android Model"
-}
-
-task javadocJar(type: Jar, dependsOn:javadoc) {
-    classifier         'javadoc'
-    from               javadoc.destinationDir
-}
- 
-// add javadoc/source jar tasks as artifacts
-artifacts {
-    archives jar
-    archives sourcesJar
-    archives javadocJar
-}
-
-signing {
-    required { project.has("release") && gradle.taskGraph.hasTask("uploadArchives") }
-    sign configurations.archives
-}
diff --git a/builder-model/src/main/java/com/android/builder/model/AaptOptions.java b/builder-model/src/main/java/com/android/builder/model/AaptOptions.java
index 86192e0..4cb9b36 100644
--- a/builder-model/src/main/java/com/android/builder/model/AaptOptions.java
+++ b/builder-model/src/main/java/com/android/builder/model/AaptOptions.java
@@ -16,7 +16,7 @@
 
 package com.android.builder.model;
 
-import java.util.List;
+import java.util.Collection;
 
 /**
  * Options for aapt.
@@ -30,5 +30,5 @@
     /**
      * Returns the list of values for the -0 (disabled compression) option, or null
      */
-    List<String> getNoCompress();
+    Collection<String> getNoCompress();
 }
diff --git a/builder-model/src/main/java/com/android/builder/model/ArtifactInfo.java b/builder-model/src/main/java/com/android/builder/model/AndroidArtifact.java
similarity index 75%
rename from builder-model/src/main/java/com/android/builder/model/ArtifactInfo.java
rename to builder-model/src/main/java/com/android/builder/model/AndroidArtifact.java
index 4a68755..3cfd2f2 100644
--- a/builder-model/src/main/java/com/android/builder/model/ArtifactInfo.java
+++ b/builder-model/src/main/java/com/android/builder/model/AndroidArtifact.java
@@ -20,12 +20,12 @@
 import com.android.annotations.Nullable;
 
 import java.io.File;
-import java.util.List;
+import java.util.Collection;
 
 /**
- * The information for a generated artifact.
+ * The information for a generated Android artifact.
  */
-public interface ArtifactInfo {
+public interface AndroidArtifact extends BaseArtifact {
 
     /**
      * Returns the output file for this artifact. Depending on whether the project is an app
@@ -73,12 +73,10 @@
     String getSourceGenTaskName();
 
     /**
-     * Returns the name of the task used to generate the artifact.
-     *
-     * @return the name of the task.
+     * The generated manifest for this variant's artifact.
      */
     @NonNull
-    String getAssembleTaskName();
+    File getGeneratedManifest();
 
     /**
      * Returns all the source folders that are generated. This is typically folders for the R,
@@ -87,7 +85,7 @@
      * @return a list of folders.
      */
     @NonNull
-    List<File> getGeneratedSourceFolders();
+    Collection<File> getGeneratedSourceFolders();
 
     /**
      * Returns all the resource folders that are generated. This is typically the renderscript
@@ -96,22 +94,5 @@
      * @return a list of folder.
      */
     @NonNull
-    List<File> getGeneratedResourceFolders();
-
-    /**
-     * Returns the folder containing the class files. This is the output of the java compilation.
-     *
-     * @return a folder.
-     */
-    @NonNull
-    File getClassesFolder();
-
-    /**
-     * Returns the resolved dependencies for this artifact. This is a composite of all the
-     * dependencies for that artifact: default config + build type + flavor(s).s
-     *
-     * @return The dependencies.
-     */
-    @NonNull
-    Dependencies getDependencies();
+    Collection<File> getGeneratedResourceFolders();
 }
diff --git a/builder-model/src/main/java/com/android/builder/model/AndroidLibrary.java b/builder-model/src/main/java/com/android/builder/model/AndroidLibrary.java
index 64ac011..a46cb7e 100644
--- a/builder-model/src/main/java/com/android/builder/model/AndroidLibrary.java
+++ b/builder-model/src/main/java/com/android/builder/model/AndroidLibrary.java
@@ -20,6 +20,7 @@
 import com.android.annotations.Nullable;
 
 import java.io.File;
+import java.util.Collection;
 import java.util.List;
 
 /**
@@ -49,12 +50,18 @@
     File getFolder();
 
     /**
-     * Returns the direct dependency of this dependency.
+     * Returns the direct dependency of this dependency. The order is important.
      */
     @NonNull
     List<? extends AndroidLibrary> getLibraryDependencies();
 
     /**
+     * Returns the location of the manifest.
+     */
+    @NonNull
+    File getManifest();
+
+    /**
      * Returns the location of the jar file to use for packaging.
      *
      * @return a File for the jar file. The file may not point to an existing file.
@@ -68,7 +75,7 @@
      * @return a list of File. May be empty but not null.
      */
     @NonNull
-    List<File> getLocalJars();
+    Collection<File> getLocalJars();
 
     /**
      * Returns the location of the res folder.
diff --git a/builder-model/src/main/java/com/android/builder/model/AndroidProject.java b/builder-model/src/main/java/com/android/builder/model/AndroidProject.java
index f3929e1..f1118f4 100644
--- a/builder-model/src/main/java/com/android/builder/model/AndroidProject.java
+++ b/builder-model/src/main/java/com/android/builder/model/AndroidProject.java
@@ -18,14 +18,18 @@
 
 import com.android.annotations.NonNull;
 
-import java.util.List;
-import java.util.Map;
+import java.io.File;
+import java.util.Collection;
 
 /**
  * Entry point for the model of the Android Projects. This models a single module, whether
  * the module is an app project or a library project.
  */
 public interface AndroidProject {
+    String BUILD_MODEL_ONLY_SYSTEM_PROPERTY =  "android.build.model.only";
+
+    public static final String ARTIFACT_MAIN = "_main_";
+    public static final String ARTIFACT_INSTRUMENT_TEST = "_instrument_test_";
 
     /**
      * Returns the model version. This is a string in the format X.Y.Z
@@ -58,34 +62,39 @@
     ProductFlavorContainer getDefaultConfig();
 
     /**
-     * Returns a map of all the {@link BuildType} in their container. The key is the build type
-     * name as returned by {@link BuildType#getName()}
+     * Returns a list of all the {@link BuildType} in their container.
      *
-     * @return a map of build type containers.
+     * @return a list of build type containers.
      */
     @NonNull
-    Map<String, BuildTypeContainer> getBuildTypes();
+    Collection<BuildTypeContainer> getBuildTypes();
 
     /**
-     * Returns a map of all the {@link ProductFlavor} in their container. The key is the product
-     * flavor name as returned by {@link ProductFlavor#getName()}
+     * Returns a list of all the {@link ProductFlavor} in their container.
      *
-     * @return a map of product flavor containers.
+     * @return a list of product flavor containers.
      */
     @NonNull
-    Map<String, ProductFlavorContainer> getProductFlavors();
+    Collection<ProductFlavorContainer> getProductFlavors();
 
     /**
-     * Returns a map of all the variants. The key is the variant name as returned by
-     * {@link Variant#getName()}.
+     * Returns a list of all the variants.
      *
-     * This does not include test variant. Instead the variant and its component each contribute
-     * their test part.
+     * This does not include test variant. Test variants are additional artifacts in their
+     * respective variant info.
      *
-     * @return a map of the variants.
+     * @return a list of the variants.
      */
     @NonNull
-    Map<String, Variant> getVariants();
+    Collection<Variant> getVariants();
+
+    /**
+     * Returns a list of extra artifacts meta data. This does not include the main artifact.
+     *
+     * @return a list of extra artifacts
+     */
+    @NonNull
+    Collection<ArtifactMetaData> getExtraArtifacts();
 
     /**
      * Returns the compilation target as a string. This is the full extended target hash string.
@@ -103,16 +112,22 @@
      * @return a list of jar files.
      */
     @NonNull
-    List<String> getBootClasspath();
+    Collection<String> getBootClasspath();
 
     /**
-     * Returns a map of {@link SigningConfig}. The key is the signing config name as returned by
-     * {@link SigningConfig#getName()}
+     * Returns a list of folders or jar files that contains the framework source code.
+     * @return a list of folders or jar files that contains the framework source code.
+     */
+    @NonNull
+    Collection<File> getFrameworkSources();
+
+    /**
+     * Returns a list of {@link SigningConfig}.
      *
      * @return a map of signing config
      */
     @NonNull
-    Map<String, SigningConfig> getSigningConfigs();
+    Collection<SigningConfig> getSigningConfigs();
 
     /**
      * Returns the aapt options.
@@ -121,4 +136,23 @@
      */
     @NonNull
     AaptOptions getAaptOptions();
+
+    /**
+     * Returns the dependencies that were not successfully resolved. The returned list gets
+     * populated only if the system property {@link #BUILD_MODEL_ONLY_SYSTEM_PROPERTY} has been
+     * set to {@code true}.
+     * <p>
+     * Each value of the collection has the format group:name:version, for example:
+     * com.google.guava:guava:15.0.2
+     *
+     * @return the dependencies that were not successfully resolved.
+     */
+    @NonNull
+    Collection<String> getUnresolvedDependencies();
+
+    /**
+     * @return the compile options for Java code.
+     */
+    @NonNull
+    JavaCompileOptions getJavaCompileOptions();
 }
diff --git a/builder-model/src/main/java/com/android/builder/model/ArtifactMetaData.java b/builder-model/src/main/java/com/android/builder/model/ArtifactMetaData.java
new file mode 100644
index 0000000..f1af029
--- /dev/null
+++ b/builder-model/src/main/java/com/android/builder/model/ArtifactMetaData.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2013 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.builder.model;
+
+import com.android.annotations.NonNull;
+
+/**
+ * Meta Data for an Artifact.
+ */
+public interface ArtifactMetaData {
+
+    public final static int TYPE_ANDROID = 1;
+    public final static int TYPE_JAVA = 2;
+
+    @NonNull
+    String getName();
+
+    boolean isTest();
+
+    int getType();
+}
diff --git a/builder-model/src/main/java/com/android/builder/model/BaseArtifact.java b/builder-model/src/main/java/com/android/builder/model/BaseArtifact.java
new file mode 100644
index 0000000..fd1a7b7
--- /dev/null
+++ b/builder-model/src/main/java/com/android/builder/model/BaseArtifact.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2013 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.builder.model;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+
+import java.io.File;
+
+/**
+ * The base information for all generated artifacts
+ */
+public interface BaseArtifact {
+
+    /**
+     * Name of the artifact. This should match {@link ArtifactMetaData#getName()}.
+     */
+    @NonNull
+    String getName();
+
+    /**
+     * @return the name of the task used to compile Java code.
+     */
+    @NonNull
+    String getJavaCompileTaskName();
+
+    /**
+     * Returns the name of the task used to generate the artifact.
+     *
+     * @return the name of the task.
+     */
+    @NonNull
+    String getAssembleTaskName();
+
+    /**
+     * Returns the folder containing the class files. This is the output of the java compilation.
+     *
+     * @return a folder.
+     */
+    @NonNull
+    File getClassesFolder();
+
+    /**
+     * Returns the resolved dependencies for this artifact. This is a composite of all the
+     * dependencies for that artifact: default config + build type + flavor(s).s
+     *
+     * @return The dependencies.
+     */
+    @NonNull
+    Dependencies getDependencies();
+
+    /**
+     * A SourceProvider specific to the variant. This can be null if there is no flavors as
+     * the "variant" is equal to the build type.
+     *
+     * @return the variant specific source provider
+     */
+    @Nullable
+    SourceProvider getVariantSourceProvider();
+
+    /**
+     * A SourceProvider specific to the flavor combination.
+     *
+     * For instance if there are 2 dimensions, then this would be Flavor1Flavor2, and would be
+     * common to all variant using these two flavors and any of the build type.
+     *
+     * This can be null if there is less than 2 flavors.
+     *
+     * @return the multi flavor specific source provider
+     */
+    @Nullable
+    SourceProvider getMultiFlavorSourceProvider();
+}
diff --git a/builder-model/src/main/java/com/android/builder/model/BaseConfig.java b/builder-model/src/main/java/com/android/builder/model/BaseConfig.java
index ff3efcc..d4e22c1 100644
--- a/builder-model/src/main/java/com/android/builder/model/BaseConfig.java
+++ b/builder-model/src/main/java/com/android/builder/model/BaseConfig.java
@@ -19,7 +19,7 @@
 import com.android.annotations.NonNull;
 
 import java.io.File;
-import java.util.List;
+import java.util.Collection;
 
 /**
  * Base config object for Build Type and Product flavor.
@@ -27,11 +27,11 @@
 public interface BaseConfig {
 
     /**
-     * List of Build Config lines.
-     * @return a non-null list of lines (possibly empty)
+     * List of Build Config Fields
+     * @return a non-null list of class fields (possibly empty)
      */
     @NonNull
-    List<String> getBuildConfig();
+    Collection<ClassField> getBuildConfigFields();
 
     /**
      * Returns the list of proguard rule files.
@@ -39,5 +39,13 @@
      * @return a non-null list of files.
      */
     @NonNull
-    List<File> getProguardFiles();
+    Collection<File> getProguardFiles();
+
+    /**
+     * Returns the list of proguard rule files for consumers of the library to use.
+     *
+     * @return a non-null list of files.
+     */
+    @NonNull
+    Collection<File> getConsumerProguardFiles();
 }
diff --git a/builder-model/src/main/java/com/android/builder/model/BuildType.java b/builder-model/src/main/java/com/android/builder/model/BuildType.java
index 33d9ada..c8cf61e 100644
--- a/builder-model/src/main/java/com/android/builder/model/BuildType.java
+++ b/builder-model/src/main/java/com/android/builder/model/BuildType.java
@@ -26,7 +26,7 @@
  * or in the artifact info.
  *
  * @see BuildTypeContainer
- * @see ArtifactInfo#getDependencies()
+ * @see AndroidArtifact#getDependencies()
  */
 public interface BuildType extends BaseConfig {
 
@@ -69,7 +69,7 @@
 
     /**
      * Returns the package name suffix applied to this build type.
-     * To get the final package name, use {@link ArtifactInfo#getPackageName()}.
+     * To get the final package name, use {@link AndroidArtifact#getPackageName()}.
      *
      * @return the package name suffix.
      */
@@ -97,4 +97,11 @@
      * @return true if zipalign is enabled.
      */
     boolean isZipAlign();
+
+    /**
+     * Returns the NDK configuration.
+     * @return the ndk config.
+     */
+    @Nullable
+    NdkConfig getNdkConfig();
 }
diff --git a/builder-model/src/main/java/com/android/builder/model/BuildTypeContainer.java b/builder-model/src/main/java/com/android/builder/model/BuildTypeContainer.java
index 4a0170d..87765f1 100644
--- a/builder-model/src/main/java/com/android/builder/model/BuildTypeContainer.java
+++ b/builder-model/src/main/java/com/android/builder/model/BuildTypeContainer.java
@@ -18,6 +18,8 @@
 
 import com.android.annotations.NonNull;
 
+import java.util.Collection;
+
 /**
  * A Container of all the data related to {@link BuildType}.
  */
@@ -38,4 +40,12 @@
      */
     @NonNull
     SourceProvider getSourceProvider();
+
+    /**
+     * Returns a list of ArtifactMetaData/SourceProvider association.
+     *
+     * @return a list of ArtifactMetaData/SourceProvider association.
+     */
+    @NonNull
+    Collection<SourceProviderContainer> getExtraSourceProviders();
 }
diff --git a/builder-model/src/main/java/com/android/builder/model/ClassField.java b/builder-model/src/main/java/com/android/builder/model/ClassField.java
new file mode 100644
index 0000000..d6b6a26
--- /dev/null
+++ b/builder-model/src/main/java/com/android/builder/model/ClassField.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2013 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.builder.model;
+
+import com.android.annotations.NonNull;
+
+/**
+ * A Simple class field with name, type and value, all as strings.
+ */
+public interface ClassField {
+    @NonNull
+    String getType();
+
+    @NonNull
+    String getName();
+
+    @NonNull
+    String getValue();
+}
diff --git a/builder-model/src/main/java/com/android/builder/model/Dependencies.java b/builder-model/src/main/java/com/android/builder/model/Dependencies.java
index b5a27d1..f28b648 100644
--- a/builder-model/src/main/java/com/android/builder/model/Dependencies.java
+++ b/builder-model/src/main/java/com/android/builder/model/Dependencies.java
@@ -19,10 +19,11 @@
 import com.android.annotations.NonNull;
 
 import java.io.File;
+import java.util.Collection;
 import java.util.List;
 
 /**
- * A set of dependencies for an {@link ArtifactInfo}.
+ * A set of dependencies for an {@link AndroidArtifact}.
  */
 public interface Dependencies {
 
@@ -41,7 +42,7 @@
      * @return the list of jar files.
      */
     @NonNull
-    List<File> getJars();
+    Collection<File> getJars();
 
     /**
      * The list of project dependencies. This is only for non Android module dependencies (which
@@ -50,5 +51,5 @@
      * @return the list of projects.
      */
     @NonNull
-    List<String> getProjects();
+    Collection<String> getProjects();
 }
diff --git a/builder-model/src/main/java/com/android/builder/model/JavaArtifact.java b/builder-model/src/main/java/com/android/builder/model/JavaArtifact.java
new file mode 100644
index 0000000..02cba55
--- /dev/null
+++ b/builder-model/src/main/java/com/android/builder/model/JavaArtifact.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2013 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.builder.model;
+
+/**
+ * The information for a generated Java artifact.
+ */
+public interface JavaArtifact extends BaseArtifact {
+
+}
diff --git a/builder-model/src/main/java/com/android/builder/model/JavaCompileOptions.java b/builder-model/src/main/java/com/android/builder/model/JavaCompileOptions.java
new file mode 100644
index 0000000..dafe83a
--- /dev/null
+++ b/builder-model/src/main/java/com/android/builder/model/JavaCompileOptions.java
@@ -0,0 +1,35 @@
+/*
+ * 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.builder.model;
+
+import com.android.annotations.NonNull;
+
+/**
+ * Java compile options.
+ */
+public interface JavaCompileOptions {
+    /**
+     * @return the level of compliance Java source code has.
+     */
+    @NonNull
+    String getSourceCompatibility();
+
+    /**
+     * @return the Java version to be able to run classes on.
+     */
+    @NonNull
+    String getTargetCompatibility();
+}
diff --git a/builder-model/src/main/java/com/android/builder/model/NdkConfig.java b/builder-model/src/main/java/com/android/builder/model/NdkConfig.java
new file mode 100644
index 0000000..25056c7
--- /dev/null
+++ b/builder-model/src/main/java/com/android/builder/model/NdkConfig.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2013 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.builder.model;
+
+import com.android.annotations.Nullable;
+
+import java.util.Collection;
+
+/**
+ * Base class for NDK config file.
+ */
+public interface NdkConfig {
+
+    /**
+     * The module name
+     */
+    @Nullable
+    public String getModuleName();
+
+    /**
+     * The C Flags
+     */
+    @Nullable
+    public String getcFlags();
+
+    /**
+     * The LD Libs
+     */
+    @Nullable
+    public Collection<String> getLdLibs();
+
+    /**
+     * The ABI Filters
+     */
+    @Nullable
+    public Collection<String> getAbiFilters();
+
+    /**
+     * The APP_STL value
+     */
+    @Nullable
+    public String getStl();
+}
diff --git a/builder-model/src/main/java/com/android/builder/model/ProductFlavor.java b/builder-model/src/main/java/com/android/builder/model/ProductFlavor.java
index f1a6d16..2e5eb81 100644
--- a/builder-model/src/main/java/com/android/builder/model/ProductFlavor.java
+++ b/builder-model/src/main/java/com/android/builder/model/ProductFlavor.java
@@ -19,6 +19,8 @@
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 
+import java.util.Collection;
+
 /**
  * a Product Flavor. This is only the configuration of the flavor.
  *
@@ -26,7 +28,7 @@
  * or in the artifact info.
  *
  * @see ProductFlavorContainer
- * @see ArtifactInfo#getDependencies()
+ * @see BaseArtifact#getDependencies()
  */
 public interface ProductFlavor extends BaseConfig {
 
@@ -40,7 +42,7 @@
 
     /**
      * Returns the name of the product flavor. This is only the value set on this product flavor.
-     * To get the final package name, use {@link ArtifactInfo#getPackageName()}.
+     * To get the final package name, use {@link AndroidArtifact#getPackageName()}.
      *
      * @return the package name.
      */
@@ -90,9 +92,23 @@
     int getRenderscriptTargetApi();
 
     /**
+     * Returns whether the renderscript code should be compiled in support mode to
+     * make it compatible with older versions of Android.
+     *
+     * @return true if support mode is enabled.
+     */
+    boolean getRenderscriptSupportMode();
+
+    /**
+     * Returns whether the renderscript code should be compiled to generate C/C++ bindings.
+     * @return true for C/C++ generation, false for Java
+     */
+    boolean getRenderscriptNdkMode();
+
+    /**
      * Returns the test package name. This is only the value set on this product flavor.
      * To get the final value, use {@link Variant#getTestArtifactInfo()} and
-     * {@link ArtifactInfo#getPackageName()}
+     * {@link AndroidArtifact#getPackageName()}
      *
      * @return the test package name.
      */
@@ -107,4 +123,38 @@
      */
     @Nullable
     String getTestInstrumentationRunner();
+
+    /**
+     * Returns the handlingProfile value. This is only the value set on this product flavor.
+     *
+     *  @return the handlingProfile value.
+     */
+    @Nullable
+    Boolean getTestHandleProfiling();
+
+    /**
+     * Returns the functionalTest value. This is only the value set on this product flavor.
+     *
+     * @return the functionalTest value.
+     */
+    @Nullable
+    Boolean getTestFunctionalTest();
+
+    /**
+     * Returns the NDK configuration.
+     * @return the ndk config.
+     */
+    @Nullable
+    NdkConfig getNdkConfig();
+
+    /**
+     * Returns the resource configuration for this variant.
+     * TODO implement this.
+     *
+     * This is the list of -c parameters for aapt.
+     *
+     * @return the resource configuration options.
+     */
+    @NonNull
+    Collection<String> getResourceConfigurations();
 }
diff --git a/builder-model/src/main/java/com/android/builder/model/ProductFlavorContainer.java b/builder-model/src/main/java/com/android/builder/model/ProductFlavorContainer.java
index f8cc7ed..8643f9d 100644
--- a/builder-model/src/main/java/com/android/builder/model/ProductFlavorContainer.java
+++ b/builder-model/src/main/java/com/android/builder/model/ProductFlavorContainer.java
@@ -18,6 +18,8 @@
 
 import com.android.annotations.NonNull;
 
+import java.util.Collection;
+
 /**
  * A Container of all the data related to {@link ProductFlavor}.
  */
@@ -40,10 +42,10 @@
     SourceProvider getSourceProvider();
 
     /**
-     * The associated test sources of the product flavor
+     * Returns a list of ArtifactMetaData/SourceProvider association.
      *
-     * @return the test source provider.
+     * @return a list of ArtifactMetaData/SourceProvider association.
      */
     @NonNull
-    SourceProvider getTestSourceProvider();
+    Collection<SourceProviderContainer> getExtraSourceProviders();
 }
diff --git a/builder-model/src/main/java/com/android/builder/model/SourceProvider.java b/builder-model/src/main/java/com/android/builder/model/SourceProvider.java
index 7ed06ef..6d58e64 100644
--- a/builder-model/src/main/java/com/android/builder/model/SourceProvider.java
+++ b/builder-model/src/main/java/com/android/builder/model/SourceProvider.java
@@ -18,7 +18,7 @@
 import com.android.annotations.NonNull;
 
 import java.io.File;
-import java.util.Set;
+import java.util.Collection;
 
 /**
  * Represent a SourceProvider for a given configuration.
@@ -41,7 +41,7 @@
      * @return a list of folders. They may not all exist.
      */
     @NonNull
-    Set<File> getJavaDirectories();
+    Collection<File> getJavaDirectories();
 
     /**
      * Returns the java resources folders.
@@ -49,7 +49,7 @@
      * @return a list of folders. They may not all exist.
      */
     @NonNull
-    Set<File> getResourcesDirectories();
+    Collection<File> getResourcesDirectories();
 
     /**
      * Returns the aidl source folders.
@@ -57,7 +57,7 @@
      * @return a list of folders. They may not all exist.
      */
     @NonNull
-    Set<File> getAidlDirectories();
+    Collection<File> getAidlDirectories();
 
     /**
      * Returns the renderscript source folders.
@@ -65,7 +65,7 @@
      * @return a list of folders. They may not all exist.
      */
     @NonNull
-    Set<File> getRenderscriptDirectories();
+    Collection<File> getRenderscriptDirectories();
 
     /**
      * Returns the jni source folders.
@@ -73,7 +73,7 @@
      * @return a list of folders. They may not all exist.
      */
     @NonNull
-    Set<File> getJniDirectories();
+    Collection<File> getJniDirectories();
 
     /**
      * Returns the android resources folders.
@@ -81,7 +81,7 @@
      * @return a list of folders. They may not all exist.
      */
     @NonNull
-    Set<File> getResDirectories();
+    Collection<File> getResDirectories();
 
     /**
      * Returns the android assets folders.
@@ -89,5 +89,5 @@
      * @return a list of folders. They may not all exist.
      */
     @NonNull
-    Set<File> getAssetsDirectories();
+    Collection<File> getAssetsDirectories();
 }
diff --git a/builder-model/src/main/java/com/android/builder/model/SourceProviderContainer.java b/builder-model/src/main/java/com/android/builder/model/SourceProviderContainer.java
new file mode 100644
index 0000000..0ac8387
--- /dev/null
+++ b/builder-model/src/main/java/com/android/builder/model/SourceProviderContainer.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013 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.builder.model;
+
+import com.android.annotations.NonNull;
+
+/**
+ * An association of an {@link ArtifactMetaData}'s name and a {@link SourceProvider}.
+ */
+public interface SourceProviderContainer {
+
+    /**
+     * Returns the name matching {@link ArtifactMetaData#getName()}
+     */
+    @NonNull
+    String getArtifactName();
+
+    /**
+     * Returns the source provider
+     */
+    @NonNull
+    SourceProvider getSourceProvider();
+}
diff --git a/builder-model/src/main/java/com/android/builder/model/Variant.java b/builder-model/src/main/java/com/android/builder/model/Variant.java
index 21ef33e..67813ae 100644
--- a/builder-model/src/main/java/com/android/builder/model/Variant.java
+++ b/builder-model/src/main/java/com/android/builder/model/Variant.java
@@ -17,8 +17,8 @@
 package com.android.builder.model;
 
 import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
 
+import java.util.Collection;
 import java.util.List;
 
 /**
@@ -49,16 +49,13 @@
      * @return the artifact.
      */
     @NonNull
-    ArtifactInfo getMainArtifactInfo();
+    AndroidArtifact getMainArtifact();
 
-    /**
-     * Returns the test artifact for this variant. This may be null if this particular variant
-     * is not configured to be tested.
-     *
-     * @return the test artifact.
-     */
-    @Nullable
-    ArtifactInfo getTestArtifactInfo();
+    @NonNull
+    Collection<AndroidArtifact> getExtraAndroidArtifacts();
+
+    @NonNull
+    Collection<JavaArtifact> getExtraJavaArtifacts();
 
     /**
      * Returns the build type. All variants have a build type, so this is never null.
@@ -89,15 +86,4 @@
      */
     @NonNull
     ProductFlavor getMergedFlavor();
-
-    /**
-     * Returns the resource configuration for this variant.
-     * TODO implement this.
-     *
-     * This is the list of -c parameters for aapt.
-     *
-     * @return the resource configuration options.
-     */
-    @NonNull
-    List<String> getResourceConfigurations();
 }
diff --git a/builder-test-api/build.gradle b/builder-test-api/build.gradle
index 579f4f7..4ba5312 100644
--- a/builder-test-api/build.gradle
+++ b/builder-test-api/build.gradle
@@ -7,102 +7,9 @@
     compile "com.android.tools.ddms:ddmlib:$project.ext.baseAndroidVersion"
 }
 
-def getVersion() {
-    if (project.has("release")) {
-        return project.ext.baseVersion
-    }
-
-    return project.ext.baseVersion + '-SNAPSHOT'
-}
-
-version = getVersion()
 archivesBaseName = 'builder-test-api'
+project.ext.pomName = 'Android Builder Test API library'
+project.ext.pomDesc = 'API for the Test extension point in the Builder library.'
 
-task publishLocal(type: Upload) {
-    configuration = configurations.archives
-    repositories {
-        mavenDeployer {
-            repository(url: uri("$rootProject.ext.androidHostOut/repo"))
-        }
-    }
-}
-
-project.ext.sonatypeUsername = hasProperty('sonatypeUsername') ? sonatypeUsername : ""
-project.ext.sonatypePassword = hasProperty('sonatypePassword') ? sonatypePassword : ""
-
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            beforeDeployment { MavenDeployment deployment ->
-                if (!project.has("release")) {
-                    throw new StopExecutionException("uploadArchives must be called with the release.gradle init script")
-                }
-
-                if (project.ext.sonatypeUsername.length() == 0 || project.ext.sonatypePassword.length() == 0) {
-                    throw new StopExecutionException("uploadArchives cannot be called without sonatype username and password")
-                }
-
-                signing.signPom(deployment)
-            }
-
-            repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
-                authentication(userName: project.ext.sonatypeUsername, password: project.ext.sonatypePassword)
-            }
-
-            pom.project {
-                name 'Android Builder Test API library'
-                description 'API for the Test extension point in the Builder library.'
-                url 'http://tools.android.com'
-                inceptionYear '2007'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "https://android.googlesource.com/platform/tools/build"
-                    connection "git://android.googlesource.com/platform/tools/build.git"
-                }
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
-}
-
-// custom tasks for creating source/javadoc jars
-task sourcesJar(type: Jar, dependsOn:classes) {
-    classifier = 'sources'
-    from sourceSets.main.allSource
-}
-
-javadoc {
-    exclude               "**/internal/**"
-    options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PROTECTED
-
-    title                 "Android Model"
-}
-
-task javadocJar(type: Jar, dependsOn:javadoc) {
-    classifier         'javadoc'
-    from               javadoc.destinationDir
-}
- 
-// add javadoc/source jar tasks as artifacts
-artifacts {
-    archives jar
-    archives sourcesJar
-    archives javadocJar
-}
-
-signing {
-    required { project.has("release") && gradle.taskGraph.hasTask("uploadArchives") }
-    sign configurations.archives
-}
+apply from: '../publish.gradle'
+apply from: '../javadoc.gradle'
diff --git a/builder-test-api/src/main/java/com/android/builder/testing/api/DeviceConnector.java b/builder-test-api/src/main/java/com/android/builder/testing/api/DeviceConnector.java
index 243bdc2..48208e8 100644
--- a/builder-test-api/src/main/java/com/android/builder/testing/api/DeviceConnector.java
+++ b/builder-test-api/src/main/java/com/android/builder/testing/api/DeviceConnector.java
@@ -23,6 +23,7 @@
 import com.google.common.annotations.Beta;
 
 import java.io.File;
+import java.util.List;
 
 /**
  * A connector to a device to install/uninstall APKs, and run shell command.
@@ -60,10 +61,18 @@
      */
     public abstract void uninstallPackage(@NonNull String packageName, int timeout, ILogger logger) throws DeviceException;
 
+    /**
+     * Returns the API level of the device, or 0 if it could not be queried.
+     * @return the api level
+     */
     public abstract int getApiLevel();
 
+    /**
+     * The device supported ABIs. This is in preferred order.
+     * @return the list of supported ABIs
+     */
     @NonNull
-    public abstract String getAbi();
+    public abstract List<String> getAbis();
 
     public abstract int getDensity();
 
diff --git a/builder/build.gradle b/builder/build.gradle
index f73a991..6b5fb9b 100644
--- a/builder/build.gradle
+++ b/builder/build.gradle
@@ -11,114 +11,24 @@
     compile project(':builder-test-api')
     compile "com.android.tools:sdklib:$project.ext.baseAndroidVersion"
     compile "com.android.tools:sdk-common:$project.ext.baseAndroidVersion"
+    compile "com.android.tools:common:$project.ext.baseAndroidVersion"
     compile "com.android.tools.build:manifest-merger:$project.ext.baseAndroidVersion"
     compile "com.android.tools.ddms:ddmlib:$project.ext.baseAndroidVersion"
 
+    compile 'com.squareup:javawriter:2.2.1'
     compile 'org.bouncycastle:bcpkix-jdk15on:1.48'
 
     testCompile 'junit:junit:3.8.1'
     testCompile "com.android.tools:testutils:$project.ext.baseAndroidVersion"
 }
 
-def getVersion() {
-    if (project.has("release")) {
-        return project.ext.baseVersion
-    }
-
-    return project.ext.baseVersion + '-SNAPSHOT'
-}
-
-version = getVersion()
 archivesBaseName = 'builder'
-jar.manifest.attributes("Builder-Version": version)
+project.ext.pomName = 'Android Builder library'
+project.ext.pomDesc = 'Library to build Android applications.'
 
-task publishLocal(type: Upload) {
-    configuration = configurations.archives
-    repositories {
-        mavenDeployer {
-            repository(url: uri("$rootProject.ext.androidHostOut/repo"))
-        }
-    }
-}
+apply from: '../publish.gradle'
+apply from: '../javadoc.gradle'
+
+jar.manifest.attributes("Builder-Version": version)
 publishLocal.dependsOn ':builder-model:publishLocal', ':builder-test-api:publishLocal'
 
-project.ext.sonatypeUsername = hasProperty('sonatypeUsername') ? sonatypeUsername : ""
-project.ext.sonatypePassword = hasProperty('sonatypePassword') ? sonatypePassword : ""
-
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            beforeDeployment { MavenDeployment deployment ->
-                if (!project.has("release")) {
-                    throw new StopExecutionException("uploadArchives must be called with the release.gradle init script")
-                }
-
-                if (project.ext.sonatypeUsername.length() == 0 || project.ext.sonatypePassword.length() == 0) {
-                    throw new StopExecutionException("uploadArchives cannot be called without sonatype username and password")
-                }
-
-                signing.signPom(deployment)
-            }
-
-            repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
-                authentication(userName: project.ext.sonatypeUsername, password: project.ext.sonatypePassword)
-            }
-
-            pom.project {
-                name 'Android Builder library'
-                description 'library to build Android applications.'
-                url 'http://tools.android.com'
-                inceptionYear '2007'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "https://android.googlesource.com/platform/tools/build"
-                    connection "git://android.googlesource.com/platform/tools/build.git"
-                }
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
-}
-
-
-// custom tasks for creating source/javadoc jars
-task sourcesJar(type: Jar, dependsOn:classes) {
-    classifier = 'sources'
-    from sourceSets.main.allSource
-}
-
-javadoc {
-    exclude               "**/internal/**"
-    options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PROTECTED
-
-    title                 "Android Builder"
-}
-
-task javadocJar(type: Jar, dependsOn:javadoc) {
-    classifier         'javadoc'
-    from               javadoc.destinationDir
-}
- 
-// add javadoc/source jar tasks as artifacts
-artifacts {
-    archives jar
-    archives sourcesJar
-    archives javadocJar
-}
-
-signing {
-    required { project.has("release") && gradle.taskGraph.hasTask("uploadArchives") }
-    sign configurations.archives
-}
diff --git a/builder/src/main/java/com/android/builder/AndroidBuilder.java b/builder/src/main/java/com/android/builder/AndroidBuilder.java
index 9442f4d..0e12242 100644
--- a/builder/src/main/java/com/android/builder/AndroidBuilder.java
+++ b/builder/src/main/java/com/android/builder/AndroidBuilder.java
@@ -16,24 +16,25 @@
 
 package com.android.builder;
 
-import com.android.SdkConstants;
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.annotations.VisibleForTesting;
 import com.android.builder.compiling.DependencyFileProcessor;
 import com.android.builder.dependency.ManifestDependency;
 import com.android.builder.dependency.SymbolFileProvider;
-import com.android.builder.internal.BuildConfigGenerator;
+import com.android.builder.internal.ClassFieldImpl;
 import com.android.builder.internal.SymbolLoader;
 import com.android.builder.internal.SymbolWriter;
 import com.android.builder.internal.TestManifestGenerator;
 import com.android.builder.internal.compiler.AidlProcessor;
-import com.android.builder.internal.compiler.FileGatherer;
 import com.android.builder.internal.compiler.LeafFolderGatherer;
+import com.android.builder.internal.compiler.RenderScriptProcessor;
 import com.android.builder.internal.compiler.SourceSearcher;
 import com.android.builder.internal.packaging.JavaResourceProcessor;
 import com.android.builder.internal.packaging.Packager;
 import com.android.builder.model.AaptOptions;
+import com.android.builder.model.ClassField;
+import com.android.builder.model.ProductFlavor;
 import com.android.builder.model.SigningConfig;
 import com.android.builder.packaging.DuplicateFileException;
 import com.android.builder.packaging.PackagerException;
@@ -51,11 +52,13 @@
 import com.android.sdklib.IAndroidTarget;
 import com.android.sdklib.repository.FullRevision;
 import com.android.utils.ILogger;
+import com.android.utils.SdkUtils;
+import com.google.common.base.Joiner;
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Multimap;
-import com.google.common.io.Files;
+import com.google.common.collect.Sets;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -65,6 +68,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -78,13 +82,12 @@
  * create a builder with {@link #AndroidBuilder(SdkParser, String, ILogger, boolean)}
  *
  * then build steps can be done with
- * {@link #generateBuildConfig(String, boolean, java.util.List, String)}
  * {@link #processManifest(java.io.File, java.util.List, java.util.List, String, int, String, int, int, String)}
- * {@link #processTestManifest(String, int, int, String, String, java.util.List, String)}
+ * {@link #processTestManifest(String, int, int, String, String, Boolean, Boolean, java.util.List, String)}
  * {@link #processResources(java.io.File, java.io.File, java.io.File, java.util.List, String, String, String, String, String, com.android.builder.VariantConfiguration.Type, boolean, com.android.builder.model.AaptOptions)}
  * {@link #compileAllAidlFiles(java.util.List, java.io.File, java.util.List, com.android.builder.compiling.DependencyFileProcessor)}
- * {@link #convertByteCode(Iterable, Iterable, File, String, DexOptions, boolean)}
- * {@link #packageApk(String, String, java.util.List, String, String, boolean, SigningConfig, String)}
+ * {@link #convertByteCode(Iterable, Iterable, File, DexOptions, boolean)}
+ * {@link #packageApk(String, String, java.util.List, String, java.util.Collection, java.util.Set, boolean, com.android.builder.model.SigningConfig, String)}
  *
  * Java compilation is not handled but the builder provides the bootclasspath with
  * {@link #getBootClasspath(SdkParser)}.
@@ -104,6 +107,7 @@
     private final ILogger mLogger;
     private final CommandLineRunner mCmdLineRunner;
     private final boolean mVerboseExec;
+    private boolean mLibrary;
 
     @NonNull
     private final IAndroidTarget mTarget;
@@ -166,6 +170,7 @@
     /**
      * Helper method to get the boot classpath to be used during compilation.
      */
+    @NonNull
     public static List<String> getBootClasspath(@NonNull SdkParser sdkParser) {
 
         List<String> classpath = Lists.newArrayList();
@@ -190,35 +195,92 @@
         return classpath;
     }
 
+    /** Sets whether this builder is currently used to build a library. Defaults to false. */
+    public AndroidBuilder setBuildingLibrary(boolean library) {
+        mLibrary = library;
+        return this;
+    }
+
+    /** Sets whether this builder is currently used to build a library */
+    public boolean isBuildingLibrary() {
+        return mLibrary;
+    }
+
+    /**
+     * Returns the compile classpath for this config. If the config tests a library, this
+     * will include the classpath of the tested config
+     *
+     * @return a non null, but possibly empty set.
+     */
+    @NonNull
+    public Set<File> getCompileClasspath(@NonNull VariantConfiguration variantConfiguration) {
+        Set<File> compileClasspath = variantConfiguration.getCompileClasspath();
+
+        ProductFlavor mergedFlavor = variantConfiguration.getMergedFlavor();
+
+        if (mergedFlavor.getRenderscriptSupportMode()) {
+            File renderScriptSupportJar = RenderScriptProcessor.getSupportJar(
+                    mBuildTools.getLocation().getAbsolutePath());
+
+            Set<File> fullJars = Sets.newHashSetWithExpectedSize(compileClasspath.size() + 1);
+            fullJars.addAll(compileClasspath);
+            fullJars.add(renderScriptSupportJar);
+            compileClasspath = fullJars;
+        }
+
+        return compileClasspath;
+    }
+
+    /**
+     * Returns the list of packaged jars for this config. If the config tests a library, this
+     * will include the jars of the tested config
+     *
+     * @return a non null, but possibly empty list.
+     */
+    @NonNull
+    public List<File> getPackagedJars(@NonNull VariantConfiguration variantConfiguration) {
+        List<File> packagedJars = variantConfiguration.getPackagedJars();
+
+        ProductFlavor mergedFlavor = variantConfiguration.getMergedFlavor();
+
+        if (mergedFlavor.getRenderscriptSupportMode()) {
+            File renderScriptSupportJar = RenderScriptProcessor.getSupportJar(
+                    mBuildTools.getLocation().getAbsolutePath());
+
+            List<File> fullJars = Lists.newArrayListWithCapacity(packagedJars.size() + 1);
+            fullJars.addAll(packagedJars);
+            fullJars.add(renderScriptSupportJar);
+            packagedJars = fullJars;
+        }
+
+        return packagedJars;
+    }
+
+    @NonNull
+    public File getSupportNativeLibFolder() {
+        return RenderScriptProcessor.getSupportNativeLibFolder(
+                mBuildTools.getLocation().getAbsolutePath());
+    }
 
     /**
      * Returns an {@link AaptRunner} able to run aapt commands.
      * @return an AaptRunner object
      */
+    @NonNull
     public AaptRunner getAaptRunner() {
         return new AaptRunner(
                 mBuildTools.getPath(BuildToolInfo.PathId.AAPT),
                 mCmdLineRunner);
     }
 
-    /**
-     * Generate the BuildConfig class for the project.
-     * @param packageName the package in which to generate the class
-     * @param debuggable whether the app is considered debuggable
-     * @param javaLines additional java lines to put in the class. These must be valid Java lines.
-     * @param sourceOutputDir directory where to put this. This is the source folder, not the
-     *                        package folder.
-     * @throws IOException
-     */
-    public void generateBuildConfig(
-            @NonNull String packageName,
-                     boolean debuggable,
-            @NonNull List<String> javaLines,
-            @NonNull String sourceOutputDir) throws IOException {
+    @NonNull
+    public CommandLineRunner getCommandLineRunner() {
+        return mCmdLineRunner;
+    }
 
-        BuildConfigGenerator generator = new BuildConfigGenerator(
-                sourceOutputDir, packageName, debuggable);
-        generator.generate(javaLines);
+    @NonNull
+    public static ClassField createClassField(@NonNull String type, @NonNull String name, @NonNull String value) {
+        return new ClassFieldImpl(type, name, value);
     }
 
     /**
@@ -266,9 +328,11 @@
                 // if no manifest to merge, just copy to location, unless we have to inject
                 // attributes
                 if (attributeInjection.isEmpty() && packageOverride == null) {
-                    Files.copy(mainManifest, new File(outManifestLocation));
+                    SdkUtils.copyXmlWithSourceReference(mainManifest,
+                            new File(outManifestLocation));
                 } else {
                     ManifestMerger merger = new ManifestMerger(MergerLog.wrapSdkLog(mLogger), null);
+                    merger.setInsertSourceMarkers(isInsertSourceMarkers());
                     doMerge(merger, new File(outManifestLocation), mainManifest,
                             attributeInjection, packageOverride);
                 }
@@ -287,6 +351,7 @@
                     }
 
                     ManifestMerger merger = new ManifestMerger(MergerLog.wrapSdkLog(mLogger), null);
+                    merger.setInsertSourceMarkers(isInsertSourceMarkers());
                     doMerge(merger, mainManifestOut, mainManifest, manifestOverlays,
                             attributeInjection, packageOverride);
 
@@ -316,6 +381,8 @@
      * @param targetSdkVersion the targetSdkVersion of the test application
      * @param testedPackageName the package name of the tested application
      * @param instrumentationRunner the name of the instrumentation runner
+     * @param handleProfiling whether or not the Instrumentation object will turn profiling on and off
+     * @param functionalTest whether or not the Instrumentation class should run as a functional test
      * @param libraries the library dependency graph
      * @param outManifestLocation the output location for the merged manifest
      *
@@ -324,6 +391,8 @@
      * @see com.android.builder.VariantConfiguration#getMinSdkVersion()
      * @see com.android.builder.VariantConfiguration#getTestedPackageName()
      * @see com.android.builder.VariantConfiguration#getInstrumentationRunner()
+     * @see com.android.builder.VariantConfiguration#getHandleProfiling()
+     * @see com.android.builder.VariantConfiguration#getFunctionalTest()
      * @see com.android.builder.VariantConfiguration#getDirectLibraries()
      */
     public void processTestManifest(
@@ -332,11 +401,15 @@
                      int targetSdkVersion,
             @NonNull String testedPackageName,
             @NonNull String instrumentationRunner,
+            @NonNull Boolean handleProfiling,
+            @NonNull Boolean functionalTest,
             @NonNull List<? extends ManifestDependency> libraries,
             @NonNull String outManifestLocation) {
         checkNotNull(testPackageName, "testPackageName cannot be null.");
         checkNotNull(testedPackageName, "testedPackageName cannot be null.");
         checkNotNull(instrumentationRunner, "instrumentationRunner cannot be null.");
+        checkNotNull(handleProfiling, "handleProfiling cannot be null.");
+        checkNotNull(functionalTest, "functionalTest cannot be null.");
         checkNotNull(libraries, "libraries cannot be null.");
         checkNotNull(outManifestLocation, "outManifestLocation cannot be null.");
 
@@ -351,6 +424,8 @@
                         targetSdkVersion,
                         testedPackageName,
                         instrumentationRunner,
+                        handleProfiling,
+                        functionalTest,
                         generatedTestManifest.getAbsolutePath());
 
                 mergeLibraryManifests(
@@ -368,6 +443,8 @@
                     targetSdkVersion,
                     testedPackageName,
                     instrumentationRunner,
+                    handleProfiling,
+                    functionalTest,
                     outManifestLocation);
         }
     }
@@ -378,6 +455,8 @@
             int targetSdkVersion,
             String testedPackageName,
             String instrumentationRunner,
+            Boolean handleProfiling,
+            Boolean functionalTest,
             String outManifestLocation) {
         TestManifestGenerator generator = new TestManifestGenerator(
                 outManifestLocation,
@@ -385,7 +464,9 @@
                 minSdkVersion,
                 targetSdkVersion,
                 testedPackageName,
-                instrumentationRunner);
+                instrumentationRunner,
+                handleProfiling,
+                functionalTest);
         try {
             generator.generate();
         } catch (IOException e) {
@@ -443,7 +524,7 @@
 
         List<File> manifests = Lists.newArrayList();
         for (ManifestDependency library : directLibraries) {
-            List<? extends ManifestDependency> subLibraries = library.getManifestDependencies();
+            Collection<? extends ManifestDependency> subLibraries = library.getManifestDependencies();
             if (subLibraries.isEmpty()) {
                 manifests.add(library.getManifest());
             } else {
@@ -459,9 +540,27 @@
         }
 
         ManifestMerger merger = new ManifestMerger(MergerLog.wrapSdkLog(mLogger), null);
+        merger.setInsertSourceMarkers(isInsertSourceMarkers());
         doMerge(merger, outManifest, mainManifest, manifests, attributeInjection, packageOverride);
     }
 
+    /**
+     * Returns whether we should insert source markers in generated files (such as
+     * XML resources and merged manifest files)
+     *
+     * @return true to generate source comments
+     */
+    public boolean isInsertSourceMarkers() {
+        // In release library builds (generating AAR's) we don't want source comments.
+        // In other scenarios (e.g. during development) we do.
+
+        // TODO: Find out whether we're building in a release build type
+        boolean isRelease = false;
+
+        //noinspection ConstantConditions
+        return !(mLibrary && isRelease);
+    }
+
     private void doMerge(ManifestMerger merger, File output, File input,
                                Map<String, String> injectionMap, String packageOverride) {
         List<File> list = Collections.emptyList();
@@ -509,7 +608,8 @@
             @Nullable String proguardOutput,
                       VariantConfiguration.Type type,
                       boolean debuggable,
-            @NonNull AaptOptions options)
+            @NonNull AaptOptions options,
+            @NonNull Collection<String> resourceConfigs)
             throws IOException, InterruptedException, LoggedErrorException {
 
         checkNotNull(manifestFile, "manifestFile cannot be null.");
@@ -564,14 +664,14 @@
             command.add(sourceOutputDir);
         }
 
-        if (type != VariantConfiguration.Type.LIBRARY && resPackageOutput != null) {
+        if (resPackageOutput != null) {
             command.add("-F");
             command.add(resPackageOutput);
+        }
 
-            if (proguardOutput != null) {
-                command.add("-G");
-                command.add(proguardOutput);
-            }
+        if (proguardOutput != null) {
+            command.add("-G");
+            command.add(proguardOutput);
         }
 
         // options controlled by build variants
@@ -600,7 +700,7 @@
             command.add(ignoreAssets);
         }
 
-        List<String> noCompressList = options.getNoCompress();
+        Collection<String> noCompressList = options.getNoCompress();
         if (noCompressList != null) {
             for (String noCompress : noCompressList) {
                 command.add("-0");
@@ -608,6 +708,13 @@
             }
         }
 
+        if (!resourceConfigs.isEmpty()) {
+            command.add("-c");
+
+            Joiner joiner = Joiner.on(',');
+            command.add(joiner.join(resourceConfigs));
+        }
+
         if (symbolOutputDir != null &&
                 (type == VariantConfiguration.Type.LIBRARY || !libraries.isEmpty())) {
             command.add("--output-text-symbols");
@@ -782,9 +889,14 @@
                                             @NonNull List<File> importFolders,
                                             @NonNull File sourceOutputDir,
                                             @NonNull File resOutputDir,
+                                            @NonNull File objOutputDir,
+                                            @NonNull File libOutputDir,
                                             int targetApi,
                                             boolean debugBuild,
-                                            int optimLevel)
+                                            int optimLevel,
+                                            boolean ndkMode,
+                                            boolean supportMode,
+                                            @Nullable Set<String> abiFilters)
             throws IOException, InterruptedException, LoggedErrorException {
         checkNotNull(sourceFolders, "sourceFolders cannot be null.");
         checkNotNull(importFolders, "importFolders cannot be null.");
@@ -796,76 +908,26 @@
             throw new IllegalStateException("llvm-rs-cc is missing");
         }
 
-        // gather the files to compile
-        FileGatherer fileGatherer = new FileGatherer();
-        SourceSearcher searcher = new SourceSearcher(sourceFolders, "rs", "fs");
-        searcher.setUseExecutor(false);
-        searcher.search(fileGatherer);
-
-        List<File> renderscriptFiles = fileGatherer.getFiles();
-
-        if (renderscriptFiles.isEmpty()) {
-            return;
+        if (supportMode && mBuildTools.getRevision().compareTo(new FullRevision(18,1, 0)) == -1) {
+            throw new IllegalStateException(
+                    "RenderScript Support Mode requires buildToolsVersion >= 18.1");
         }
 
-        String rsPath = mBuildTools.getPath(BuildToolInfo.PathId.ANDROID_RS);
-        String rsClangPath = mBuildTools.getPath(BuildToolInfo.PathId.ANDROID_RS_CLANG);
-
-        // the renderscript compiler doesn't expect the top res folder,
-        // but the raw folder directly.
-        File rawFolder = new File(resOutputDir, SdkConstants.FD_RES_RAW);
-
-        // compile all the files in a single pass
-        ArrayList<String> command = Lists.newArrayList();
-
-        command.add(renderscript);
-
-        if (debugBuild) {
-            command.add("-g");
-        }
-
-        command.add("-O");
-        command.add(Integer.toString(optimLevel));
-
-        // add all import paths
-        command.add("-I");
-        command.add(rsPath);
-        command.add("-I");
-        command.add(rsClangPath);
-
-        for (File importPath : importFolders) {
-            if (importPath.isDirectory()) {
-                command.add("-I");
-                command.add(importPath.getAbsolutePath());
-            }
-        }
-
-        // source output
-        command.add("-p");
-        command.add(sourceOutputDir.getAbsolutePath());
-
-        // res output
-        command.add("-o");
-        command.add(rawFolder.getAbsolutePath());
-
-        command.add("-target-api");
-        command.add(Integer.toString(targetApi < 11 ? 11 : targetApi));
-
-        // input files
-        for (File sourceFile : renderscriptFiles) {
-            command.add(sourceFile.getAbsolutePath());
-        }
-
-        Map<String, String> env = null;
-        if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_DARWIN) {
-            env = Maps.newHashMap();
-            env.put("DYLD_LIBRARY_PATH", mBuildTools.getLocation().getAbsolutePath());
-        } else if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_LINUX) {
-            env = Maps.newHashMap();
-            env.put("LD_LIBRARY_PATH", mBuildTools.getLocation().getAbsolutePath());
-        }
-
-        mCmdLineRunner.runCmdLine(command, env);
+        RenderScriptProcessor processor = new RenderScriptProcessor(
+                sourceFolders,
+                importFolders,
+                sourceOutputDir,
+                resOutputDir,
+                objOutputDir,
+                libOutputDir,
+                mBuildTools,
+                targetApi,
+                debugBuild,
+                optimLevel,
+                ndkMode,
+                supportMode,
+                abiFilters);
+        processor.build(mCmdLineRunner);
     }
 
     /**
@@ -910,8 +972,8 @@
 
     /**
      * Converts the bytecode to Dalvik format
-     * @param classesLocation the location of the compiler output
-     * @param libraries the list of libraries
+     * @param inputs the input files
+     * @param preDexedLibraries the list of pre-dexed libraries
      * @param outDexFile the location of the output classes.dex file
      * @param dexOptions dex options
      * @param incremental true if it should attempt incremental dex if applicable
@@ -921,14 +983,13 @@
      * @throws LoggedErrorException
      */
     public void convertByteCode(
-            @NonNull Iterable<File> classesLocation,
-            @NonNull Iterable<File> libraries,
-            @Nullable File proguardFile,
-            @NonNull String outDexFile,
+            @NonNull Iterable<File> inputs,
+            @NonNull Iterable<File> preDexedLibraries,
+            @NonNull File outDexFile,
             @NonNull DexOptions dexOptions,
             boolean incremental) throws IOException, InterruptedException, LoggedErrorException {
-        checkNotNull(classesLocation, "classesLocation cannot be null.");
-        checkNotNull(libraries, "libraries cannot be null.");
+        checkNotNull(inputs, "inputs cannot be null.");
+        checkNotNull(preDexedLibraries, "preDexedLibraries cannot be null.");
         checkNotNull(outDexFile, "outDexFile cannot be null.");
         checkNotNull(dexOptions, "dexOptions cannot be null.");
 
@@ -956,45 +1017,99 @@
             command.add("--core-library");
         }
 
+        if (dexOptions.getJumboMode()) {
+            command.add("--force-jumbo");
+        }
+
         if (incremental) {
             command.add("--incremental");
             command.add("--no-strict");
         }
 
         command.add("--output");
-        command.add(outDexFile);
+        command.add(outDexFile.getAbsolutePath());
 
-        // clean up and add class inputs
-        List<String> classesList = Lists.newArrayList();
-        for (File f : classesLocation) {
+        // clean up input list
+        List<String> inputList = Lists.newArrayList();
+        for (File f : inputs) {
             if (f != null && f.exists()) {
-                classesList.add(f.getAbsolutePath());
+                inputList.add(f.getAbsolutePath());
             }
         }
 
-        if (!classesList.isEmpty()) {
-            mLogger.verbose("Dex class inputs: " + classesList);
-            command.addAll(classesList);
+        if (!inputList.isEmpty()) {
+            mLogger.verbose("Dex inputs: " + inputList);
+            command.addAll(inputList);
         }
 
         // clean up and add library inputs.
         List<String> libraryList = Lists.newArrayList();
-        for (File f : libraries) {
+        for (File f : preDexedLibraries) {
             if (f != null && f.exists()) {
                 libraryList.add(f.getAbsolutePath());
             }
         }
 
         if (!libraryList.isEmpty()) {
-            mLogger.verbose("Dex library inputs: " + libraryList);
+            mLogger.verbose("Dex pre-dexed inputs: " + libraryList);
             command.addAll(libraryList);
         }
 
-        if (proguardFile != null && proguardFile.exists()) {
-            mLogger.verbose("ProGuarded inputs " + proguardFile);
-            command.add(proguardFile.getAbsolutePath());
+        mCmdLineRunner.runCmdLine(command, null);
+    }
+
+    /**
+     * Converts the bytecode to Dalvik format
+     * @param inputFile the input file
+     * @param outFile the location of the output classes.dex file
+     * @param dexOptions dex options
+     *
+     * @throws IOException
+     * @throws InterruptedException
+     * @throws LoggedErrorException
+     */
+    public void preDexLibrary(
+            @NonNull File inputFile,
+            @NonNull File outFile,
+            @NonNull DexOptions dexOptions)
+            throws IOException, InterruptedException, LoggedErrorException {
+        checkNotNull(inputFile, "inputFile cannot be null.");
+        checkNotNull(outFile, "outFile cannot be null.");
+        checkNotNull(dexOptions, "dexOptions cannot be null.");
+
+        // launch dx: create the command line
+        ArrayList<String> command = Lists.newArrayList();
+
+        String dx = mBuildTools.getPath(BuildToolInfo.PathId.DX);
+        if (dx == null || !new File(dx).isFile()) {
+            throw new IllegalStateException("dx is missing");
         }
 
+        command.add(dx);
+
+        if (dexOptions.getJavaMaxHeapSize() != null) {
+            command.add("-JXmx" + dexOptions.getJavaMaxHeapSize());
+        }
+
+        command.add("--dex");
+
+        if (mVerboseExec) {
+            command.add("--verbose");
+        }
+
+        if (dexOptions.isCoreLibrary()) {
+            command.add("--core-library");
+        }
+
+        if (dexOptions.getJumboMode()) {
+            command.add("--force-jumbo");
+        }
+
+        command.add("--output");
+        command.add(outFile.getAbsolutePath());
+
+        command.add(inputFile.getAbsolutePath());
+
         mCmdLineRunner.runCmdLine(command, null);
     }
 
@@ -1005,7 +1120,8 @@
      * @param classesDexLocation the location of the classes.dex file
      * @param packagedJars the jars that are packaged (libraries + jar dependencies)
      * @param javaResourcesLocation the processed Java resource folder
-     * @param jniLibsLocation the location of the compiled JNI libraries
+     * @param jniLibsFolders the folders containing jni shared libraries
+     * @param abiFilters optional ABI filter
      * @param jniDebugBuild whether the app should include jni debug data
      * @param signingConfig the signing configuration
      * @param outApkLocation location of the APK.
@@ -1022,7 +1138,8 @@
             @NonNull String classesDexLocation,
             @NonNull List<File> packagedJars,
             @Nullable String javaResourcesLocation,
-            @Nullable String jniLibsLocation,
+            @Nullable Collection<File> jniLibsFolders,
+            @Nullable Set<String> abiFilters,
             boolean jniDebugBuild,
             @Nullable SigningConfig signingConfig,
             @NonNull String outApkLocation) throws DuplicateFileException, FileNotFoundException,
@@ -1059,8 +1176,10 @@
             }
 
             // also add resources from library projects and jars
-            if (jniLibsLocation != null) {
-                packager.addNativeLibraries(jniLibsLocation);
+            if (jniLibsFolders != null) {
+                for (File jniFolder : jniLibsFolders) {
+                    packager.addNativeLibraries(jniFolder, abiFilters);
+                }
             }
 
             packager.sealApk();
diff --git a/builder/src/main/java/com/android/builder/DefaultBuildType.java b/builder/src/main/java/com/android/builder/DefaultBuildType.java
index ea82e12..63abf00 100644
--- a/builder/src/main/java/com/android/builder/DefaultBuildType.java
+++ b/builder/src/main/java/com/android/builder/DefaultBuildType.java
@@ -20,6 +20,7 @@
 import com.android.annotations.Nullable;
 import com.android.builder.internal.BaseConfigImpl;
 import com.android.builder.model.BuildType;
+import com.android.builder.model.NdkConfig;
 import com.android.builder.model.SigningConfig;
 import com.google.common.base.Objects;
 
@@ -111,7 +112,8 @@
     }
 
     @Override
-    @Nullable public String getPackageNameSuffix() {
+    @Nullable
+    public String getPackageNameSuffix() {
         return mPackageNameSuffix;
     }
 
@@ -122,7 +124,8 @@
     }
 
     @Override
-    @Nullable public String getVersionNameSuffix() {
+    @Nullable
+    public String getVersionNameSuffix() {
         return mVersionNameSuffix;
     }
 
@@ -160,6 +163,12 @@
     }
 
     @Override
+    @Nullable
+    public NdkConfig getNdkConfig() {
+        return null;
+    }
+
+    @Override
     public boolean equals(Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
diff --git a/builder/src/main/java/com/android/builder/DefaultManifestParser.java b/builder/src/main/java/com/android/builder/DefaultManifestParser.java
index 97d8533..e8d70d6 100644
--- a/builder/src/main/java/com/android/builder/DefaultManifestParser.java
+++ b/builder/src/main/java/com/android/builder/DefaultManifestParser.java
@@ -67,6 +67,27 @@
     }
 
     @Override
+    public int getVersionCode(@NonNull File manifestFile) {
+        XPath xpath = AndroidXPathFactory.newXPath();
+
+        try {
+            String value= xpath.evaluate("/manifest/@android:versionCode",
+                    new InputSource(new FileInputStream(manifestFile)));
+            if (value != null) {
+                return Integer.parseInt(value);
+            }
+        } catch (XPathExpressionException e) {
+            // won't happen.
+        } catch (FileNotFoundException e) {
+            throw new RuntimeException(e);
+        } catch (NumberFormatException e) {
+            // return -1 below.
+        }
+
+        return -1;
+    }
+
+    @Override
     public int getMinSdkVersion(@NonNull File manifestFile) {
         try {
             Object value = AndroidManifest.getMinSdkVersion(new FileWrapper(manifestFile));
diff --git a/builder/src/main/java/com/android/builder/DefaultProductFlavor.java b/builder/src/main/java/com/android/builder/DefaultProductFlavor.java
index 6541dde..2dbdd81 100644
--- a/builder/src/main/java/com/android/builder/DefaultProductFlavor.java
+++ b/builder/src/main/java/com/android/builder/DefaultProductFlavor.java
@@ -19,9 +19,15 @@
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.builder.internal.BaseConfigImpl;
+import com.android.builder.model.NdkConfig;
 import com.android.builder.model.ProductFlavor;
 import com.android.builder.model.SigningConfig;
 import com.google.common.base.Objects;
+import com.google.common.collect.Sets;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Set;
 
 /**
  * The configuration of a product flavor.
@@ -36,12 +42,17 @@
     private int mMinSdkVersion = -1;
     private int mTargetSdkVersion = -1;
     private int mRenderscriptTargetApi = -1;
+    private Boolean mRenderscriptSupportMode;
+    private Boolean mRenderscriptNdkMode;
     private int mVersionCode = -1;
     private String mVersionName = null;
     private String mPackageName = null;
     private String mTestPackageName = null;
     private String mTestInstrumentationRunner = null;
+    private Boolean mTestHandleProfiling = null;
+    private Boolean mTestFunctionalTest = null;
     private SigningConfig mSigningConfig = null;
+    private Set<String> mResourceConfiguration = null;
 
     /**
      * Creates a ProductFlavor with a given name.
@@ -145,6 +156,26 @@
         mRenderscriptTargetApi = renderscriptTargetApi;
     }
 
+    @Override
+    public boolean getRenderscriptSupportMode() {
+        // default is false
+        return mRenderscriptSupportMode != null && mRenderscriptSupportMode.booleanValue();
+    }
+
+    public void setRenderscriptSupportMode(boolean renderscriptSupportMode) {
+        mRenderscriptSupportMode = renderscriptSupportMode;
+    }
+
+    @Override
+    public boolean getRenderscriptNdkMode() {
+        // default is false
+        return mRenderscriptNdkMode != null && mRenderscriptNdkMode.booleanValue();
+    }
+
+    public void setRenderscriptNdkMode(boolean renderscriptNdkMode) {
+        mRenderscriptNdkMode = renderscriptNdkMode;
+    }
+
     @NonNull
     public ProductFlavor setTestPackageName(String testPackageName) {
         mTestPackageName = testPackageName;
@@ -169,6 +200,30 @@
         return mTestInstrumentationRunner;
     }
 
+    @Override
+    @Nullable
+    public Boolean getTestHandleProfiling() {
+        return mTestHandleProfiling;
+    }
+
+    @NonNull
+    public ProductFlavor setTestHandleProfiling(boolean handleProfiling) {
+        mTestHandleProfiling = handleProfiling;
+        return this;
+    }
+
+    @Override
+    @Nullable
+    public Boolean getTestFunctionalTest() {
+        return mTestFunctionalTest;
+    }
+
+    @NonNull
+    public ProductFlavor setTestFunctionalTest(boolean functionalTest) {
+        mTestFunctionalTest = functionalTest;
+        return this;
+    }
+
     @Nullable
     public SigningConfig getSigningConfig() {
         return mSigningConfig;
@@ -180,6 +235,46 @@
         return this;
     }
 
+    @Override
+    @Nullable
+    public NdkConfig getNdkConfig() {
+        return null;
+    }
+
+    public void addResourceConfiguration(@NonNull String configuration) {
+        if (mResourceConfiguration == null) {
+            mResourceConfiguration = Sets.newHashSet();
+        }
+
+        mResourceConfiguration.add(configuration);
+    }
+
+    public void addResourceConfigurations(@NonNull String... configurations) {
+        if (mResourceConfiguration == null) {
+            mResourceConfiguration = Sets.newHashSet();
+        }
+
+        mResourceConfiguration.addAll(Arrays.asList(configurations));
+    }
+
+    public void addResourceConfigurations(@NonNull Collection<String> configurations) {
+        if (mResourceConfiguration == null) {
+            mResourceConfiguration = Sets.newHashSet();
+        }
+
+        mResourceConfiguration.addAll(configurations);
+    }
+
+    @NonNull
+    @Override
+    public Collection<String> getResourceConfigurations() {
+        if (mResourceConfiguration == null) {
+            mResourceConfiguration = Sets.newHashSet();
+        }
+
+        return mResourceConfiguration;
+    }
+
     /**
      * Merges the flavor on top of a base platform and returns a new object with the result.
      * @param base the flavor to merge on top of
@@ -193,6 +288,10 @@
         flavor.mTargetSdkVersion = chooseInt(mTargetSdkVersion, base.mTargetSdkVersion);
         flavor.mRenderscriptTargetApi = chooseInt(mRenderscriptTargetApi,
                 base.mRenderscriptTargetApi);
+        flavor.mRenderscriptSupportMode = chooseBoolean(mRenderscriptSupportMode,
+                base.mRenderscriptSupportMode);
+        flavor.mRenderscriptNdkMode = chooseBoolean(mRenderscriptNdkMode,
+                base.mRenderscriptNdkMode);
 
         flavor.mVersionCode = chooseInt(mVersionCode, base.mVersionCode);
         flavor.mVersionName = chooseString(mVersionName, base.mVersionName);
@@ -203,9 +302,17 @@
         flavor.mTestInstrumentationRunner = chooseString(mTestInstrumentationRunner,
                 base.mTestInstrumentationRunner);
 
+        flavor.mTestHandleProfiling = chooseBoolean(mTestHandleProfiling,
+                base.mTestHandleProfiling);
+
+        flavor.mTestFunctionalTest = chooseBoolean(mTestFunctionalTest,
+                base.mTestFunctionalTest);
+
         flavor.mSigningConfig =
                 mSigningConfig != null ? mSigningConfig : base.mSigningConfig;
 
+        flavor.addResourceConfigurations(base.getResourceConfigurations());
+
         return flavor;
     }
 
@@ -218,6 +325,10 @@
         return overlay != null ? overlay : base;
     }
 
+    private Boolean chooseBoolean(Boolean overlay, Boolean base) {
+        return overlay != null ? overlay : base;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
@@ -226,27 +337,30 @@
 
         DefaultProductFlavor that = (DefaultProductFlavor) o;
 
-        if (!mName.equals(that.mName)) return false;
         if (mMinSdkVersion != that.mMinSdkVersion) return false;
-        if (mTargetSdkVersion != that.mTargetSdkVersion) return false;
         if (mRenderscriptTargetApi != that.mRenderscriptTargetApi) return false;
+        if (mTargetSdkVersion != that.mTargetSdkVersion) return false;
         if (mVersionCode != that.mVersionCode) return false;
-        if (mPackageName != null ?
-                !mPackageName.equals(that.mPackageName) :
-                that.mPackageName != null)
+        if (!mName.equals(that.mName)) return false;
+        if (mPackageName != null ? !mPackageName.equals(that.mPackageName) : that.mPackageName != null)
             return false;
-        if (mTestInstrumentationRunner != null ?
-                !mTestInstrumentationRunner.equals(that.mTestInstrumentationRunner) :
-                that.mTestInstrumentationRunner != null)
+        if (mRenderscriptNdkMode != null ? !mRenderscriptNdkMode.equals(that.mRenderscriptNdkMode) : that.mRenderscriptNdkMode != null)
             return false;
-        if (mTestPackageName != null ?
-                !mTestPackageName.equals(that.mTestPackageName) : that.mTestPackageName != null)
+        if (mRenderscriptSupportMode != null ? !mRenderscriptSupportMode.equals(that.mRenderscriptSupportMode) : that.mRenderscriptSupportMode != null)
             return false;
-        if (mVersionName != null ?
-                !mVersionName.equals(that.mVersionName) : that.mVersionName != null)
+        if (mResourceConfiguration != null ? !mResourceConfiguration.equals(that.mResourceConfiguration) : that.mResourceConfiguration != null)
             return false;
-        if (mSigningConfig != null ?
-                !mSigningConfig.equals(that.mSigningConfig) : that.mSigningConfig != null)
+        if (mSigningConfig != null ? !mSigningConfig.equals(that.mSigningConfig) : that.mSigningConfig != null)
+            return false;
+        if (mTestFunctionalTest != null ? !mTestFunctionalTest.equals(that.mTestFunctionalTest) : that.mTestFunctionalTest != null)
+            return false;
+        if (mTestHandleProfiling != null ? !mTestHandleProfiling.equals(that.mTestHandleProfiling) : that.mTestHandleProfiling != null)
+            return false;
+        if (mTestInstrumentationRunner != null ? !mTestInstrumentationRunner.equals(that.mTestInstrumentationRunner) : that.mTestInstrumentationRunner != null)
+            return false;
+        if (mTestPackageName != null ? !mTestPackageName.equals(that.mTestPackageName) : that.mTestPackageName != null)
+            return false;
+        if (mVersionName != null ? !mVersionName.equals(that.mVersionName) : that.mVersionName != null)
             return false;
 
         return true;
@@ -259,13 +373,17 @@
         result = 31 * result + mMinSdkVersion;
         result = 31 * result + mTargetSdkVersion;
         result = 31 * result + mRenderscriptTargetApi;
+        result = 31 * result + (mRenderscriptSupportMode != null ? mRenderscriptSupportMode.hashCode() : 0);
+        result = 31 * result + (mRenderscriptNdkMode != null ? mRenderscriptNdkMode.hashCode() : 0);
         result = 31 * result + mVersionCode;
         result = 31 * result + (mVersionName != null ? mVersionName.hashCode() : 0);
         result = 31 * result + (mPackageName != null ? mPackageName.hashCode() : 0);
         result = 31 * result + (mTestPackageName != null ? mTestPackageName.hashCode() : 0);
-        result = 31 * result + (mTestInstrumentationRunner != null ?
-                mTestInstrumentationRunner.hashCode() : 0);
+        result = 31 * result + (mTestInstrumentationRunner != null ? mTestInstrumentationRunner.hashCode() : 0);
+        result = 31 * result + (mTestHandleProfiling != null ? mTestHandleProfiling.hashCode() : 0);
+        result = 31 * result + (mTestFunctionalTest != null ? mTestFunctionalTest.hashCode() : 0);
         result = 31 * result + (mSigningConfig != null ? mSigningConfig.hashCode() : 0);
+        result = 31 * result + (mResourceConfiguration != null ? mResourceConfiguration.hashCode() : 0);
         return result;
     }
 
@@ -277,18 +395,17 @@
                 .add("minSdkVersion", mMinSdkVersion)
                 .add("targetSdkVersion", mTargetSdkVersion)
                 .add("renderscriptTargetApi", mRenderscriptTargetApi)
+                .add("renderscriptSupportMode", mRenderscriptSupportMode)
+                .add("renderscriptNdkMode", mRenderscriptNdkMode)
                 .add("versionCode", mVersionCode)
                 .add("versionName", mVersionName)
                 .add("packageName", mPackageName)
                 .add("testPackageName", mTestPackageName)
                 .add("testInstrumentationRunner", mTestInstrumentationRunner)
+                .add("testHandleProfiling", mTestHandleProfiling)
+                .add("testFunctionalTest", mTestFunctionalTest)
                 .add("signingConfig", mSigningConfig)
+                .add("resConfig", mResourceConfiguration)
                 .toString();
     }
-
-    /*
-        release signing info (keystore, key alias, passwords,...).
-        native abi filter
-    */
-
 }
diff --git a/builder/src/main/java/com/android/builder/DefaultSdkParser.java b/builder/src/main/java/com/android/builder/DefaultSdkParser.java
index 3f52971..6244fb9 100644
--- a/builder/src/main/java/com/android/builder/DefaultSdkParser.java
+++ b/builder/src/main/java/com/android/builder/DefaultSdkParser.java
@@ -50,6 +50,7 @@
 public class DefaultSdkParser implements SdkParser {
 
     private final String mSdkLocation;
+    private final File mNdkLocation;
     private SdkManager mManager;
 
     private IAndroidTarget mTarget;
@@ -60,12 +61,13 @@
     private File mAdb;
     private File mZipAlign;
 
-    public DefaultSdkParser(@NonNull String sdkLocation) {
+    public DefaultSdkParser(@NonNull String sdkLocation, @Nullable File ndkLocation) {
         if (!sdkLocation.endsWith(File.separator)) {
             mSdkLocation = sdkLocation + File.separator;
         } else {
             mSdkLocation = sdkLocation;
         }
+        mNdkLocation = ndkLocation;
     }
 
     @Override
@@ -211,4 +213,10 @@
 
         return mTools;
     }
+
+    @Nullable
+    @Override
+    public File getNdkLocation() {
+        return mNdkLocation;
+    }
 }
diff --git a/builder/src/main/java/com/android/builder/DexOptions.java b/builder/src/main/java/com/android/builder/DexOptions.java
index 966d081..1db13be 100644
--- a/builder/src/main/java/com/android/builder/DexOptions.java
+++ b/builder/src/main/java/com/android/builder/DexOptions.java
@@ -20,5 +20,7 @@
 
     boolean isCoreLibrary();
     boolean getIncremental();
+    boolean getPreDexLibraries();
+    boolean getJumboMode();
     String getJavaMaxHeapSize();
 }
diff --git a/builder/src/main/java/com/android/builder/ManifestParser.java b/builder/src/main/java/com/android/builder/ManifestParser.java
index 5b6411c..3b8e4a3 100644
--- a/builder/src/main/java/com/android/builder/ManifestParser.java
+++ b/builder/src/main/java/com/android/builder/ManifestParser.java
@@ -21,14 +21,56 @@
 
 import java.io.File;
 
+/**
+ * A Manifest parser
+ */
 public interface ManifestParser {
 
+    /**
+     * Returns the package name parsed from the given manifest file.
+     *
+     * @param manifestFile the manifest file to parse
+     *
+     * @return the package name or null if not found.
+     */
     @Nullable
     String getPackage(@NonNull File manifestFile);
 
+    /**
+     * Returns the minSdkVersion parsed from the given manifest file.
+     *
+     * @param manifestFile the manifest file to parse
+     *
+     * @return the minSdkVersion or 1 if not found.
+     */
     int getMinSdkVersion(@NonNull File manifestFile);
+
+    /**
+     * Returns the targetSdkVersion parsed from the given manifest file.
+     *
+     * @param manifestFile the manifest file to parse
+     *
+     * @return the targetSdkVersion or -1 if not found.
+     */
     int getTargetSdkVersion(@NonNull File manifestFile);
 
+    /**
+     * Returns the version name parsed from the given manifest file.
+     *
+     * @param manifestFile the manifest file to parse
+     *
+     * @return the version name or null if not found.
+     */
     @Nullable
     String getVersionName(@NonNull File manifestFile);
+
+    /**
+     * Returns the version code parsed from the given manifest file.
+     *
+     * @param manifestFile the manifest file to parse
+     *
+     * @return the version code or -1 if not found.
+     */
+    int getVersionCode(@NonNull File manifestFile);
+
 }
diff --git a/builder/src/main/java/com/android/builder/PlatformSdkParser.java b/builder/src/main/java/com/android/builder/PlatformSdkParser.java
index 3325ea0..d0e1828 100644
--- a/builder/src/main/java/com/android/builder/PlatformSdkParser.java
+++ b/builder/src/main/java/com/android/builder/PlatformSdkParser.java
@@ -165,4 +165,10 @@
         }
         return mHostTools;
     }
+
+    @Nullable
+    @Override
+    public File getNdkLocation() {
+        return null;
+    }
 }
diff --git a/builder/src/main/java/com/android/builder/SdkParser.java b/builder/src/main/java/com/android/builder/SdkParser.java
index 7c4e0c2..c247694 100644
--- a/builder/src/main/java/com/android/builder/SdkParser.java
+++ b/builder/src/main/java/com/android/builder/SdkParser.java
@@ -94,6 +94,13 @@
     @NonNull
     File getAdb();
 
+    /**
+     * Returns the location of artifact repositories built-in the SDK.
+     * @return a non null list of repository folders.
+     */
     @NonNull
     List<File> getRepositories();
+
+    @Nullable
+    File getNdkLocation();
 }
\ No newline at end of file
diff --git a/builder/src/main/java/com/android/builder/VariantConfiguration.java b/builder/src/main/java/com/android/builder/VariantConfiguration.java
index 614d467..80df454 100644
--- a/builder/src/main/java/com/android/builder/VariantConfiguration.java
+++ b/builder/src/main/java/com/android/builder/VariantConfiguration.java
@@ -22,6 +22,11 @@
 import com.android.builder.dependency.DependencyContainer;
 import com.android.builder.dependency.JarDependency;
 import com.android.builder.dependency.LibraryDependency;
+import com.android.builder.internal.MergedNdkConfig;
+import com.android.builder.internal.StringHelper;
+import com.android.builder.model.ClassField;
+import com.android.builder.model.NdkConfig;
+import com.android.builder.model.ProductFlavor;
 import com.android.builder.model.SigningConfig;
 import com.android.builder.model.SourceProvider;
 import com.android.builder.testing.TestData;
@@ -32,6 +37,7 @@
 
 import java.io.File;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 
@@ -45,25 +51,61 @@
 
     private static final ManifestParser sManifestParser = new DefaultManifestParser();
 
+    /**
+     * Full, unique name of the variant in camel case, including BuildType and Flavors (and Test)
+     */
+    private String mFullName;
+    /**
+     * Flavor Name of the variant, including all flavors in camel case (starting with a lower
+     * case).
+     */
+    private String mFlavorName;
+    /**
+     * Full, unique name of the variant, including BuildType, flavors and test, dash separated.
+     * (similar to full name but with dashes)
+     */
+    private String mBaseName;
+    /**
+     * Unique directory name (can include multiple folders) for the variant, based on build type,
+     * flavor and test.
+     * This always uses forward slashes ('/') as separator on all platform.
+     *
+     */
+    private String mDirName;
+
+    @NonNull
     private final DefaultProductFlavor mDefaultConfig;
+    @NonNull
     private final SourceProvider mDefaultSourceProvider;
 
+    @NonNull
     private final DefaultBuildType mBuildType;
     /** SourceProvider for the BuildType. Can be null */
+    @Nullable
     private final SourceProvider mBuildTypeSourceProvider;
 
+    private final List<String> mFlavorDimensionNames = Lists.newArrayList();
     private final List<DefaultProductFlavor> mFlavorConfigs = Lists.newArrayList();
     private final List<SourceProvider> mFlavorSourceProviders = Lists.newArrayList();
 
+    /** Variant specific source provider, may be null */
+    @Nullable
+    private SourceProvider mVariantSourceProvider;
+
+    /** MultiFlavors specific source provider, may be null */
+    @Nullable
+    private SourceProvider mMultiFlavorSourceProvider;
+
+    @NonNull
     private final Type mType;
     /** Optional tested config in case type is Type#TEST */
     private final VariantConfiguration mTestedConfig;
-    private final String mDebugName;
     /** An optional output that is only valid if the type is Type#LIBRARY so that the test
      * for the library can use the library as if it was a normal dependency. */
     private LibraryDependency mOutput;
 
     private DefaultProductFlavor mMergedFlavor;
+    private final MergedNdkConfig mMergedNdkConfig = new MergedNdkConfig();
 
     private final Set<JarDependency> mJars = Sets.newHashSet();
 
@@ -98,18 +140,16 @@
      * @param defaultSourceProvider the default source provider. Required
      * @param buildType the build type for this variant. Required.
      * @param buildTypeSourceProvider the source provider for the build type. Required.
-     * @param debugName an optional debug name
      */
     public VariantConfiguration(
             @NonNull DefaultProductFlavor defaultConfig,
             @NonNull SourceProvider defaultSourceProvider,
             @NonNull DefaultBuildType buildType,
-            @NonNull SourceProvider buildTypeSourceProvider,
-            @Nullable String debugName) {
-        this(defaultConfig, defaultSourceProvider,
+            @Nullable SourceProvider buildTypeSourceProvider) {
+        this(
+                defaultConfig, defaultSourceProvider,
                 buildType, buildTypeSourceProvider,
-                Type.DEFAULT, null /*testedConfig*/,
-                debugName);
+                Type.DEFAULT, null /*testedConfig*/);
     }
 
     /**
@@ -120,19 +160,17 @@
      * @param buildType the build type for this variant. Required.
      * @param buildTypeSourceProvider the source provider for the build type.
      * @param type the type of the project.
-     * @param debugName an optional debug name
      */
     public VariantConfiguration(
             @NonNull DefaultProductFlavor defaultConfig,
             @NonNull SourceProvider defaultSourceProvider,
             @NonNull DefaultBuildType buildType,
             @Nullable SourceProvider buildTypeSourceProvider,
-            @NonNull Type type,
-            @Nullable String debugName) {
-        this(defaultConfig, defaultSourceProvider,
+            @NonNull Type type) {
+        this(
+                defaultConfig, defaultSourceProvider,
                 buildType, buildTypeSourceProvider,
-                type, null /*testedConfig*/,
-                debugName);
+                type, null /*testedConfig*/);
     }
 
     /**
@@ -144,7 +182,6 @@
      * @param buildTypeSourceProvider the source provider for the build type.
      * @param type the type of the project.
      * @param testedConfig the reference to the tested project. Required if type is Type.TEST
-     * @param debugName an optional debug name
      */
     public VariantConfiguration(
             @NonNull DefaultProductFlavor defaultConfig,
@@ -152,18 +189,17 @@
             @NonNull DefaultBuildType buildType,
             @Nullable SourceProvider buildTypeSourceProvider,
             @NonNull Type type,
-            @Nullable VariantConfiguration testedConfig,
-            @Nullable String debugName) {
+            @Nullable VariantConfiguration testedConfig) {
         mDefaultConfig = checkNotNull(defaultConfig);
         mDefaultSourceProvider = checkNotNull(defaultSourceProvider);
         mBuildType = checkNotNull(buildType);
         mBuildTypeSourceProvider = buildTypeSourceProvider;
         mType = checkNotNull(type);
         mTestedConfig = testedConfig;
-        mDebugName = debugName;
         checkState(mType != Type.TEST || mTestedConfig != null);
 
         mMergedFlavor = mDefaultConfig;
+        computeNdkConfig();
 
         if (testedConfig != null &&
                 testedConfig.mType == Type.LIBRARY &&
@@ -175,26 +211,240 @@
     }
 
     /**
+     * Returns the full, unique name of the variant in camel case (starting with a lower case),
+     * including BuildType, Flavors and Test (if applicable).
+     *
+     * @return the name of the variant
+     */
+    @NonNull
+    public String getFullName() {
+        if (mFullName == null) {
+            StringBuilder sb = new StringBuilder();
+            String flavorName = getFlavorName();
+            if (!flavorName.isEmpty()) {
+                sb.append(flavorName);
+                sb.append(StringHelper.capitalize(mBuildType.getName()));
+            } else {
+                sb.append(mBuildType.getName());
+            }
+
+            if (mType == Type.TEST) {
+                sb.append("Test");
+            }
+
+            mFullName = sb.toString();
+        }
+
+        return mFullName;
+    }
+
+
+    /**
+     * Returns the flavor name of the variant, including all flavors in camel case (starting
+     * with a lower case). If the variant has no flavor, then an empty string is returned.
+     *
+     * @return the flavor name or an empty string.
+     */
+    @NonNull
+    public String getFlavorName() {
+        if (mFlavorName == null) {
+            if (mFlavorConfigs.isEmpty()) {
+                mFlavorName = "";
+            } else {
+                StringBuilder sb = new StringBuilder();
+                boolean first = true;
+                for (DefaultProductFlavor flavor : mFlavorConfigs) {
+                    sb.append(first ? flavor.getName() : StringHelper.capitalize(flavor.getName()));
+                    first = false;
+                }
+
+                mFlavorName = sb.toString();
+            }
+        }
+
+        return mFlavorName;
+    }
+
+    /**
+     * Returns the full, unique name of the variant, including BuildType, flavors and test,
+     * dash separated. (similar to full name but with dashes)
+     *
+     * @return the name of the variant
+     */
+    @NonNull
+    public String getBaseName() {
+        if (mBaseName == null) {
+            StringBuilder sb = new StringBuilder();
+
+            if (!mFlavorConfigs.isEmpty()) {
+                for (ProductFlavor pf : mFlavorConfigs) {
+                    sb.append(pf.getName()).append('-');
+                }
+            }
+
+            sb.append(mBuildType.getName());
+
+            if (mType == Type.TEST) {
+                sb.append('-').append("test");
+            }
+
+            mBaseName = sb.toString();
+        }
+
+        return mBaseName;
+    }
+
+    /**
+     * Returns a unique directory name (can include multiple folders) for the variant,
+     * based on build type, flavor and test.
+     * This always uses forward slashes ('/') as separator on all platform.
+     *
+     * @return the directory name for the variant
+     */
+    @NonNull
+    public String getDirName() {
+        if (mDirName == null) {
+            StringBuilder sb = new StringBuilder();
+
+            if (mType == Type.TEST) {
+                sb.append("test/");
+            }
+
+            if (!mFlavorConfigs.isEmpty()) {
+                for (DefaultProductFlavor flavor : mFlavorConfigs) {
+                    sb.append(flavor.getName());
+                }
+
+                sb.append('/').append(mBuildType.getName());
+
+            } else {
+                sb.append(mBuildType.getName());
+            }
+
+            mDirName = sb.toString();
+
+        }
+
+        return mDirName;
+    }
+
+    /**
+     * Return the names of the applied flavors.
+     *
+     * The list contains the dimension names as well.
+     *
+     * @return the list, possibly empty if there are no flavors.
+     */
+    @NonNull
+    public List<String> getFlavorNamesWithDimensionNames() {
+        if (mFlavorConfigs.isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        List<String> names;
+        int count = mFlavorConfigs.size();
+
+        if (count > 1) {
+            names = Lists.newArrayListWithCapacity(count * 2);
+
+            for (int i = 0 ; i < count ; i++) {
+                names.add(mFlavorConfigs.get(i).getName());
+                names.add(mFlavorDimensionNames.get(i));
+            }
+
+        } else {
+            names = Collections.singletonList(mFlavorConfigs.get(0).getName());
+        }
+
+        return names;
+    }
+
+
+    /**
      * Add a new configured ProductFlavor.
      *
      * If multiple flavors are added, the priority follows the order they are added when it
      * comes to resolving Android resources overlays (ie earlier added flavors supersedes
      * latter added ones).
      *
-     * @param sourceProvider the configured product flavor
+     * @param productFlavor the configured product flavor
+     * @param sourceProvider the source provider for the product flavor
+     * @param dimensionName the name of the dimension associated with the flavor
+     *
      * @return the config object
      */
     @NonNull
-    public VariantConfiguration addProductFlavor(@NonNull DefaultProductFlavor productFlavor,
-                                                 @NonNull SourceProvider sourceProvider) {
+    public VariantConfiguration addProductFlavor(
+            @NonNull DefaultProductFlavor productFlavor,
+            @NonNull SourceProvider sourceProvider,
+            @NonNull String dimensionName) {
+
         mFlavorConfigs.add(productFlavor);
         mFlavorSourceProviders.add(sourceProvider);
+        mFlavorDimensionNames.add(dimensionName);
+
         mMergedFlavor = productFlavor.mergeOver(mMergedFlavor);
+        computeNdkConfig();
 
         return this;
     }
 
     /**
+     * Sets the variant-specific source provider.
+     * @param sourceProvider the source provider for the product flavor
+     *
+     * @return the config object
+     */
+    public VariantConfiguration setVariantSourceProvider(@Nullable SourceProvider sourceProvider) {
+        mVariantSourceProvider = sourceProvider;
+        return this;
+    }
+
+    /**
+     * Sets the variant-specific source provider.
+     * @param sourceProvider the source provider for the product flavor
+     *
+     * @return the config object
+     */
+    public VariantConfiguration setMultiFlavorSourceProvider(@Nullable SourceProvider sourceProvider) {
+        mMultiFlavorSourceProvider = sourceProvider;
+        return this;
+    }
+
+    /**
+     * Returns the variant specific source provider
+     * @return the source provider or null if none has been provided.
+     */
+    @Nullable
+    public SourceProvider getVariantSourceProvider() {
+        return mVariantSourceProvider;
+    }
+
+    @Nullable
+    public SourceProvider getMultiFlavorSourceProvider() {
+        return mMultiFlavorSourceProvider;
+    }
+
+    private void computeNdkConfig() {
+        mMergedNdkConfig.reset();
+
+        if (mDefaultConfig.getNdkConfig() != null) {
+            mMergedNdkConfig.append(mDefaultConfig.getNdkConfig());
+        }
+
+        for (int i = mFlavorConfigs.size() - 1 ; i >= 0 ; i--) {
+            NdkConfig ndkConfig = mFlavorConfigs.get(i).getNdkConfig();
+            if (ndkConfig != null) {
+                mMergedNdkConfig.append(ndkConfig);
+            }
+        }
+
+        if (mBuildType.getNdkConfig() != null && mType != Type.TEST) {
+            mMergedNdkConfig.append(mBuildType.getNdkConfig());
+        }
+    }
+
+    /**
      * Sets the dependencies
      *
      * @param container a DependencyContainer.
@@ -276,8 +526,15 @@
         return mFlavorConfigs;
     }
 
+    /**
+     * Returns the list of SourceProviders for the flavors.
+     *
+     * The list is ordered from higher priority to lower priority.
+     *
+     * @return the list of Source Providers for the flavors. Never null.
+     */
     @NonNull
-    public Iterable<SourceProvider> getFlavorSourceSets() {
+    public List<SourceProvider> getFlavorSourceProviders() {
         return mFlavorSourceProviders;
     }
 
@@ -330,10 +587,11 @@
             LibraryDependency library = directDependencies.get(i);
 
             // get its libraries
-            List<LibraryDependency> dependencies = library.getDependencies();
+            Collection<LibraryDependency> dependencies = library.getDependencies();
+            List<LibraryDependency> depList = Lists.newArrayList(dependencies);
 
             // resolve the dependencies for those libraries
-            resolveIndirectLibraryDependencies(dependencies, outFlatDependencies);
+            resolveIndirectLibraryDependencies(depList, outFlatDependencies);
 
             // and add the current one (if needed) in front (higher priority)
             if (!outFlatDependencies.contains(library)) {
@@ -386,7 +644,7 @@
         }
 
         if (packageName == null) {
-            throw new RuntimeException("Failed get query package name for " + mDebugName);
+            throw new RuntimeException("Failed get query package name for " + getFullName());
         }
 
         return packageName;
@@ -447,7 +705,11 @@
 
         if (versionSuffix != null && versionSuffix.length() > 0) {
             if (versionName == null) {
-                versionName = getVersionNameFromManifest();
+                if (mType != Type.TEST) {
+                    versionName = getVersionNameFromManifest();
+                } else {
+                    versionName = "";
+                }
             }
 
             versionName = versionName + versionSuffix;
@@ -456,7 +718,27 @@
         return versionName;
     }
 
+    /**
+     * Returns the version code for this variant. This could be coming from the manifest or
+     * could be overridden through the product flavors, and can have a suffix specified by
+     * the build type.
+     *
+     * @return the version code or -1 if there was non defined.
+     */
+    public int getVersionCode() {
+        int versionCode = mMergedFlavor.getVersionCode();
+
+        if (versionCode == -1 && mType != Type.TEST) {
+
+            versionCode = getVersionCodeFromManifest();
+        }
+
+        return versionCode;
+    }
+
     private final static String DEFAULT_TEST_RUNNER = "android.test.InstrumentationTestRunner";
+    private final static Boolean DEFAULT_HANDLE_PROFILING = false;
+    private final static Boolean DEFAULT_FUNCTIONAL_TEST = false;
 
     /**
      * Returns the instrumentationRunner to use to test this variant, or if the
@@ -475,6 +757,38 @@
     }
 
     /**
+     * Returns handleProfiling value to use to test this variant, or if the
+     * variant is a test, the one to use to test the tested variant.
+     * @return the handleProfiling value
+     */
+    @Override
+    @NonNull
+    public Boolean getHandleProfiling() {
+        VariantConfiguration config = this;
+        if (mType == Type.TEST) {
+            config = getTestedConfig();
+        }
+        Boolean handleProfiling = config.mMergedFlavor.getTestHandleProfiling();
+        return handleProfiling != null ? handleProfiling : DEFAULT_HANDLE_PROFILING;
+    }
+
+    /**
+     * Returns functionalTest value to use to test this variant, or if the
+     * variant is a test, the one to use to test the tested variant.
+     * @return the functionalTest value
+     */
+    @Override
+    @NonNull
+    public Boolean getFunctionalTest() {
+        VariantConfiguration config = this;
+        if (mType == Type.TEST) {
+            config = getTestedConfig();
+        }
+        Boolean functionalTest = config.mMergedFlavor.getTestFunctionalTest();
+        return functionalTest != null ? functionalTest : DEFAULT_FUNCTIONAL_TEST;
+    }
+
+    /**
      * Reads the package name from the manifest. This is unmodified by the build type.
      */
     @Nullable
@@ -494,6 +808,14 @@
     }
 
     /**
+     * Reads the version code from the manifest.
+     */
+    public int getVersionCodeFromManifest() {
+        File manifestLocation = mDefaultSourceProvider.getManifestFile();
+        return sManifestParser.getVersionCode(manifestLocation);
+    }
+
+    /**
      * Return the minSdkVersion for this variant.
      *
      * This uses both the value from the manifest (if present), and the override coming
@@ -552,6 +874,13 @@
     public List<File> getManifestOverlays() {
         List<File> inputs = Lists.newArrayList();
 
+        if (mVariantSourceProvider != null) {
+            File variantLocation = mVariantSourceProvider.getManifestFile();
+            if (variantLocation.isFile()) {
+                inputs.add(variantLocation);
+            }
+        }
+
         if (mBuildTypeSourceProvider != null) {
             File typeLocation = mBuildTypeSourceProvider.getManifestFile();
             if (typeLocation.isFile()) {
@@ -559,6 +888,13 @@
             }
         }
 
+        if (mMultiFlavorSourceProvider != null) {
+            File variantLocation = mMultiFlavorSourceProvider.getManifestFile();
+            if (variantLocation.isFile()) {
+                inputs.add(variantLocation);
+            }
+        }
+
         for (SourceProvider sourceProvider : mFlavorSourceProviders) {
             File f = sourceProvider.getManifestFile();
             if (f.isFile()) {
@@ -601,7 +937,7 @@
             }
         }
 
-        Set<File> mainResDirs = mDefaultSourceProvider.getResDirectories();
+        Collection<File> mainResDirs = mDefaultSourceProvider.getResDirectories();
 
         ResourceSet resourceSet = new ResourceSet(BuilderConstants.MAIN);
         resourceSet.addSources(mainResDirs);
@@ -614,7 +950,7 @@
         for (int n = mFlavorSourceProviders.size() - 1; n >= 0 ; n--) {
             SourceProvider sourceProvider = mFlavorSourceProviders.get(n);
 
-            Set<File> flavorResDirs = sourceProvider.getResDirectories();
+            Collection<File> flavorResDirs = sourceProvider.getResDirectories();
             // we need the same of the flavor config, but it's in a different list.
             // This is fine as both list are parallel collections with the same number of items.
             resourceSet = new ResourceSet(mFlavorConfigs.get(n).getName());
@@ -622,13 +958,30 @@
             resourceSets.add(resourceSet);
         }
 
+        // multiflavor specific overrides flavor
+        if (mMultiFlavorSourceProvider != null) {
+            Collection<File> variantResDirs = mMultiFlavorSourceProvider.getResDirectories();
+            resourceSet = new ResourceSet(getFlavorName());
+            resourceSet.addSources(variantResDirs);
+            resourceSets.add(resourceSet);
+        }
+
+        // build type overrides the flavors
         if (mBuildTypeSourceProvider != null) {
-            Set<File> typeResDirs = mBuildTypeSourceProvider.getResDirectories();
+            Collection<File> typeResDirs = mBuildTypeSourceProvider.getResDirectories();
             resourceSet = new ResourceSet(mBuildType.getName());
             resourceSet.addSources(typeResDirs);
             resourceSets.add(resourceSet);
         }
 
+        // variant specific overrides all
+        if (mVariantSourceProvider != null) {
+            Collection<File> variantResDirs = mVariantSourceProvider.getResDirectories();
+            resourceSet = new ResourceSet(getFullName());
+            resourceSet.addSources(variantResDirs);
+            resourceSets.add(resourceSet);
+        }
+
         return resourceSets;
     }
 
@@ -659,7 +1012,7 @@
             }
         }
 
-        Set<File> mainResDirs = mDefaultSourceProvider.getAssetsDirectories();
+        Collection<File> mainResDirs = mDefaultSourceProvider.getAssetsDirectories();
 
         AssetSet assetSet = new AssetSet(BuilderConstants.MAIN);
         assetSet.addSources(mainResDirs);
@@ -669,7 +1022,7 @@
         for (int n = mFlavorSourceProviders.size() - 1; n >= 0 ; n--) {
             SourceProvider sourceProvider = mFlavorSourceProviders.get(n);
 
-            Set<File> flavorResDirs = sourceProvider.getAssetsDirectories();
+            Collection<File> flavorResDirs = sourceProvider.getAssetsDirectories();
             // we need the same of the flavor config, but it's in a different list.
             // This is fine as both list are parallel collections with the same number of items.
             assetSet = new AssetSet(mFlavorConfigs.get(n).getName());
@@ -677,16 +1030,48 @@
             assetSets.add(assetSet);
         }
 
+        // multiflavor specific overrides flavor
+        if (mMultiFlavorSourceProvider != null) {
+            Collection<File> variantResDirs = mMultiFlavorSourceProvider.getAssetsDirectories();
+            assetSet = new AssetSet(getFlavorName());
+            assetSet.addSources(variantResDirs);
+            assetSets.add(assetSet);
+        }
+
+        // build type overrides flavors
         if (mBuildTypeSourceProvider != null) {
-            Set<File> typeResDirs = mBuildTypeSourceProvider.getAssetsDirectories();
+            Collection<File> typeResDirs = mBuildTypeSourceProvider.getAssetsDirectories();
             assetSet = new AssetSet(mBuildType.getName());
             assetSet.addSources(typeResDirs);
             assetSets.add(assetSet);
         }
 
+        // variant specific overrides all
+        if (mVariantSourceProvider != null) {
+            Collection<File> variantResDirs = mVariantSourceProvider.getAssetsDirectories();
+            assetSet = new AssetSet(getFullName());
+            assetSet.addSources(variantResDirs);
+            assetSets.add(assetSet);
+        }
+
         return assetSets;
     }
 
+    @NonNull
+    public List<File> getLibraryJniFolders() {
+        List<File> list = Lists.newArrayListWithExpectedSize(mFlatLibraries.size());
+
+        for (int n = mFlatLibraries.size() - 1 ; n >= 0 ; n--) {
+            LibraryDependency dependency = mFlatLibraries.get(n);
+            File jniFolder = dependency.getJniFolder();
+            if (jniFolder.isDirectory()) {
+                list.add(jniFolder);
+            }
+        }
+
+        return list;
+    }
+
     /**
      * Returns all the renderscript import folder that are outside of the current project.
      */
@@ -724,6 +1109,14 @@
             }
         }
 
+        if (mMultiFlavorSourceProvider != null) {
+            sourceList.addAll(mMultiFlavorSourceProvider.getRenderscriptDirectories());
+        }
+
+        if (mVariantSourceProvider != null) {
+            sourceList.addAll(mVariantSourceProvider.getRenderscriptDirectories());
+        }
+
         return sourceList;
     }
 
@@ -758,6 +1151,39 @@
             }
         }
 
+        if (mMultiFlavorSourceProvider != null) {
+            sourceList.addAll(mMultiFlavorSourceProvider.getAidlDirectories());
+        }
+
+        if (mVariantSourceProvider != null) {
+            sourceList.addAll(mVariantSourceProvider.getAidlDirectories());
+        }
+
+        return sourceList;
+    }
+
+    @NonNull
+    public List<File> getJniSourceList() {
+        List<File> sourceList = Lists.newArrayList();
+        sourceList.addAll(mDefaultSourceProvider.getJniDirectories());
+        if (mType != Type.TEST && mBuildTypeSourceProvider != null) {
+            sourceList.addAll(mBuildTypeSourceProvider.getJniDirectories());
+        }
+
+        if (hasFlavors()) {
+            for (SourceProvider flavorSourceSet : mFlavorSourceProviders) {
+                sourceList.addAll(flavorSourceSet.getJniDirectories());
+            }
+        }
+
+        if (mMultiFlavorSourceProvider != null) {
+            sourceList.addAll(mMultiFlavorSourceProvider.getJniDirectories());
+        }
+
+        if (mVariantSourceProvider != null) {
+            sourceList.addAll(mVariantSourceProvider.getJniDirectories());
+        }
+
         return sourceList;
     }
 
@@ -819,27 +1245,52 @@
         return Lists.newArrayList(jars);
     }
 
+    /**
+     * Returns a list of items for the BuildConfig class.
+     *
+     * Items can be either fields (instance of {@link com.android.builder.model.ClassField})
+     * or comments (instance of String).
+     *
+     * @return a list of items.
+     */
     @NonNull
-    public List<String> getBuildConfigLines() {
-        List<String> fullList = Lists.newArrayList();
+    public List<Object> getBuildConfigItems() {
+        List<Object> fullList = Lists.newArrayList();
 
-        List<String> list = mDefaultConfig.getBuildConfig();
-        if (!list.isEmpty()) {
-            fullList.add("// lines from default config.");
-            fullList.addAll(list);
-        }
+        Set<String> usedFieldNames = Sets.newHashSet();
 
-        list = mBuildType.getBuildConfig();
+        List<ClassField> list = mBuildType.getBuildConfigFields();
         if (!list.isEmpty()) {
-            fullList.add("// lines from build type: " + mBuildType.getName());
-            fullList.addAll(list);
+            fullList.add("Fields from build type: " + mBuildType.getName());
+            for (ClassField f : list) {
+                usedFieldNames.add(f.getName());
+                fullList.add(f);
+            }
         }
 
         for (DefaultProductFlavor flavor : mFlavorConfigs) {
-            list = flavor.getBuildConfig();
+            list = flavor.getBuildConfigFields();
             if (!list.isEmpty()) {
-                fullList.add("// lines from product flavor: " + flavor.getName());
-                fullList.addAll(list);
+                fullList.add("Fields from product flavor: " + flavor.getName());
+                for (ClassField f : list) {
+                    String name = f.getName();
+                    if (!usedFieldNames.contains(name)) {
+                        usedFieldNames.add(f.getName());
+                        fullList.add(f);
+                    }
+                }
+            }
+        }
+
+        list = mDefaultConfig.getBuildConfigFields();
+        if (!list.isEmpty()) {
+            fullList.add("Fields from default config.");
+            for (ClassField f : list) {
+                String name = f.getName();
+                if (!usedFieldNames.contains(name)) {
+                    usedFieldNames.add(f.getName());
+                    fullList.add(f);
+                }
             }
         }
 
@@ -885,6 +1336,21 @@
         return fullList;
     }
 
+    @NonNull
+    public List<Object> getConsumerProguardFiles() {
+        List<Object> fullList = Lists.newArrayList();
+
+        // add the config files from the build type, main config and flavors
+        fullList.addAll(mDefaultConfig.getConsumerProguardFiles());
+        fullList.addAll(mBuildType.getConsumerProguardFiles());
+
+        for (DefaultProductFlavor flavor : mFlavorConfigs) {
+            fullList.addAll(flavor.getConsumerProguardFiles());
+        }
+
+        return fullList;
+    }
+
     protected void validate() {
         if (mType != Type.TEST) {
             File manifest = mDefaultSourceProvider.getManifestFile();
@@ -895,11 +1361,18 @@
         }
     }
 
+    @NonNull
+    public NdkConfig getNdkConfig() {
+        return mMergedNdkConfig;
+    }
 
     @Nullable
     @Override
     public Set<String> getSupportedAbis() {
-        // no ndk support yet, so return null
+        if (mMergedNdkConfig != null) {
+            return mMergedNdkConfig.getAbiFilters();
+        }
+
         return null;
     }
 }
diff --git a/builder/src/main/java/com/android/builder/compiling/BuildConfigGenerator.java b/builder/src/main/java/com/android/builder/compiling/BuildConfigGenerator.java
new file mode 100644
index 0000000..1e298b4
--- /dev/null
+++ b/builder/src/main/java/com/android/builder/compiling/BuildConfigGenerator.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2011 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.builder.compiling;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.builder.AndroidBuilder;
+import com.android.builder.model.ClassField;
+import com.google.common.collect.Lists;
+import com.squareup.javawriter.JavaWriter;
+
+import javax.lang.model.element.Modifier;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Class able to generate a BuildConfig class in Android project.
+ * The BuildConfig class contains constants related to the build target.
+ */
+public class BuildConfigGenerator {
+
+    public final static String BUILD_CONFIG_NAME = "BuildConfig.java";
+
+    private final String mGenFolder;
+    private final String mBuildConfigPackageName;
+
+    private final List<ClassField> mFields = Lists.newArrayList();
+    private List<Object> mItems = Lists.newArrayList();
+
+    /**
+     * Creates a generator
+     * @param genFolder the gen folder of the project
+     * @param buildConfigPackageName the package in which to create the class.
+     */
+    public BuildConfigGenerator(@NonNull String genFolder, @NonNull String buildConfigPackageName) {
+        mGenFolder = checkNotNull(genFolder);
+        mBuildConfigPackageName = checkNotNull(buildConfigPackageName);
+    }
+
+    public BuildConfigGenerator addField(
+            @NonNull String type, @NonNull String name, @NonNull String value) {
+        mFields.add(AndroidBuilder.createClassField(type, name, value));
+        return this;
+    }
+
+    public BuildConfigGenerator addItems(@Nullable Collection<Object> items) {
+        if (items != null) {
+            mItems.addAll(items);
+        }
+        return this;
+    }
+
+    /**
+     * Returns a File representing where the BuildConfig class will be.
+     */
+    public File getFolderPath() {
+        File genFolder = new File(mGenFolder);
+        return new File(genFolder, mBuildConfigPackageName.replace('.', File.separatorChar));
+    }
+
+    public File getBuildConfigFile() {
+        File folder = getFolderPath();
+        return new File(folder, BUILD_CONFIG_NAME);
+    }
+
+    /**
+     * Generates the BuildConfig class.
+     */
+    public void generate() throws IOException {
+        File pkgFolder = getFolderPath();
+        if (!pkgFolder.isDirectory()) {
+            if (!pkgFolder.mkdirs()) {
+                throw new RuntimeException("Failed to create " + pkgFolder.getAbsolutePath());
+            }
+        }
+
+        File buildConfigJava = new File(pkgFolder, BUILD_CONFIG_NAME);
+        FileWriter out = new FileWriter(buildConfigJava);
+
+        JavaWriter writer = new JavaWriter(out);
+
+        Set<Modifier> publicFinal = EnumSet.of(Modifier.PUBLIC, Modifier.FINAL);
+        Set<Modifier> publicFinalStatic = EnumSet.of(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC);
+
+        writer.emitJavadoc("Automatically generated file. DO NOT MODIFY")
+                .emitPackage(mBuildConfigPackageName)
+                .beginType("BuildConfig", "class", publicFinal);
+
+        for (ClassField field : mFields) {
+            writer.emitField(
+                    field.getType(),
+                    field.getName(),
+                    publicFinalStatic,
+                    field.getValue());
+        }
+
+        for (Object item : mItems) {
+            if (item instanceof ClassField) {
+                ClassField field = (ClassField)item;
+                writer.emitField(
+                        field.getType(),
+                        field.getName(),
+                        publicFinalStatic,
+                        field.getValue());
+
+            } else if (item instanceof String) {
+                writer.emitSingleLineComment((String) item);
+            }
+        }
+
+        writer.endType();
+
+        out.close();
+    }
+}
diff --git a/builder/src/main/java/com/android/builder/dependency/LibraryDependency.java b/builder/src/main/java/com/android/builder/dependency/LibraryDependency.java
index c143276..3649588 100644
--- a/builder/src/main/java/com/android/builder/dependency/LibraryDependency.java
+++ b/builder/src/main/java/com/android/builder/dependency/LibraryDependency.java
@@ -19,6 +19,7 @@
 import com.android.annotations.NonNull;
 import com.android.builder.model.AndroidLibrary;
 
+import java.util.Collection;
 import java.util.List;
 
 /**
@@ -27,15 +28,15 @@
 public interface LibraryDependency extends AndroidLibrary, ManifestDependency, SymbolFileProvider {
 
     /**
-     * Returns the direct dependency of this dependency.
+     * Returns the direct dependency of this dependency. The order is important
      */
     @NonNull
     List<LibraryDependency> getDependencies();
 
     /**
-     * Returns the list of local Jar files that are included in the dependency.
+     * Returns the collection of local Jar files that are included in the dependency.
      * @return a list of JarDependency. May be empty but not null.
      */
     @NonNull
-    List<JarDependency> getLocalDependencies();
+    Collection<JarDependency> getLocalDependencies();
 }
diff --git a/builder/src/main/java/com/android/builder/internal/BaseConfigImpl.java b/builder/src/main/java/com/android/builder/internal/BaseConfigImpl.java
index e2517c5..62653d3 100644
--- a/builder/src/main/java/com/android/builder/internal/BaseConfigImpl.java
+++ b/builder/src/main/java/com/android/builder/internal/BaseConfigImpl.java
@@ -18,11 +18,13 @@
 
 import com.android.annotations.NonNull;
 import com.android.builder.model.BaseConfig;
+import com.android.builder.model.ClassField;
 import com.google.common.collect.Lists;
 
 import java.io.File;
 import java.io.Serializable;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.List;
 
 /**
@@ -31,23 +33,28 @@
 public class BaseConfigImpl implements Serializable, BaseConfig {
     private static final long serialVersionUID = 1L;
 
-    private final List<String> mBuildConfigLines = Lists.newArrayList();
+    private final List<ClassField> mBuildConfigFields = Lists.newArrayList();
     private final List<File> mProguardFiles = Lists.newArrayList();
+    private final List<File> mConsumerProguardFiles = Lists.newArrayList();
 
-    public void setBuildConfig(String... lines) {
-        mBuildConfigLines.clear();
-        mBuildConfigLines.addAll(Arrays.asList(lines));
+    public void setBuildConfigFields(@NonNull ClassField... fields) {
+        mBuildConfigFields.clear();
+        mBuildConfigFields.addAll(Arrays.asList(fields));
     }
 
-    public void setBuildConfig(String line) {
-        mBuildConfigLines.clear();
-        mBuildConfigLines.add(line);
+    public void setBuildConfigFields(@NonNull Collection<ClassField> fields) {
+        mBuildConfigFields.clear();
+        mBuildConfigFields.addAll(fields);
+    }
+
+    public void addBuildConfigField(@NonNull ClassField field) {
+        mBuildConfigFields.add(field);
     }
 
     @Override
     @NonNull
-    public List<String> getBuildConfig() {
-        return mBuildConfigLines;
+    public List<ClassField> getBuildConfigFields() {
+        return mBuildConfigFields;
     }
 
     @Override
@@ -56,12 +63,20 @@
         return mProguardFiles;
     }
 
+    @Override
+    @NonNull
+    public List<File> getConsumerProguardFiles() {
+        return mConsumerProguardFiles;
+    }
+
     protected void _initWith(BaseConfig that) {
-        mBuildConfigLines.clear();
-        mBuildConfigLines.addAll(that.getBuildConfig());
+        setBuildConfigFields(that.getBuildConfigFields());
 
         mProguardFiles.clear();
         mProguardFiles.addAll(that.getProguardFiles());
+
+        mConsumerProguardFiles.clear();
+        mConsumerProguardFiles.addAll(that.getConsumerProguardFiles());
     }
 
     @Override
@@ -71,16 +86,18 @@
 
         BaseConfigImpl that = (BaseConfigImpl) o;
 
-        if (!mBuildConfigLines.equals(that.mBuildConfigLines)) return false;
+        if (!mBuildConfigFields.equals(that.mBuildConfigFields)) return false;
         if (!mProguardFiles.equals(that.mProguardFiles)) return false;
+        if (!mConsumerProguardFiles.equals(that.mConsumerProguardFiles)) return false;
 
         return true;
     }
 
     @Override
     public int hashCode() {
-        int result = mBuildConfigLines.hashCode();
+        int result = mBuildConfigFields.hashCode();
         result = 31 * result + mProguardFiles.hashCode();
+        result = 31 * result + mConsumerProguardFiles.hashCode();
         return result;
     }
 }
diff --git a/builder/src/main/java/com/android/builder/internal/BuildConfigGenerator.java b/builder/src/main/java/com/android/builder/internal/BuildConfigGenerator.java
deleted file mode 100644
index b41ed5d..0000000
--- a/builder/src/main/java/com/android/builder/internal/BuildConfigGenerator.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2011 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.builder.internal;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.google.common.collect.Maps;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-import java.util.Map;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * Class able to generate a BuildConfig class in Android project.
- * The BuildConfig class contains constants related to the build target.
- */
-public class BuildConfigGenerator {
-
-    private final static String TEMPLATE = "BuildConfig.template";
-    private final static String PH_PACKAGE = "#PACKAGE#";
-    private final static String PH_DEBUG = "#DEBUG#";
-    private final static String PH_LINES = "#ADDITIONAL_LINES#";
-
-    private final String mGenFolder;
-    private final String mAppPackage;
-    private final boolean mDebug;
-
-    public final static String BUILD_CONFIG_NAME = "BuildConfig.java";
-
-    /**
-     * Creates a generator
-     * @param genFolder the gen folder of the project
-     * @param appPackage the application package
-     * @param debug whether it's a debug build
-     */
-    public BuildConfigGenerator(@NonNull String genFolder, @NonNull String appPackage,
-                                boolean debug) {
-        mGenFolder = checkNotNull(genFolder);
-        mAppPackage = checkNotNull(appPackage);
-        mDebug = debug;
-    }
-
-    /**
-     * Returns a File representing where the BuildConfig class will be.
-     */
-    public File getFolderPath() {
-        File genFolder = new File(mGenFolder);
-        return new File(genFolder, mAppPackage.replace('.', File.separatorChar));
-    }
-
-    public File getBuildConfigFile() {
-        File folder = getFolderPath();
-        return new File(folder, BUILD_CONFIG_NAME);
-    }
-
-    /**
-     * Generates the BuildConfig class.
-     * @param additionalLines a list of additional lines to be added to the class.
-     */
-    public void generate(@Nullable List<String> additionalLines) throws IOException {
-        Map<String, String> map = Maps.newHashMap();
-        map.put(PH_PACKAGE, mAppPackage);
-        map.put(PH_DEBUG, Boolean.toString(mDebug));
-
-        if (additionalLines != null) {
-            StringBuilder sb = new StringBuilder();
-            for (String line : additionalLines) {
-                sb.append("    ").append(line).append('\n');
-            }
-            map.put(PH_LINES, sb.toString());
-
-        } else {
-            map.put(PH_LINES, "");
-        }
-
-        File pkgFolder = getFolderPath();
-        if (!pkgFolder.isDirectory()) {
-            pkgFolder.mkdirs();
-        }
-
-        File buildConfigJava = new File(pkgFolder, BUILD_CONFIG_NAME);
-
-        TemplateProcessor processor = new TemplateProcessor(
-                BuildConfigGenerator.class.getResourceAsStream(TEMPLATE), map);
-        processor.generate(buildConfigJava);
-    }
-}
diff --git a/builder/src/main/java/com/android/builder/internal/ClassFieldImpl.java b/builder/src/main/java/com/android/builder/internal/ClassFieldImpl.java
new file mode 100644
index 0000000..44824b4
--- /dev/null
+++ b/builder/src/main/java/com/android/builder/internal/ClassFieldImpl.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2013 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.builder.internal;
+
+import com.android.annotations.NonNull;
+import com.android.builder.model.ClassField;
+
+import java.io.Serializable;
+
+/**
+ */
+public final class ClassFieldImpl implements ClassField, Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @NonNull
+    private final String type;
+    @NonNull
+    private final String name;
+    @NonNull
+    private final String value;
+
+    public ClassFieldImpl(@NonNull String type, @NonNull String name, @NonNull String value) {
+        //noinspection ConstantConditions
+        if (type == null || name == null || value == null) {
+            throw new NullPointerException("Build Config field cannot have a null parameter");
+        }
+        this.type = type;
+        this.name = name;
+        this.value = value;
+    }
+
+    @Override
+    @NonNull
+    public String getType() {
+        return type;
+    }
+
+    @Override
+    @NonNull
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    @NonNull
+    public String getValue() {
+        return value;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        ClassFieldImpl that = (ClassFieldImpl) o;
+
+        if (!name.equals(that.name)) return false;
+        if (!type.equals(that.type)) return false;
+        if (!value.equals(that.value)) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = type.hashCode();
+        result = 31 * result + name.hashCode();
+        result = 31 * result + value.hashCode();
+        return result;
+    }
+}
diff --git a/builder/src/main/java/com/android/builder/internal/MergedNdkConfig.java b/builder/src/main/java/com/android/builder/internal/MergedNdkConfig.java
new file mode 100644
index 0000000..bd83e99
--- /dev/null
+++ b/builder/src/main/java/com/android/builder/internal/MergedNdkConfig.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2013 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.builder.internal;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.builder.model.NdkConfig;
+import com.google.common.collect.Sets;
+
+import java.util.Set;
+
+/**
+ * Implementation of NdkConfig used to merge multiple configs together.
+ */
+public class MergedNdkConfig implements NdkConfig {
+
+    private String moduleName;
+    private String cFlags;
+    private Set<String> ldLibs;
+    private Set<String> abiFilters;
+    private String stl;
+
+    public void reset() {
+        moduleName = null;
+        cFlags = null;
+        ldLibs = null;
+        abiFilters = null;
+    }
+
+    @Override
+    @Nullable
+    public String getModuleName() {
+        return moduleName;
+    }
+
+    @Override
+    @Nullable
+    public String getcFlags() {
+        return cFlags;
+    }
+
+    @Override
+    @Nullable
+    public Set<String> getLdLibs() {
+        return ldLibs;
+    }
+
+    @Override
+    @Nullable
+    public Set<String> getAbiFilters() {
+        return abiFilters;
+    }
+
+    @Override
+    @Nullable
+    public String getStl() {
+        return stl;
+    }
+
+    public void append(@NonNull NdkConfig ndkConfig) {
+        // override
+        if (ndkConfig.getModuleName() != null) {
+            moduleName = ndkConfig.getModuleName();
+        }
+
+        if (ndkConfig.getStl() != null) {
+            stl = ndkConfig.getStl();
+        }
+
+        // append
+        if (ndkConfig.getAbiFilters() != null) {
+            if (abiFilters == null) {
+                abiFilters = Sets.newHashSetWithExpectedSize(ndkConfig.getAbiFilters().size());
+            } else {
+                abiFilters.clear();
+            }
+            abiFilters.addAll(ndkConfig.getAbiFilters());
+        }
+
+        if (cFlags == null) {
+            cFlags = ndkConfig.getcFlags();
+        } else if (ndkConfig.getcFlags() != null) {
+            cFlags = cFlags + " " + ndkConfig.getcFlags();
+        }
+
+        if (ndkConfig.getLdLibs() != null) {
+            if (ldLibs == null) {
+                ldLibs = Sets.newHashSetWithExpectedSize(ndkConfig.getLdLibs().size());
+            } else {
+                ldLibs.clear();
+            }
+            ldLibs.addAll(ndkConfig.getLdLibs());
+        }
+    }
+}
diff --git a/builder/src/main/java/com/android/builder/internal/StringHelper.java b/builder/src/main/java/com/android/builder/internal/StringHelper.java
new file mode 100644
index 0000000..4a59a82
--- /dev/null
+++ b/builder/src/main/java/com/android/builder/internal/StringHelper.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2013 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.builder.internal;
+
+import com.android.annotations.NonNull;
+
+import java.util.Locale;
+
+/**
+ */
+public class StringHelper {
+
+    @NonNull
+    public static String capitalize(@NonNull String string) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(string.substring(0, 1).toUpperCase(Locale.US)).append(string.substring(1));
+
+        return sb.toString();
+    }
+}
diff --git a/builder/src/main/java/com/android/builder/internal/TestManifestGenerator.java b/builder/src/main/java/com/android/builder/internal/TestManifestGenerator.java
index 69fa7b5..9e443a7 100644
--- a/builder/src/main/java/com/android/builder/internal/TestManifestGenerator.java
+++ b/builder/src/main/java/com/android/builder/internal/TestManifestGenerator.java
@@ -33,6 +33,8 @@
     private final static String PH_TARGET_SDK_VERSION = "#TARGETSDKVERSION#";
     private final static String PH_TESTED_PACKAGE = "#TESTEDPACKAGE#";
     private final static String PH_TEST_RUNNER = "#TESTRUNNER#";
+    private final static String PH_HANDLE_PROFILING = "#HANDLEPROFILING#";
+    private final static String PH_FUNCTIONAL_TEST = "#FUNCTIONALTEST#";
 
     private final String mOutputFile;
     private final String mPackageName;
@@ -40,19 +42,25 @@
     private final int mTargetSdkVersion;
     private final String mTestedPackageName;
     private final String mTestRunnerName;
+    private final boolean mHandleProfiling;
+    private final boolean mFunctionalTest;
 
     public TestManifestGenerator(@NonNull String outputFile,
                           @NonNull String packageName,
                           int minSdkVersion,
                           int targetSdkVersion,
                           @NonNull String testedPackageName,
-                          @NonNull String testRunnerName) {
+                          @NonNull String testRunnerName,
+                          @NonNull Boolean handleProfiling,
+                          @NonNull Boolean functionalTest) {
         mOutputFile = outputFile;
         mPackageName = packageName;
         mMinSdkVersion = minSdkVersion;
         mTargetSdkVersion = targetSdkVersion != -1 ? targetSdkVersion : minSdkVersion;
         mTestedPackageName = testedPackageName;
         mTestRunnerName = testRunnerName;
+        mHandleProfiling = handleProfiling;
+        mFunctionalTest = functionalTest;
     }
 
     public void generate() throws IOException {
@@ -62,6 +70,8 @@
         map.put(PH_TARGET_SDK_VERSION, Integer.toString(mTargetSdkVersion));
         map.put(PH_TESTED_PACKAGE, mTestedPackageName);
         map.put(PH_TEST_RUNNER, mTestRunnerName);
+        map.put(PH_HANDLE_PROFILING, Boolean.toString(mHandleProfiling));
+        map.put(PH_FUNCTIONAL_TEST, Boolean.toString(mFunctionalTest));
 
         TemplateProcessor processor = new TemplateProcessor(
                 TestManifestGenerator.class.getResourceAsStream(TEMPLATE),
diff --git a/builder/src/main/java/com/android/builder/internal/compiler/RenderScriptProcessor.java b/builder/src/main/java/com/android/builder/internal/compiler/RenderScriptProcessor.java
new file mode 100644
index 0000000..9b28743
--- /dev/null
+++ b/builder/src/main/java/com/android/builder/internal/compiler/RenderScriptProcessor.java
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2013 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.builder.internal.compiler;
+
+import com.android.SdkConstants;
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.common.internal.CommandLineRunner;
+import com.android.ide.common.internal.LoggedErrorException;
+import com.android.ide.common.internal.WaitableExecutor;
+import com.android.sdklib.BuildToolInfo;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
+import static com.android.SdkConstants.EXT_BC;
+import static com.android.SdkConstants.FN_RENDERSCRIPT_V8_JAR;
+
+/**
+ * Compiles Renderscript files.
+ */
+public class RenderScriptProcessor {
+
+    // ABI list, as pairs of (android-ABI, toolchain-ABI)
+    private static final class Abi {
+
+        @NonNull
+        private final String mDevice;
+        @NonNull
+        private final String mToolchain;
+        @NonNull
+        private final BuildToolInfo.PathId mLinker;
+        @NonNull
+        private final String[] mLinkerArgs;
+
+        Abi(@NonNull String device,
+            @NonNull String toolchain,
+            @NonNull BuildToolInfo.PathId linker,
+            @NonNull String... linkerArgs) {
+
+            mDevice = device;
+            mToolchain = toolchain;
+            mLinker = linker;
+            mLinkerArgs = linkerArgs;
+        }
+    }
+
+    private static final Abi[] ABIS = {
+            new Abi("armeabi-v7a", "armv7-none-linux-gnueabi", BuildToolInfo.PathId.LD_ARM,
+                    "-dynamic-linker", "/system/bin/linker", "-X", "-m", "armelf_linux_eabi"),
+            new Abi("mips", "mipsel-unknown-linux", BuildToolInfo.PathId.LD_MIPS, "-EL"),
+            new Abi("x86", "i686-unknown-linux", BuildToolInfo.PathId.LD_X86, "-m", "elf_i386") };
+
+    public static final String RS_DEPS = "rsDeps";
+
+    @NonNull
+    private final List<File> mSourceFolders;
+
+    @NonNull
+    private final List<File> mImportFolders;
+
+    @NonNull
+    private final File mSourceOutputDir;
+
+    @NonNull
+    private final File mResOutputDir;
+
+    @NonNull
+    private final File mObjOutputDir;
+
+    @NonNull
+    private final File mLibOutputDir;
+
+    @NonNull
+    private final BuildToolInfo mBuildToolInfo;
+
+    private final int mTargetApi;
+
+    private final boolean mDebugBuild;
+
+    private final int mOptimLevel;
+
+    private final boolean mNdkMode;
+
+    private final boolean mSupportMode;
+    private final Set<String> mAbiFilters;
+
+    private final File mRsLib;
+    private final File mLibClCore;
+
+    public RenderScriptProcessor(
+            @NonNull List<File> sourceFolders,
+            @NonNull List<File> importFolders,
+            @NonNull File sourceOutputDir,
+            @NonNull File resOutputDir,
+            @NonNull File objOutputDir,
+            @NonNull File libOutputDir,
+            @NonNull BuildToolInfo buildToolInfo,
+            int targetApi,
+            boolean debugBuild,
+            int optimLevel,
+            boolean ndkMode,
+            boolean supportMode,
+            @Nullable Set<String> abiFilters) {
+        mSourceFolders = sourceFolders;
+        mImportFolders = importFolders;
+        mSourceOutputDir = sourceOutputDir;
+        mResOutputDir = resOutputDir;
+        mObjOutputDir = objOutputDir;
+        mLibOutputDir = libOutputDir;
+        mBuildToolInfo = buildToolInfo;
+        mTargetApi = targetApi;
+        mDebugBuild = debugBuild;
+        mOptimLevel = optimLevel;
+        mNdkMode = ndkMode;
+        mSupportMode = supportMode;
+        mAbiFilters = abiFilters;
+
+        if (supportMode) {
+            File rs = new File(mBuildToolInfo.getLocation(), "renderscript");
+            mRsLib = new File(rs, "lib");
+            mLibClCore = new File(mRsLib, "libclcore.bc");
+        } else {
+            mLibClCore = null;
+            mRsLib = null;
+        }
+    }
+
+    public static File getSupportJar(String buildToolsFolder) {
+        return new File(buildToolsFolder, "renderscript/lib/" + FN_RENDERSCRIPT_V8_JAR);
+    }
+
+    public static File getSupportNativeLibFolder(String buildToolsFolder) {
+        File rs = new File(buildToolsFolder, "renderscript");
+        File lib = new File(rs, "lib");
+        return new File(lib, "packaged");
+    }
+
+    public void build(@NonNull CommandLineRunner launcher)
+            throws IOException, InterruptedException, LoggedErrorException {
+
+        // gather the files to compile
+        FileGatherer fileGatherer = new FileGatherer();
+        SourceSearcher searcher = new SourceSearcher(mSourceFolders, "rs", "fs");
+        searcher.setUseExecutor(false);
+        searcher.search(fileGatherer);
+
+        List<File> renderscriptFiles = fileGatherer.getFiles();
+
+        if (renderscriptFiles.isEmpty()) {
+            return;
+        }
+
+        // get the env var
+        Map<String, String> env = Maps.newHashMap();
+        if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_DARWIN) {
+            env.put("DYLD_LIBRARY_PATH", mBuildToolInfo.getLocation().getAbsolutePath());
+        } else if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_LINUX) {
+            env.put("LD_LIBRARY_PATH", mBuildToolInfo.getLocation().getAbsolutePath());
+        }
+
+        doMainCompilation(renderscriptFiles, launcher, env);
+
+        if (mSupportMode) {
+            createSupportFiles(launcher, env);
+        }
+    }
+
+    private void doMainCompilation(
+            @NonNull List<File> inputFiles,
+            @NonNull CommandLineRunner launcher,
+            @NonNull Map<String, String> env)
+            throws IOException, InterruptedException, LoggedErrorException {
+
+        String renderscript = mBuildToolInfo.getPath(BuildToolInfo.PathId.LLVM_RS_CC);
+        if (renderscript == null || !new File(renderscript).isFile()) {
+            throw new IllegalStateException(BuildToolInfo.PathId.LLVM_RS_CC + " is missing");
+        }
+
+        String rsPath = mBuildToolInfo.getPath(BuildToolInfo.PathId.ANDROID_RS);
+        String rsClangPath = mBuildToolInfo.getPath(BuildToolInfo.PathId.ANDROID_RS_CLANG);
+
+        // the renderscript compiler doesn't expect the top res folder,
+        // but the raw folder directly.
+        File rawFolder = new File(mResOutputDir, SdkConstants.FD_RES_RAW);
+
+        // compile all the files in a single pass
+        ArrayList<String> command = Lists.newArrayListWithExpectedSize(26);
+
+        command.add(renderscript);
+
+        // Due to a device side bug, let's not enable this at this time.
+//        if (mDebugBuild) {
+//            command.add("-g");
+//        }
+
+        command.add("-O");
+        command.add(Integer.toString(mOptimLevel));
+
+        // add all import paths
+        command.add("-I");
+        command.add(rsPath);
+        command.add("-I");
+        command.add(rsClangPath);
+
+        for (File importPath : mImportFolders) {
+            if (importPath.isDirectory()) {
+                command.add("-I");
+                command.add(importPath.getAbsolutePath());
+            }
+        }
+
+        if (mSupportMode) {
+            command.add("-rs-package-name=android.support.v8.renderscript");
+        }
+
+        // source output
+        command.add("-p");
+        command.add(mSourceOutputDir.getAbsolutePath());
+
+        if (mNdkMode) {
+            command.add("-reflect-c++");
+        }
+
+        // res output
+        command.add("-o");
+        command.add(rawFolder.getAbsolutePath());
+
+        command.add("-target-api");
+        int targetApi = mTargetApi < 11 ? 11 : mTargetApi;
+        targetApi = (mSupportMode && targetApi < 18) ? 18 : targetApi;
+        command.add(Integer.toString(targetApi));
+
+        // input files
+        for (File sourceFile : inputFiles) {
+            command.add(sourceFile.getAbsolutePath());
+        }
+
+        launcher.runCmdLine(command, env);
+    }
+
+    private void createSupportFiles(@NonNull final CommandLineRunner launcher,
+            @NonNull final Map<String, String> env)
+            throws IOException, InterruptedException, LoggedErrorException {
+        // get the generated BC files.
+        File rawFolder = new File(mResOutputDir, SdkConstants.FD_RES_RAW);
+
+        SourceSearcher searcher = new SourceSearcher(
+                Collections.singletonList(rawFolder), EXT_BC);
+        FileGatherer fileGatherer = new FileGatherer();
+        searcher.search(fileGatherer);
+
+        WaitableExecutor<Void> mExecutor  = new WaitableExecutor<Void>();
+
+        for (final File bcFile : fileGatherer.getFiles()) {
+            String name = bcFile.getName();
+            final String objName = name.replaceAll("\\.bc", ".o");
+            final String soName = "librs." + name.replaceAll("\\.bc", ".so");
+
+            for (final Abi abi : ABIS) {
+                if (mAbiFilters != null && !mAbiFilters.contains(abi.mDevice)) {
+                    continue;
+                }
+
+                // make sure the dest folders exist
+                final File objAbiFolder = new File(mObjOutputDir, abi.mDevice);
+                if (!objAbiFolder.isDirectory() && !objAbiFolder.mkdirs()) {
+                    throw new IOException("Unable to create dir " + objAbiFolder.getAbsolutePath());
+                }
+
+                final File libAbiFolder = new File(mLibOutputDir, abi.mDevice);
+                if (!libAbiFolder.isDirectory() && !libAbiFolder.mkdirs()) {
+                    throw new IOException("Unable to create dir " + libAbiFolder.getAbsolutePath());
+                }
+
+                mExecutor.execute(new Callable<Void>() {
+                    @Override
+                    public Void call() throws Exception {
+                        File objFile = createSupportObjFile(bcFile, abi, objName, objAbiFolder,
+                                launcher, env);
+                        createSupportLibFile(objFile, abi, soName, libAbiFolder, launcher, env);
+                        return null;
+                    }
+                });
+            }
+        }
+
+        mExecutor.waitForTasksWithQuickFail(true /*cancelRemaining*/);
+    }
+
+    private File createSupportObjFile(
+            @NonNull File bcFile,
+            @NonNull Abi abi,
+            @NonNull String objName,
+            @NonNull File objAbiFolder,
+            @NonNull CommandLineRunner launcher,
+            @NonNull Map<String, String> env)
+            throws IOException, InterruptedException, LoggedErrorException {
+
+        List<String> args = Lists.newArrayListWithExpectedSize(10);
+
+        args.add(mBuildToolInfo.getPath(BuildToolInfo.PathId.BCC_COMPAT));
+
+        args.add("-O" + Integer.toString(mOptimLevel));
+
+        File outFile = new File(objAbiFolder, objName);
+        args.add("-o");
+        args.add(outFile.getAbsolutePath());
+
+        args.add("-fPIC");
+        args.add("-shared");
+
+        args.add("-rt-path");
+        args.add(mLibClCore.getAbsolutePath());
+
+        args.add("-mtriple");
+        args.add(abi.mToolchain);
+
+        args.add(bcFile.getAbsolutePath());
+
+        launcher.runCmdLine(args, env);
+
+        return outFile;
+    }
+
+    private void createSupportLibFile(
+            @NonNull File objFile,
+            @NonNull Abi abi,
+            @NonNull String soName,
+            @NonNull File libAbiFolder,
+            @NonNull CommandLineRunner launcher,
+            @NonNull Map<String, String> env)
+            throws IOException, InterruptedException, LoggedErrorException {
+
+        File intermediatesFolder = new File(mRsLib, "intermediates");
+        File intermediatesAbiFolder = new File(intermediatesFolder, abi.mDevice);
+        File packagedFolder = new File(mRsLib, "packaged");
+        File packagedAbiFolder = new File(packagedFolder, abi.mDevice);
+
+        List<String> args = Lists.newArrayListWithExpectedSize(26);
+
+        args.add(mBuildToolInfo.getPath(abi.mLinker));
+
+        args.add("--eh-frame-hdr");
+        Collections.addAll(args, abi.mLinkerArgs);
+        args.add("-shared");
+        args.add("-Bsymbolic");
+        args.add("-z");
+        args.add("noexecstack");
+        args.add("-z");
+        args.add("relro");
+        args.add("-z");
+        args.add("now");
+
+        File outFile = new File(libAbiFolder, soName);
+        args.add("-o");
+        args.add(outFile.getAbsolutePath());
+
+        args.add("-L" + intermediatesAbiFolder.getAbsolutePath());
+        args.add("-L" + packagedAbiFolder.getAbsolutePath());
+
+        args.add("-soname");
+        args.add(soName);
+
+        args.add(objFile.getAbsolutePath());
+        args.add(new File(intermediatesAbiFolder, "libcompiler_rt.a").getAbsolutePath());
+
+        args.add("-lRSSupport");
+        args.add("-lm");
+        args.add("-lc");
+
+        launcher.runCmdLine(args, env);
+    }
+}
diff --git a/builder/src/main/java/com/android/builder/internal/incremental/ChangeManager.java b/builder/src/main/java/com/android/builder/internal/incremental/ChangeManager.java
deleted file mode 100644
index 62395fe..0000000
--- a/builder/src/main/java/com/android/builder/internal/incremental/ChangeManager.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * 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.builder.internal.incremental;
-
-import com.android.annotations.NonNull;
-import com.android.ide.common.res2.FileStatus;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Collection;
-import java.util.Map;
-
-/**
- * Class handling changes in input and output.
- *
- * The class stores the state of inputs/outputs after a task is run, and on subsequent runs can
- * compare this to the current state of detect exact file changes in the inputs or outputs.
- *
- * Gradle already does this to figure out if a task needs to be run, but does not offer this
- * information to the task.
- * This should become available in the Gradle plugin in the future, but in the meantime this
- * provides the same information, allowing us to build truly incremental tasks.
- *
- */
-public class ChangeManager {
-
-    private static final String FN_INPUTS_DATA = "inputs.data";
-    private static final String FN_OUTPUTS_DATA = "outputs.data";
-
-    private FileManager mInputs = new FileManager();
-    private FileManager mOutputs = new FileManager();
-
-    public ChangeManager() {
-    }
-
-    /**
-     * Loads the known state.
-     *
-     * @param incrementalFolder the folder in which to store the incremental data
-     * @return false if the loading failed.
-     */
-    public boolean load(File incrementalFolder) {
-        File inputs = new File(incrementalFolder, FN_INPUTS_DATA);
-        File outputs = new File(incrementalFolder, FN_OUTPUTS_DATA);
-        return inputs.exists() && outputs.exists() &&
-                mInputs.load(inputs) && mOutputs.load(outputs);
-    }
-
-    /**
-     * Writes the incremental data to a given folder.
-     * @param incrementalFolder the name of the folder to write to.
-     *
-     * @throws IOException
-     */
-    public void write(File incrementalFolder) throws IOException {
-        if (!incrementalFolder.isDirectory() && !incrementalFolder.mkdirs()) {
-            throw new IOException("Failed to create directory " + incrementalFolder);
-        }
-
-        mInputs.write(new File(incrementalFolder, FN_INPUTS_DATA));
-        mOutputs.write(new File(incrementalFolder, FN_OUTPUTS_DATA));
-    }
-
-    /**
-     * Delete the incremental data from the given folder.
-     * @param incrementalFolder the folder to delete the incremental data from.
-     */
-    public static void delete(File incrementalFolder) {
-        File file = new File(incrementalFolder, FN_INPUTS_DATA);
-        //noinspection ResultOfMethodCallIgnored
-        file.delete();
-        file = new File(incrementalFolder, FN_OUTPUTS_DATA);
-        //noinspection ResultOfMethodCallIgnored
-        file.delete();
-    }
-
-    /**
-     * Add an input file or folder.
-     * @param file the file.
-     */
-    public void addInput(File file) {
-        mInputs.addFile(file);
-    }
-
-    /**
-     * Adds a new output file or folder
-     * @param file the file.
-     */
-    public void addOutput(File file) {
-        mOutputs.addFile(file);
-    }
-
-    /**
-     * Get the list of changed inputs. Empty list means no input changes.
-     *
-     * @return a map of (File, FileStatus) for all changed input.
-     */
-    @NonNull
-    public Map<File, FileStatus> getChangedInputs() {
-        return mInputs.getChangedFiles();
-    }
-
-    /**
-     * Returns a list of changed output. Empty list means no output changes.
-     *
-     * @return a map of (file, status) for all changed output files.
-     */
-    @NonNull
-    public Map<File, FileStatus> getChangedOutputs() {
-        return mOutputs.getChangedFiles();
-    }
-
-    /**
-     * Update the outputs before writing the file states
-     */
-    public void updateOutputs(Collection<File> outputs) {
-        mOutputs.update(outputs);
-    }
-}
diff --git a/builder/src/main/java/com/android/builder/internal/incremental/DependencyData.java b/builder/src/main/java/com/android/builder/internal/incremental/DependencyData.java
index 22035fd..575e50c 100644
--- a/builder/src/main/java/com/android/builder/internal/incremental/DependencyData.java
+++ b/builder/src/main/java/com/android/builder/internal/incremental/DependencyData.java
@@ -17,6 +17,7 @@
 package com.android.builder.internal.incremental;
 
 import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
 import com.android.annotations.VisibleForTesting;
 import com.google.common.base.Charsets;
 import com.google.common.collect.Lists;
@@ -74,6 +75,7 @@
      *
      * @param dependencyFile the dependency file
      */
+    @Nullable
     public static DependencyData parseDependencyFile(@NonNull File dependencyFile)
             throws IOException {
         // first check if the dependency file is here.
@@ -91,6 +93,7 @@
     }
 
     @VisibleForTesting
+    @Nullable
     static DependencyData processDependencyData(@NonNull List<String> content) {
         // The format is technically:
         // output1 output2 [...]: dep1 dep2 [...]
@@ -142,6 +145,10 @@
             parseMode = nextMode;
         }
 
+        if (data.getMainFile() == null) {
+            return null;
+        }
+
         return data;
     }
 
diff --git a/builder/src/main/java/com/android/builder/internal/incremental/FileEntity.java b/builder/src/main/java/com/android/builder/internal/incremental/FileEntity.java
deleted file mode 100644
index a981d8e..0000000
--- a/builder/src/main/java/com/android/builder/internal/incremental/FileEntity.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * 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.builder.internal.incremental;
-
-import com.google.common.hash.HashCode;
-import com.google.common.hash.Hashing;
-import com.google.common.io.ByteStreams;
-import com.google.common.io.Files;
-
-import java.io.File;
-
-/**
- * A {@link File} and its associated data needed to figure out if a file changed or not.
- */
-class FileEntity {
-
-    private static final byte[] sBuffer = new byte[4096];
-
-    private final File mFile;
-    private final long mLastModified;
-    private long mLength;
-    private String mSha1;
-
-    /**
-     * Exception to indicate a failure to check a jar file's content.
-     */
-    private static final class Sha1Exception extends Exception {
-        private static final long serialVersionUID = 1L;
-        private final File file;
-
-        public Sha1Exception(File jarFile, Throwable cause) {
-            super(cause);
-            file = jarFile;
-        }
-
-        public File getJarFile() {
-            return file;
-        }
-    }
-
-    /**
-     * Creates an entity from cached data.
-     *
-     * @param file the file
-     * @param lastModified when it was last modified
-     * @param length its length
-     * @param sha1 its sha1
-     */
-    FileEntity(File file, long lastModified, long length, String sha1) {
-        mFile = file;
-        mLastModified = lastModified;
-        mLength = length;
-        mSha1 = sha1;
-    }
-
-    /**
-     * Creates an entity from a {@link File}.
-     *
-     * The sha1 is not computed yet, it'll be done on demand when {@link #getSha1()} is called.
-     *
-     * @param file the file.
-     */
-    FileEntity(File file) {
-        mFile = file;
-        mLastModified = file.lastModified();
-        mLength = file.length();
-    }
-
-    /**
-     * Returns the file's last modified info.
-     * @return the file's last modified info.
-     */
-    long getLastModified() {
-        return mLastModified;
-    }
-
-    /**
-     * Return the file length.
-     * @return the file length.
-     */
-    long getLength() {
-        return mLength;
-    }
-
-    /**
-     * Returns the file this entity represents.
-     * @return the file.
-     */
-    File getFile() {
-        return mFile;
-    }
-
-    /**
-     * Returns the file's sha1, computing it if necessary.
-     *
-     * @return the fha1 or null if it couldn't be computed.
-     */
-    String getSha1() {
-        try {
-            return computeAndReturnSha1();
-        } catch (Sha1Exception e) {
-            return null;
-        }
-    }
-
-    /**
-     * Checks whether the {@link File#lastModified()} matches the cached value. If not, length
-     * is updated and the sha1 is reset (but not recomputed, this is done on demand).
-     *
-     * @return return whether the file was changed.
-     */
-    private boolean checkValidity() {
-        if (mLastModified != mFile.lastModified()) {
-            mLength = mFile.length();
-            mSha1 = null;
-            return true;
-        }
-
-        return false;
-    }
-
-    /**
-     * Returns whether the two entity are different files.
-     *
-     * This will compute the files' sha1 if they are not yet computed.
-     *
-     * @param fileEntity the file to compare to.
-     * @return true if the files are the same, false otherwise.
-     */
-    public boolean isDifferentThan(FileEntity fileEntity) {
-        assert fileEntity.mFile.equals(mFile);
-
-        // same date, same files.
-        if (mLastModified == fileEntity.mLastModified) {
-            return false;
-        }
-
-        try {
-            // different date doesn't necessarily mean different file.
-            // start with size, less computing intensive than sha1.
-            return mLength != fileEntity.mLength ||
-                    !computeAndReturnSha1().equals(fileEntity.computeAndReturnSha1());
-        } catch (Sha1Exception e) {
-            // if we can't compute the sha1, we consider the files different.
-            return true;
-        }
-    }
-
-    /**
-     * Returns the file's sha1, computing it if necessary.
-     *
-     * @return the sha1
-     * @throws Sha1Exception
-     */
-    private String computeAndReturnSha1() throws Sha1Exception {
-        if (mSha1 == null) {
-            mSha1 = getSha1(mFile);
-        }
-        return mSha1;
-    }
-
-    /**
-     * Computes the sha1 of a file and returns it.
-     *
-     * @param f the file to compute the sha1 for.
-     * @return the sha1 value
-     * @throws Sha1Exception if the sha1 value cannot be computed.
-     */
-    static String getSha1(File f) throws Sha1Exception {
-        synchronized (sBuffer) {
-
-            try {
-                HashCode value = ByteStreams.hash(Files.newInputStreamSupplier(f), Hashing.sha1());
-                return value.toString();
-            } catch (Exception e) {
-                throw new Sha1Exception(f, e);
-            }
-        }
-    }
-
-    @Override
-    public String toString() {
-        return "FileEntity{" +
-                "mFile=" + mFile +
-                ", mLastModified=" + mLastModified +
-                ", mLength=" + mLength +
-                ", mSha1='" + mSha1 + '\'' +
-                '}';
-    }
-}
diff --git a/builder/src/main/java/com/android/builder/internal/incremental/FileManager.java b/builder/src/main/java/com/android/builder/internal/incremental/FileManager.java
deleted file mode 100644
index 6408c26..0000000
--- a/builder/src/main/java/com/android/builder/internal/incremental/FileManager.java
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * 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.builder.internal.incremental;
-
-import com.android.annotations.NonNull;
-import com.android.ide.common.res2.FileStatus;
-import com.google.common.base.Charsets;
-import com.google.common.collect.Maps;
-import com.google.common.io.Closeables;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.io.UnsupportedEncodingException;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Class handling changes in a set of files.
- *
- * The class can store the state of the files, and later reload it and compare it to the
- * previous known state.
- *
- */
-class FileManager {
-
-    private static final Pattern READ_PATTERN = Pattern.compile(
-            "^(\\d+)\\s+(\\d+)\\s+([0-9a-f]+)\\s+(.+)$");
-
-    private Map<File, FileEntity> mLoadedFiles = Maps.newHashMap();
-    private Map<File, FileEntity> mProcessedFiles = Maps.newHashMap();
-    private Map<File, FileStatus> mResults = Maps.newHashMap();
-    private Map<File, FileStatus> mReturnedMap = null;
-
-    public FileManager() {
-    }
-
-    /**
-     * Loads the known state.
-     *
-     * @param stateFile the file to load the state from.
-     * @return false if the loading failed.
-     *
-     * @see #write(java.io.File)
-     */
-    public boolean load(File stateFile) {
-        if (!stateFile.exists()) {
-            return false;
-        }
-
-        BufferedReader reader = null;
-        try {
-            reader = new BufferedReader(new InputStreamReader(
-                    new FileInputStream(stateFile), Charsets.UTF_8));
-
-            String line = null;
-            while ((line = reader.readLine()) != null) {
-                // skip comments
-                if (line.charAt(0) == '#') {
-                    continue;
-                }
-
-                // get the data with a regexp
-                Matcher m = READ_PATTERN.matcher(line);
-                if (m.matches()) {
-                    String path = m.group(4);
-                    File f = new File(path);
-
-                    FileEntity entity = new FileEntity(
-                            f,
-                            Long.parseLong(m.group(1)),
-                            Long.parseLong(m.group(2)),
-                            m.group(3));
-
-                    mLoadedFiles.put(f, entity);
-                }
-            }
-
-            return true;
-        } catch (FileNotFoundException ignored) {
-            // won't happen, we check up front.
-        } catch (UnsupportedEncodingException ignored) {
-            // shouldn't happen, but if it does, we just won't have a cache.
-        } catch (IOException ignored) {
-            // shouldn't happen, but if it does, we just won't have a cache.
-        } finally {
-            Closeables.closeQuietly(reader);
-        }
-
-        return false;
-    }
-
-    /**
-     * Writes the state to a file
-     * @param stateFile the file to write the state to.
-     *
-     * @throws IOException
-     *
-     * @see #load(java.io.File)
-     */
-    public void write(File stateFile) throws IOException {
-        OutputStreamWriter writer = null;
-        try {
-            // first make sure the folders exist!
-            File parentFolder = stateFile.getParentFile();
-            if (!parentFolder.isDirectory() && !parentFolder.mkdirs()) {
-                throw new IOException("Failed to create directory " + parentFolder);
-            }
-
-            // then write the file.
-            writer = new OutputStreamWriter(new FileOutputStream(stateFile), Charsets.UTF_8);
-
-            writer.write("# incremental data. DO NOT EDIT.\n");
-            writer.write("# format is <lastModified> <length> <SHA-1> <path>\n");
-            writer.write("# Encoding is UTF-8\n");
-
-            for (FileEntity entity : mProcessedFiles.values()) {
-                String sha1 = entity.getSha1();
-                if (sha1 == null) {
-                    sha1 = "0123456789012345678901234567890123456789"; // TODO: find a better way to detect missing sha1
-                }
-
-                writer.write(String.format("%d %d %s %s\n",
-                        entity.getLastModified(),
-                        entity.getLength(),
-                        sha1,
-                        entity.getFile().getAbsolutePath()));
-            }
-        } finally {
-            Closeables.closeQuietly(writer);
-        }
-    }
-
-   /**
-     * Add an input file or folder.
-     * @param file the file.
-     */
-    public void addFile(File file) {
-        processFile(file);
-    }
-
-    /**
-     * Get the list of changed inputs. Empty list means no input changes.
-     *
-     * @return a map of (File, FileStatus) for all changed input.
-     */
-    @NonNull
-    public Map<File, FileStatus> getChangedFiles() {
-        if (mReturnedMap == null) {
-            // create a map with the content of the result map.
-            mReturnedMap = Maps.newHashMap(mResults);
-
-            // at this point, all the files that needed processing have been processed,
-            // but there may be removed files remaining in the loaded file map.
-            for (File f : mLoadedFiles.keySet()) {
-                mReturnedMap.put(f, FileStatus.REMOVED);
-            }
-
-            // wrap this
-            mReturnedMap = Collections.unmodifiableMap(mReturnedMap);
-        }
-
-        return mReturnedMap;
-    }
-
-    private void processFile(File file) {
-        if (file.isFile()) {
-            if (file.getName().startsWith(".")) {
-                return;
-            }
-
-            // get the FileEntity for the new(?) version.
-            FileEntity newFileEntity = new FileEntity(file);
-
-            // see if it existed before.
-            FileEntity fileEntity = mLoadedFiles.get(file);
-
-            if (fileEntity == null) {
-                // new file!
-                mResults.put(file, FileStatus.NEW);
-
-                // add it to the list of processed files
-                mProcessedFiles.put(file, newFileEntity);
-            } else {
-                // remove it from the loaded files.
-                mLoadedFiles.remove(file);
-
-                if (newFileEntity.isDifferentThan(fileEntity)) {
-                    mResults.put(file, FileStatus.CHANGED);
-
-                    // put the newFileEntity in the processed files.
-                    mProcessedFiles.put(file, newFileEntity);
-                } else {
-                    // just move the original entity so avoid recomputing the sha1.
-                    // FileEntity.isDifferentThan doesn't necessarily compute it.
-                   mProcessedFiles.put(file, fileEntity);
-                }
-            }
-        } else if (file.isDirectory()) {
-            File[] files = file.listFiles();
-            if (files != null && files.length > 0) {
-                for (File f : files) {
-                    processFile(f);
-                }
-            }
-         }
-    }
-
-    /**
-     * Updates the existing files with the given files/folders.
-     * @param files the new folders/files to process.
-     */
-    void update(Collection<File> files) {
-        mLoadedFiles.clear();
-        mLoadedFiles.putAll(mProcessedFiles);
-        mResults.clear();
-        mProcessedFiles.clear();
-        for (File f : files) {
-            processFile(f);
-        }
-    }
-}
diff --git a/builder/src/main/java/com/android/builder/internal/packaging/Packager.java b/builder/src/main/java/com/android/builder/internal/packaging/Packager.java
index 445d607..8c370d0 100644
--- a/builder/src/main/java/com/android/builder/internal/packaging/Packager.java
+++ b/builder/src/main/java/com/android/builder/internal/packaging/Packager.java
@@ -39,6 +39,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Set;
 import java.util.jar.Attributes;
 import java.util.jar.Manifest;
 import java.util.regex.Pattern;
@@ -402,7 +403,7 @@
      *
      * This may or may not copy gdbserver into the apk based on whether the debug mode is set.
      *
-     * @param jniLibLocation the root folder containing the abi folders which contain the .so
+     * @param nativeFolder the root folder containing the abi folders which contain the .so
      *
      * @throws PackagerException if an error occurred
      * @throws SealedPackageException if the APK is already sealed.
@@ -411,14 +412,12 @@
      *
      * @see #setJniDebugMode(boolean)
      */
-    public void addNativeLibraries(String jniLibLocation)
+    public void addNativeLibraries(@NonNull File nativeFolder, @Nullable Set<String> abiFilters)
             throws PackagerException, SealedPackageException, DuplicateFileException {
         if (mIsSealed) {
             throw new SealedPackageException("APK is already sealed");
         }
 
-        File nativeFolder = new File(jniLibLocation);
-
         if (!nativeFolder.isDirectory()) {
             // not a directory? check if it's a file or doesn't exist
             if (nativeFolder.exists()) {
@@ -434,6 +433,10 @@
 
         if (abiList != null) {
             for (File abi : abiList) {
+                if (abiFilters != null && !abiFilters.contains(abi.getName())) {
+                    continue;
+                }
+
                 if (abi.isDirectory()) { // ignore files
 
                     File[] libs = abi.listFiles();
@@ -441,14 +444,16 @@
                         for (File lib : libs) {
                             // only consider files that are .so or, if in debug mode, that
                             // are gdbserver executables
+                            String libName = lib.getName();
                             if (lib.isFile() &&
                                     (PATTERN_NATIVELIB_EXT.matcher(lib.getName()).matches() ||
-                                            (mJniDebugMode &&
-                                                    SdkConstants.FN_GDBSERVER.equals(
-                                                            lib.getName())))) {
+                                        (mJniDebugMode &&
+                                            (SdkConstants.FN_GDBSERVER.equals(libName) ||
+                                             SdkConstants.FN_GDB_SETUP.equals(libName))))) {
+
                                 String path =
                                     SdkConstants.FD_APK_NATIVE_LIBS + "/" +
-                                    abi.getName() + "/" + lib.getName();
+                                    abi.getName() + "/" + libName;
 
                                 try {
                                     doAddFile(lib, path);
diff --git a/builder/src/main/java/com/android/builder/testing/ConnectedDevice.java b/builder/src/main/java/com/android/builder/testing/ConnectedDevice.java
index 8a1aa64..7e6ae77 100644
--- a/builder/src/main/java/com/android/builder/testing/ConnectedDevice.java
+++ b/builder/src/main/java/com/android/builder/testing/ConnectedDevice.java
@@ -25,9 +25,11 @@
 import com.android.ddmlib.ShellCommandUnresponsiveException;
 import com.android.ddmlib.TimeoutException;
 import com.android.utils.ILogger;
+import com.google.common.collect.Lists;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -115,8 +117,19 @@
 
     @NonNull
     @Override
-    public String getAbi() {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    public List<String> getAbis() {
+        List<String> abis = Lists.newArrayListWithExpectedSize(2);
+        String abi = iDevice.getProperty(IDevice.PROP_DEVICE_CPU_ABI);
+        if (abi != null) {
+            abis.add(abi);
+        }
+
+        abi = iDevice.getProperty(IDevice.PROP_DEVICE_CPU_ABI2);
+        if (abi != null) {
+            abis.add(abi);
+        }
+
+        return abis;
     }
 
     @Override
diff --git a/builder/src/main/java/com/android/builder/testing/SimpleTestRunner.java b/builder/src/main/java/com/android/builder/testing/SimpleTestRunner.java
index 85114d4..237d871 100644
--- a/builder/src/main/java/com/android/builder/testing/SimpleTestRunner.java
+++ b/builder/src/main/java/com/android/builder/testing/SimpleTestRunner.java
@@ -26,6 +26,7 @@
 
 import java.io.File;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Basic {@link TestRunner} running tests on all devices.
@@ -47,21 +48,11 @@
 
         WaitableExecutor<Boolean> executor = new WaitableExecutor<Boolean>(maxThreads);
 
-        int minSdkVersion = testData.getMinSdkVersion();
         for (DeviceConnector device : deviceList) {
-            int deviceApiLevel = device.getApiLevel();
-            if (minSdkVersion <= deviceApiLevel) {
+            if (filterOutDevice(device, testData, logger, projectName, variantName)) {
                 executor.execute(new SimpleTestCallable(device, projectName, variantName,
                         testApk, testedApk, testData,
                         resultsDir, timeout, logger));
-            } else {
-                if (deviceApiLevel == 0) {
-                    logger.info("Skipping device '%s' for '%s:%s': Unknown API Level",
-                            device.getName(), projectName, variantName);
-                } else {
-                    logger.info("Skipping device '%s' for '%s:%s'",
-                            device.getName(), projectName, variantName);
-                }
             }
         }
 
@@ -82,4 +73,45 @@
 
         return success;
     }
+
+    private boolean filterOutDevice(@NonNull DeviceConnector device, @NonNull TestData testData,
+                                    @NonNull ILogger logger,
+                                    @NonNull String projectName, @NonNull String variantName) {
+        int deviceApiLevel = device.getApiLevel();
+        if (deviceApiLevel == 0) {
+            logger.info("Skipping device '%s' for '%s:%s': Unknown API Level",
+                    device.getName(), projectName, variantName);
+            return false;
+        }
+
+        if (testData.getMinSdkVersion() > deviceApiLevel) {
+            logger.info("Skipping device '%s' for '%s:%s'",
+                    device.getName(), projectName, variantName);
+
+            return false;
+        }
+
+        Set<String> appAbis = testData.getSupportedAbis();
+        if (appAbis != null) {
+            List<String> deviceAbis = device.getAbis();
+            if (deviceAbis == null || deviceAbis.isEmpty()) {
+                logger.info("Skipping device '%s' for '%s:%s': Unknown ABI",
+                        device.getName(), projectName, variantName);
+                return false;
+            }
+
+            boolean compatibleAbi = false;
+            for (String deviceAbi : deviceAbis) {
+                if (appAbis.contains(deviceAbi)) {
+                    compatibleAbi = true;
+                }
+            }
+
+            if (!compatibleAbi) {
+                return false;
+            }
+        }
+
+        return true;
+    }
 }
diff --git a/builder/src/main/java/com/android/builder/testing/TestData.java b/builder/src/main/java/com/android/builder/testing/TestData.java
index 2495659..a736026 100644
--- a/builder/src/main/java/com/android/builder/testing/TestData.java
+++ b/builder/src/main/java/com/android/builder/testing/TestData.java
@@ -44,8 +44,18 @@
     @NonNull
     String getInstrumentationRunner();
 
+    @NonNull
+    Boolean getHandleProfiling();
+
+    @NonNull
+    Boolean getFunctionalTest();
+
     int getMinSdkVersion();
 
+    /**
+     * List of supported ABIs. Null means all.
+     * @return a list of abi or null for all
+     */
     @Nullable
     Set<String> getSupportedAbis();
 }
diff --git a/builder/src/main/resources/com/android/builder/internal/AndroidManifest.template b/builder/src/main/resources/com/android/builder/internal/AndroidManifest.template
index 257e79d..ed50233 100644
--- a/builder/src/main/resources/com/android/builder/internal/AndroidManifest.template
+++ b/builder/src/main/resources/com/android/builder/internal/AndroidManifest.template
@@ -10,5 +10,7 @@
 
     <instrumentation android:name="#TESTRUNNER#"
                      android:targetPackage="#TESTEDPACKAGE#"
+                     android:handleProfiling="#HANDLEPROFILING#"
+                     android:functionalTest="#FUNCTIONALTEST#"
                      android:label="Tests for #TESTEDPACKAGE#"/>
 </manifest>
diff --git a/builder/src/main/resources/com/android/builder/internal/BuildConfig.template b/builder/src/main/resources/com/android/builder/internal/BuildConfig.template
deleted file mode 100644
index 618c013..0000000
--- a/builder/src/main/resources/com/android/builder/internal/BuildConfig.template
+++ /dev/null
@@ -1,7 +0,0 @@
-/** Automatically generated file. DO NOT MODIFY */
-package #PACKAGE#;
-
-public final class BuildConfig {
-    public final static boolean DEBUG = #DEBUG#;
-
-#ADDITIONAL_LINES#}
\ No newline at end of file
diff --git a/builder/src/test/java/com/android/builder/DefaultProductFlavorTest.java b/builder/src/test/java/com/android/builder/DefaultProductFlavorTest.java
index 1e09828..f618085 100644
--- a/builder/src/test/java/com/android/builder/DefaultProductFlavorTest.java
+++ b/builder/src/test/java/com/android/builder/DefaultProductFlavorTest.java
@@ -39,6 +39,8 @@
         mCustom.setPackageName("com.forty.two");
         mCustom.setTestPackageName("com.forty.two.test");
         mCustom.setTestInstrumentationRunner("com.forty.two.test.Runner");
+        mCustom.setTestHandleProfiling(true);
+        mCustom.setTestFunctionalTest(true);
     }
 
     public void testMergeOnDefault() {
@@ -52,6 +54,8 @@
         assertEquals("com.forty.two", flavor.getPackageName());
         assertEquals("com.forty.two.test", flavor.getTestPackageName());
         assertEquals("com.forty.two.test.Runner", flavor.getTestInstrumentationRunner());
+        assertEquals(Boolean.TRUE, flavor.getTestHandleProfiling());
+        assertEquals(Boolean.TRUE, flavor.getTestFunctionalTest());
     }
 
     public void testMergeOnCustom() {
@@ -65,6 +69,8 @@
         assertEquals("com.forty.two", flavor.getPackageName());
         assertEquals("com.forty.two.test", flavor.getTestPackageName());
         assertEquals("com.forty.two.test.Runner", flavor.getTestInstrumentationRunner());
+        assertEquals(Boolean.TRUE, flavor.getTestHandleProfiling());
+        assertEquals(Boolean.TRUE, flavor.getTestFunctionalTest());
     }
 
     public void testMergeDefaultOnDefault() {
@@ -78,5 +84,7 @@
         assertNull(flavor.getPackageName());
         assertNull(flavor.getTestPackageName());
         assertNull(flavor.getTestInstrumentationRunner());
+        assertNull(flavor.getTestHandleProfiling());
+        assertNull(flavor.getTestFunctionalTest());
     }
 }
diff --git a/builder/src/test/java/com/android/builder/VariantConfigurationTest.java b/builder/src/test/java/com/android/builder/VariantConfigurationTest.java
index 6d244ac..e1ffc3d 100644
--- a/builder/src/test/java/com/android/builder/VariantConfigurationTest.java
+++ b/builder/src/test/java/com/android/builder/VariantConfigurationTest.java
@@ -57,6 +57,11 @@
         public String getVersionName(@NonNull File manifestFile) {
             return "1.0";
         }
+
+        @Override
+        public int getVersionCode(@NonNull File manifestFile) {
+            return 1;
+        }
     }
 
     @Override
@@ -128,7 +133,7 @@
         VariantConfiguration variant = new VariantConfiguration(
                 mDefaultConfig, new MockSourceProvider("main"),
                 mBuildType, new MockSourceProvider("debug"),
-                VariantConfiguration.Type.DEFAULT, "test") {
+                VariantConfiguration.Type.DEFAULT) {
             // don't do validation.
             @Override
             protected void validate() {
@@ -136,7 +141,7 @@
             }
         };
 
-        variant.addProductFlavor(mFlavorConfig, new MockSourceProvider("custom"));
+        variant.addProductFlavor(mFlavorConfig, new MockSourceProvider("custom"), "");
 
         return variant;
     }
@@ -145,7 +150,7 @@
         VariantConfiguration variant = new VariantConfiguration(
                 mDefaultConfig, new MockSourceProvider("main"),
                 mBuildType, new MockSourceProvider("debug"),
-                VariantConfiguration.Type.DEFAULT, "test") {
+                VariantConfiguration.Type.DEFAULT) {
             @Override
             public String getPackageFromManifest() {
                 return packageName;
@@ -157,7 +162,7 @@
             }
         };
 
-        variant.addProductFlavor(mFlavorConfig, new MockSourceProvider("custom"));
+        variant.addProductFlavor(mFlavorConfig, new MockSourceProvider("custom"), "");
         return variant;
     }
 
@@ -165,7 +170,7 @@
         VariantConfiguration variant = new VariantConfiguration(
                 mDefaultConfig, new MockSourceProvider("main"),
                 mBuildType, new MockSourceProvider("debug"),
-                VariantConfiguration.Type.DEFAULT, "test") {
+                VariantConfiguration.Type.DEFAULT) {
             @Override
             public String getVersionNameFromManifest() {
                 return versionName;
@@ -177,7 +182,7 @@
             }
         };
 
-        variant.addProductFlavor(mFlavorConfig, new MockSourceProvider("custom"));
+        variant.addProductFlavor(mFlavorConfig, new MockSourceProvider("custom"), "");
         return variant;
     }
 }
diff --git a/builder/src/test/java/com/android/builder/compiling/BuildConfigGeneratorTest.java b/builder/src/test/java/com/android/builder/compiling/BuildConfigGeneratorTest.java
new file mode 100644
index 0000000..0bb2200
--- /dev/null
+++ b/builder/src/test/java/com/android/builder/compiling/BuildConfigGeneratorTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2013 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.builder.compiling;
+
+import com.android.builder.AndroidBuilder;
+import com.google.common.base.Charsets;
+import com.google.common.collect.Lists;
+import com.google.common.io.Files;
+import junit.framework.TestCase;
+
+import java.io.File;
+import java.util.List;
+
+@SuppressWarnings("ResultOfMethodCallIgnored")
+public class BuildConfigGeneratorTest extends TestCase {
+    public void testFalse() throws Exception {
+        File tempDir = Files.createTempDir();
+        BuildConfigGenerator generator = new BuildConfigGenerator(tempDir.getPath(),
+                "my.app.pkg");
+
+        generator.addField("boolean", "DEBUG", "false").generate();
+
+        File file = generator.getBuildConfigFile();
+        assertTrue(file.exists());
+        String actual = Files.toString(file, Charsets.UTF_8);
+        assertEquals(
+                "/**\n" +
+                " * Automatically generated file. DO NOT MODIFY\n" +
+                " */\n" +
+                "package my.app.pkg;\n" +
+                "\n" +
+                "public final class BuildConfig {\n" +
+                "  public static final boolean DEBUG = false;\n" +
+                "}\n", actual);
+        file.delete();
+        tempDir.delete();
+    }
+
+    public void testTrue() throws Exception {
+        File tempDir = Files.createTempDir();
+        BuildConfigGenerator generator = new BuildConfigGenerator(tempDir.getPath(),
+                "my.app.pkg");
+        generator.addField("boolean", "DEBUG", "Boolean.parseBoolean(\"true\")").generate();
+
+        File file = generator.getBuildConfigFile();
+        assertTrue(file.exists());
+        String actual = Files.toString(file, Charsets.UTF_8);
+        assertEquals(
+                "/**\n" +
+                " * Automatically generated file. DO NOT MODIFY\n" +
+                " */\n" +
+                "package my.app.pkg;\n" +
+                "\n" +
+                "public final class BuildConfig {\n" +
+                "  public static final boolean DEBUG = Boolean.parseBoolean(\"true\");\n" +
+                "}\n", actual);
+        file.delete();
+        tempDir.delete();
+    }
+
+    public void testExtra() throws Exception {
+        File tempDir = Files.createTempDir();
+        BuildConfigGenerator generator = new BuildConfigGenerator(tempDir.getPath(),
+                "my.app.pkg");
+
+        List<Object> items = Lists.newArrayList();
+        items.add("Extra line");
+        items.add(AndroidBuilder.createClassField("int", "EXTRA", "42"));
+
+        generator.addItems(items).generate();
+
+        File file = generator.getBuildConfigFile();
+        assertTrue(file.exists());
+        String actual = Files.toString(file, Charsets.UTF_8);
+        assertEquals(
+                "/**\n" +
+                " * Automatically generated file. DO NOT MODIFY\n" +
+                " */\n" +
+                "package my.app.pkg;\n" +
+                "\n" +
+                "public final class BuildConfig {\n" +
+                "  // Extra line\n" +
+                "  public static final int EXTRA = 42;\n" +
+                "}\n", actual);
+        file.delete();
+        tempDir.delete();
+    }
+}
diff --git a/builder/src/test/java/com/android/builder/internal/incremental/FileManagerTest.java b/builder/src/test/java/com/android/builder/internal/incremental/FileManagerTest.java
deleted file mode 100644
index 3ac6bb3..0000000
--- a/builder/src/test/java/com/android/builder/internal/incremental/FileManagerTest.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * 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.builder.internal.incremental;
-
-import com.android.ide.common.res2.FileStatus;
-import com.android.testutils.TestUtils;
-import com.google.common.base.Charsets;
-import com.google.common.collect.Maps;
-import com.google.common.io.Files;
-import junit.framework.TestCase;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Map;
-import java.util.regex.Matcher;
-
-public class FileManagerTest extends TestCase {
-
-    private static FileManager sFileManager = null;
-    private static File sFilesFolder = null;
-
-    public void testUntouched() throws Exception {
-        FileManager fileManager = getFileManager();
-        Map<File, FileStatus> changedFiles = fileManager.getChangedFiles();
-
-        File file = new File(sFilesFolder, "untouched.png");
-        FileStatus status = changedFiles.get(file);
-        assertNull(status);
-    }
-
-    public void testUntouchedDateBefore() throws Exception {
-        FileManager fileManager = getFileManager();
-        Map<File, FileStatus> changedFiles = fileManager.getChangedFiles();
-
-        File file = new File(sFilesFolder, "untouched_date_before.png");
-        FileStatus status = changedFiles.get(file);
-        // no change
-        assertNull(status);
-    }
-
-    public void testUntouchedDateAfter() throws Exception {
-        FileManager fileManager = getFileManager();
-        Map<File, FileStatus> changedFiles = fileManager.getChangedFiles();
-
-        File file = new File(sFilesFolder, "untouched_date_after.png");
-        FileStatus status = changedFiles.get(file);
-        // no change
-        assertNull(status);
-    }
-
-    public void testContentChanged() throws Exception {
-        FileManager fileManager = getFileManager();
-        Map<File, FileStatus> changedFiles = fileManager.getChangedFiles();
-
-        File file = new File(sFilesFolder, "content_changed.png");
-        FileStatus status = changedFiles.get(file);
-        assertEquals(FileStatus.CHANGED, status);
-    }
-
-    public void testSizeChanged() throws Exception {
-        FileManager fileManager = getFileManager();
-        Map<File, FileStatus> changedFiles = fileManager.getChangedFiles();
-
-        File file = new File(sFilesFolder, "size_changed.png");
-        FileStatus status = changedFiles.get(file);
-        assertEquals(FileStatus.CHANGED, status);
-    }
-
-    public void testRemoved() throws Exception {
-        FileManager fileManager = getFileManager();
-        Map<File, FileStatus> changedFiles = fileManager.getChangedFiles();
-
-        File file = new File(sFilesFolder, "removed.png");
-        FileStatus status = changedFiles.get(file);
-        assertEquals(FileStatus.REMOVED, status);
-    }
-
-    public void testNew() throws Exception {
-        FileManager fileManager = getFileManager();
-        Map<File, FileStatus> changedFiles = fileManager.getChangedFiles();
-
-        File file = new File(sFilesFolder, "new.png");
-        FileStatus status = changedFiles.get(file);
-        assertEquals(FileStatus.NEW, status);
-    }
-
-    private FileManager getFileManager() throws IOException {
-        if (sFileManager == null) {
-            File root = TestUtils.getCanonicalRoot("changeManager");
-            File dataFile = new File(root, "files.data");
-            sFilesFolder = new File(root, "files");
-
-            // update the last modified on some of the files.
-            Map<String, String> placeHolderMap = Maps.newHashMap();
-            String[] files = new String[] { "untouched" };
-            for (String filename : files) {
-                File file = new File(sFilesFolder, filename + ".png");
-                placeHolderMap.put(
-                        String.format("\\$lm_%s\\$", filename),
-                        String.format("%d", file.lastModified()));
-            }
-
-            File trueDataFile = getDataFile(dataFile, sFilesFolder, placeHolderMap);
-
-            sFileManager = new FileManager();
-            sFileManager.load(trueDataFile);
-
-            sFileManager.addFile(sFilesFolder);
-        }
-
-        return sFileManager;
-    }
-
-    /**
-     * Returns a data file where the placeholders have been updated with the real folder based
-     * on where the tests are run from.
-     *
-     * @param file the data folder.
-     * @param targetFolder the targetFolder
-     * @param placeholders additional placeholders and values to replace.
-     *
-     * @return a new data file that's been updated with the targetFolder
-     * @throws IOException
-     */
-    private static File getDataFile(File file, File targetFolder, Map<String, String> placeholders)
-            throws IOException {
-
-        String content = Files.toString(file, Charsets.UTF_8);
-
-        // search and replace $TOP$ with the root and $SEP$ with the platform separator.
-        content = content.replaceAll("\\$TOP\\$",  Matcher.quoteReplacement(targetFolder.getAbsolutePath()))
-                .replaceAll("\\$SEP\\$", Matcher.quoteReplacement(File.separator));
-
-        // now replace the additional placeholders
-        for (Map.Entry<String, String> entry : placeholders.entrySet()) {
-            content = content.replaceAll(entry.getKey(), Matcher.quoteReplacement(entry.getValue()));
-        }
-
-        File tmp = File.createTempFile("android", "getDataFile");
-        Files.write(content, tmp, Charsets.UTF_8);
-
-        return tmp;
-    }
-}
diff --git a/changelog.txt b/changelog.txt
index b9c8268..792cac0 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,6 +1,80 @@
+0.7.0
+- Requires Gradle 1.9
+- You can now have a variant specific source folder if you have flavors.
+  Only for app (not library or test). Name is src/flavorDebug/... or src/flavor1Flavor2Debug/
+  (note the camelcase naming, with lower case for first letter).
+  Its components (res, manifest, etc...) have higher priority than components from build type
+  or flavors.
+  There is also a "flavor combination" source folder available when more than one
+  flavor dimension is used.
+  For instance src/flavor1Flavor2/
+  Note that this is for all combinations of *all* dimensions.
+- Build config improvements and DSL changes.
+  The previous DSL proprety:
+    buildConfigLine "<value>"
+  has changed to
+    buildConfigField "<type>", "<name>", "<value>"
+  This allows override a field (see 'basic' sample)
+  Also, BuildConfig now automatically contains constants for
+  PACKAGE_NAME, VERSION_CODE, VERSION_NAME, BUILD_TYPE, FLAVOR as well as FLAVOR_<group>, FLAVOR_<group>, etc... if there are several flavor dimensions.
+- Switch to ProGuard 4.10
+   - Added ability to test proguarded (obfuscated) apps.
+- New option on product Flavor (and defaultConfig) allow filtering of resources through the -c option of aapt
+  You can pass single or multiple values through the DSL. All values from the default config and flavors
+  get combined and passed to aapt.
+     resConfig "en"
+  or 
+     resConfig "nodpi","hdpi"
+
+- Jar files are now pre-dexed for faster dexing.
+- First pass at NDK integration
+- API to add new generated source folders:
+     variant.addJavaSourceFoldersToModel(sourceFolder1, sourceFolders2,...)
+  This adds the source folder to the model (for IDE support).
+  Another API:
+     variant.registerJavaGeneratingTask(task, sourceFolder1, sourceFolders2,...)
+  This automatically adds the dependency on the task, sets up the JavaCompile task inputs and propagates
+  the folders to the model for IDE integration.
+- API to add extra artifacts on variants. This will allow to register Java or Android artifacts, for instance
+  for alternative test artifacts.
+
+0.6.3
+- Fixed ClassNotFoundException:MergingException introduced in 0.6.2
+
+0.6.2
+- Lint now picks up the SDK home from sdk.dir in local.properties
+- Error message shown when using an unsupported version of Gradle now explains how to update the Gradle wrapper
+- Merged resource files no longer place their source markers into the R file as comments
+- Project path can contain '--' (two dashes)
+- Internal changes to improve integration with Android Studio
+
+0.6.1
+
+- Fixed issues with lint task found in 0.6.0
+
+0.6.0
+
+- Enabled support for Gradle 1.8
+- Gradle 1.8 is now the minimum supported version
+- Default encoding for compiling Java code is UTF-8
+- Users can now specify the encoding to use to compile Java code
+- Fixed Gradle 1.8-specific bugs
+  - Importing projects with missing dependencies was broken
+  - Compiling projects with AIDL files was broken
+
+0.5.7
+
+- Proguard support for libraries.
+  Note the current DSL property 'proguardFiles' for library now sets the proguard rule file used when proguarding the library code.
+  The new property 'consumerProguardFiles' is used to package a rule file inside an aar.
+- Improved IDE support, including loading project with broken dependencies and anchor task to generate Java code
+- New hook tasks: preBuild and prebuild<VariantName>
+- First lint integration. This is a work in progress and therefore the lint task is not added to the check task.
+- Enable compatibility with 1.8
+
 0.5.6
 
-- Enable support for 1.7
+- Enabled support for 1.7
 
 0.5.5
 
@@ -18,17 +92,17 @@
 
 0.5.3
 
-- Fix a crashing bug in PrepareDependenciesTask
+- Fixed a crashing bug in PrepareDependenciesTask
 
 0.5.2
 
 - Better error reporting for cmd line tools, especially
   if run in parallel in spawned threads
-- Fix an issue due to windows path in merged resource files.
+- Fixed an issue due to windows path in merged resource files.
 
 0.5.1
 
-- Fix issue in the dependency checker.
+- Fixed issue in the dependency checker.
 
 0.5.0:
 
@@ -47,10 +121,10 @@
 
 0.4.3:
 
-- enable crunching for all png files, not just .9.png
-- fix dealing with non resource files in res/ and assets/
-- fix crash when doing incremental aidl compilation due to broken method name (ah the joy of Groovy...)
-- clean older R classes when the app package name has changed.
+- Enabled crunching for all png files, not just .9.png
+- Fixed dealing with non resource files in res/ and assets/
+- Fixed crash when doing incremental aidl compilation due to broken method name (ah the joy of Groovy...)
+- Cleaned older R classes when the app package name has changed.
 
 0.4.2
 
diff --git a/gradle-model/build.gradle b/gradle-model/build.gradle
index f4a8458..929eaeb 100644
--- a/gradle-model/build.gradle
+++ b/gradle-model/build.gradle
@@ -30,14 +30,6 @@
 sourceSets.test.compileClasspath += configurations.gradleRepo
 sourceSets.test.runtimeClasspath += configurations.gradleRepo
 
-def getVersion() {
-    if (project.has("release")) {
-        return project.ext.baseVersion
-    }
-
-    return project.ext.baseVersion + '-SNAPSHOT'
-}
-
 test.dependsOn ':gradle:publishLocal'
 
 idea {
diff --git a/gradle-model/src/test/java/com/android/build/gradle/model/AndroidProjectTest.java b/gradle-model/src/test/java/com/android/build/gradle/model/AndroidProjectTest.java
index 6ef14ff..dc44dc8 100644
--- a/gradle-model/src/test/java/com/android/build/gradle/model/AndroidProjectTest.java
+++ b/gradle-model/src/test/java/com/android/build/gradle/model/AndroidProjectTest.java
@@ -17,15 +17,21 @@
 package com.android.build.gradle.model;
 
 import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.builder.internal.StringHelper;
+import com.android.builder.model.AndroidArtifact;
 import com.android.builder.model.AndroidLibrary;
 import com.android.builder.model.AndroidProject;
-import com.android.builder.model.ArtifactInfo;
+import com.android.builder.model.ArtifactMetaData;
 import com.android.builder.model.BuildTypeContainer;
 import com.android.builder.model.Dependencies;
+import com.android.builder.model.JavaArtifact;
+import com.android.builder.model.JavaCompileOptions;
 import com.android.builder.model.ProductFlavor;
 import com.android.builder.model.ProductFlavorContainer;
 import com.android.builder.model.SigningConfig;
 import com.android.builder.model.SourceProvider;
+import com.android.builder.model.SourceProviderContainer;
 import com.android.builder.model.Variant;
 import com.android.builder.signing.KeystoreHelper;
 import com.android.prefs.AndroidLocation;
@@ -41,14 +47,15 @@
 import java.net.URL;
 import java.security.CodeSource;
 import java.security.KeyStore;
-import java.util.List;
+import java.util.Collection;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Set;
+
+import static com.android.builder.model.AndroidProject.ARTIFACT_INSTRUMENT_TEST;
 
 public class AndroidProjectTest extends TestCase {
 
-    private final static String MODEL_VERSION = "0.5.0-SNAPSHOT";
+    private final static String MODEL_VERSION = "0.7.0-SNAPSHOT";
 
     private static final Map<String, ProjectData> sProjectModelMap = Maps.newHashMap();
 
@@ -154,6 +161,10 @@
         assertFalse("Library Project", model.isLibrary());
         assertEquals("Compile Target", "android-15", model.getCompileTarget());
         assertFalse("Non empty bootclasspath", model.getBootClasspath().isEmpty());
+
+        JavaCompileOptions javaCompileOptions = model.getJavaCompileOptions();
+        assertEquals("1.6", javaCompileOptions.getSourceCompatibility());
+        assertEquals("1.6", javaCompileOptions.getTargetCompatibility());
     }
 
     public void testBasicSourceProviders() throws Exception {
@@ -163,17 +174,90 @@
         AndroidProject model = projectData.model;
         File projectDir = projectData.projectDir;
 
+        testDefaultSourceSets(model, projectDir);
+
+        // test the source provider for the artifacts
+        for (Variant variant : model.getVariants()) {
+            AndroidArtifact artifact = variant.getMainArtifact();
+            assertNull(artifact.getVariantSourceProvider());
+            assertNull(artifact.getMultiFlavorSourceProvider());
+        }
+    }
+
+    public void testBasicMultiFlavorsSourceProviders() throws Exception {
+        // Load the custom model for the project
+        ProjectData projectData = getModelForProject("basicMultiFlavors");
+
+        AndroidProject model = projectData.model;
+        File projectDir = projectData.projectDir;
+
+        testDefaultSourceSets(model, projectDir);
+
+        // test the source provider for the flavor
+        Collection<ProductFlavorContainer> productFlavors = model.getProductFlavors();
+        assertEquals("Product Flavor Count", 4, productFlavors.size());
+
+        for (ProductFlavorContainer pfContainer : productFlavors) {
+            String name = pfContainer.getProductFlavor().getName();
+            new SourceProviderTester(
+                    model.getName(),
+                    projectDir,
+                    name,
+                    pfContainer.getSourceProvider())
+                .test();
+
+            assertEquals(1, pfContainer.getExtraSourceProviders().size());
+            SourceProviderContainer container = getSourceProviderContainer(
+                    pfContainer.getExtraSourceProviders(), ARTIFACT_INSTRUMENT_TEST);
+            assertNotNull(container);
+
+            new SourceProviderTester(
+                    model.getName(),
+                    projectDir,
+                    "instrumentTest" + StringHelper.capitalize(name),
+                    container.getSourceProvider())
+                .test();
+        }
+
+        // test the source provider for the artifacts
+        for (Variant variant : model.getVariants()) {
+            AndroidArtifact artifact = variant.getMainArtifact();
+            assertNotNull(artifact.getVariantSourceProvider());
+            assertNotNull(artifact.getMultiFlavorSourceProvider());
+        }
+    }
+
+    private void testDefaultSourceSets(@NonNull AndroidProject model, @NonNull File projectDir) {
         ProductFlavorContainer defaultConfig = model.getDefaultConfig();
 
+        // test the main source provider
         new SourceProviderTester(model.getName(), projectDir,
                 "main", defaultConfig.getSourceProvider())
                 .test();
+
+        // test the main instrumentTest source provider
+        SourceProviderContainer testSourceProviders = getSourceProviderContainer(
+                defaultConfig.getExtraSourceProviders(), ARTIFACT_INSTRUMENT_TEST);
+        assertNotNull("InstrumentTest source Providers null-check", testSourceProviders);
+
         new SourceProviderTester(model.getName(), projectDir,
-                "instrumentTest", defaultConfig.getTestSourceProvider())
+                "instrumentTest", testSourceProviders.getSourceProvider())
+            .test();
+
+        // test the source provider for the build types
+        Collection<BuildTypeContainer> buildTypes = model.getBuildTypes();
+        assertEquals("Build Type Count", 2, buildTypes.size());
+
+        for (BuildTypeContainer btContainer : model.getBuildTypes()) {
+            new SourceProviderTester(
+                    model.getName(),
+                    projectDir,
+                    btContainer.getBuildType().getName(),
+                    btContainer.getSourceProvider())
                 .test();
 
-        Map<String, BuildTypeContainer> buildTypes = model.getBuildTypes();
-        assertEquals("Build Type Count", 2, buildTypes.size());
+            assertEquals(0, btContainer.getExtraSourceProviders().size());
+        }
     }
 
     public void testBasicVariantDetails() throws Exception {
@@ -182,47 +266,61 @@
 
         AndroidProject model = projectData.model;
 
-        Map<String, Variant> variants = model.getVariants();
+        Collection<Variant> variants = model.getVariants();
         assertEquals("Variant Count", 2 , variants.size());
 
         // debug variant
-        Variant debugVariant = variants.get("Debug");
-        assertNotNull("Debug Variant null-check", debugVariant);
+        Variant debugVariant = getVariant(variants, "debug");
+        assertNotNull("debug Variant null-check", debugVariant);
         new ProductFlavorTester(debugVariant.getMergedFlavor(), "Debug Merged Flavor")
                 .setVersionCode(12)
                 .setVersionName("2.0")
                 .setMinSdkVersion(16)
                 .setTargetSdkVersion(16)
-                .test();
+                .setTestInstrumentationRunner("android.test.InstrumentationTestRunner")
+                .setTestHandleProfiling(Boolean.FALSE)
+                .setTestFunctionalTest(null)
+            .test();
 
-        ArtifactInfo debugMainInfo = debugVariant.getMainArtifactInfo();
+        AndroidArtifact debugMainInfo = debugVariant.getMainArtifact();
         assertNotNull("Debug main info null-check", debugMainInfo);
         assertEquals("Debug package name", "com.android.tests.basic.debug",
                 debugMainInfo.getPackageName());
         assertTrue("Debug signed check", debugMainInfo.isSigned());
         assertEquals("Debug signingConfig name", "myConfig", debugMainInfo.getSigningConfigName());
+        assertEquals("Debug sourceGenTask", "generateDebugSources", debugMainInfo.getSourceGenTaskName());
+        assertEquals("Debug javaCompileTask", "compileDebugJava", debugMainInfo.getJavaCompileTaskName());
+
+        Collection<AndroidArtifact> debugExtraAndroidArtifacts = debugVariant.getExtraAndroidArtifacts();
+
 
         // this variant is tested.
-        ArtifactInfo debugTestInfo = debugVariant.getTestArtifactInfo();
+        AndroidArtifact debugTestInfo = getAndroidArtifact(debugExtraAndroidArtifacts,
+                ARTIFACT_INSTRUMENT_TEST);
         assertNotNull("Test info null-check", debugTestInfo);
         assertEquals("Test package name", "com.android.tests.basic.debug.test",
                 debugTestInfo.getPackageName());
         assertNotNull("Test output file null-check", debugTestInfo.getOutputFile());
         assertTrue("Test signed check", debugTestInfo.isSigned());
         assertEquals("Test signingConfig name", "myConfig", debugTestInfo.getSigningConfigName());
+        assertEquals("Test sourceGenTask", "generateDebugTestSources", debugTestInfo.getSourceGenTaskName());
+        assertEquals("Test javaCompileTask", "compileDebugTestJava", debugTestInfo.getJavaCompileTaskName());
 
         // release variant, not tested.
-        Variant releaseVariant = variants.get("Release");
-        assertNotNull("Release Variant null-check", releaseVariant);
+        Variant releaseVariant = getVariant(variants, "release");
+        assertNotNull("release Variant null-check", releaseVariant);
 
-        ArtifactInfo relMainInfo = releaseVariant.getMainArtifactInfo();
+        AndroidArtifact relMainInfo = releaseVariant.getMainArtifact();
         assertNotNull("Release main info null-check", relMainInfo);
         assertEquals("Release package name", "com.android.tests.basic",
                 relMainInfo.getPackageName());
         assertFalse("Release signed check", relMainInfo.isSigned());
         assertNull("Release signingConfig name", relMainInfo.getSigningConfigName());
+        assertEquals("Release sourceGenTask", "generateReleaseSources", relMainInfo.getSourceGenTaskName());
+        assertEquals("Release javaCompileTask", "compileReleaseJava", relMainInfo.getJavaCompileTaskName());
 
-        ArtifactInfo relTestInfo = releaseVariant.getTestArtifactInfo();
+        Collection<AndroidArtifact> releaseExtraAndroidArtifacts = releaseVariant.getExtraAndroidArtifacts();
+        AndroidArtifact relTestInfo = getAndroidArtifact(releaseExtraAndroidArtifacts, ARTIFACT_INSTRUMENT_TEST);
         assertNull("Release test info null-check", relTestInfo);
 
         // check debug dependencies
@@ -231,7 +329,7 @@
         assertEquals(2, dependencies.getJars().size());
         assertEquals(1, dependencies.getLibraries().size());
 
-        AndroidLibrary lib = dependencies.getLibraries().get(0);
+        AndroidLibrary lib = dependencies.getLibraries().iterator().next();
         assertNotNull(lib);
         assertNotNull(lib.getBundle());
         assertNotNull(lib.getFolder());
@@ -245,12 +343,16 @@
 
         AndroidProject model = projectData.model;
 
-        assertEquals("Number of signingConfig", 2, model.getSigningConfigs().size());
+        Collection<SigningConfig> signingConfigs = model.getSigningConfigs();
+        assertNotNull("SigningConfigs null-check", signingConfigs);
+        assertEquals("Number of signingConfig", 2, signingConfigs.size());
 
-        SigningConfig debugSigningConfig = model.getSigningConfigs().get("debug");
+        SigningConfig debugSigningConfig = getSigningConfig(signingConfigs, "debug");
+        assertNotNull("debug signing config null-check", debugSigningConfig);
         new SigningConfigTester(debugSigningConfig, "debug", true).test();
 
-        SigningConfig mySigningConfig = model.getSigningConfigs().get("myConfig");
+        SigningConfig mySigningConfig = getSigningConfig(signingConfigs, "myConfig");
+        assertNotNull("myConfig signing config null-check", mySigningConfig);
         new SigningConfigTester(mySigningConfig, "myConfig", true)
                 .setStoreFile(new File(projectData.projectDir, "debug.keystore"))
                 .test();
@@ -280,8 +382,12 @@
                 .setManifestFile("AndroidManifest.xml")
                 .test();
 
+        SourceProviderContainer testSourceProviderContainer = getSourceProviderContainer(
+                defaultConfig.getExtraSourceProviders(), ARTIFACT_INSTRUMENT_TEST);
+        assertNotNull("InstrumentTest source Providers null-check", testSourceProviderContainer);
+
         new SourceProviderTester(model.getName(), projectDir,
-                "instrumentTest", defaultConfig.getTestSourceProvider())
+                "instrumentTest", testSourceProviderContainer.getSourceProvider())
                 .setJavaDir("tests/java")
                 .setResourcesDir("tests/resources")
                 .setAidlDir("tests/aidl")
@@ -303,13 +409,13 @@
         assertNotNull("Model Object null-check", model);
         assertEquals("Model Name", "renamedApk", model.getName());
 
-        Map<String, Variant> variants = model.getVariants();
+        Collection<Variant> variants = model.getVariants();
         assertEquals("Variant Count", 2 , variants.size());
 
         File buildDir = new File(projectDir, "build");
 
-        for (Variant variant : variants.values()) {
-            ArtifactInfo mainInfo = variant.getMainArtifactInfo();
+        for (Variant variant : variants) {
+            AndroidArtifact mainInfo = variant.getMainArtifact();
             assertNotNull(
                     "Null-check on mainArtifactInfo for " + variant.getDisplayName(),
                     mainInfo);
@@ -336,21 +442,26 @@
         new SourceProviderTester(model.getName(), projectDir,
                 "main", defaultConfig.getSourceProvider())
                 .test();
+
+        SourceProviderContainer testSourceProviderContainer = getSourceProviderContainer(
+                defaultConfig.getExtraSourceProviders(), ARTIFACT_INSTRUMENT_TEST);
+        assertNotNull("InstrumentTest source Providers null-check", testSourceProviderContainer);
+
         new SourceProviderTester(model.getName(), projectDir,
-                "instrumentTest", defaultConfig.getTestSourceProvider())
+                "instrumentTest", testSourceProviderContainer.getSourceProvider())
                 .test();
 
-        Map<String, BuildTypeContainer> buildTypes = model.getBuildTypes();
+        Collection<BuildTypeContainer> buildTypes = model.getBuildTypes();
         assertEquals("Build Type Count", 2, buildTypes.size());
 
-        Map<String, Variant> variants = model.getVariants();
-        assertEquals("Variant Count", 8 , variants.size());
+        Collection<Variant> variants = model.getVariants();
+        assertEquals("Variant Count", 8, variants.size());
 
-        Variant f1faDebugVariant = variants.get("F1FaDebug");
-        assertNotNull("F1faDebug Variant null-check", f1faDebugVariant);
+        Variant f1faDebugVariant = getVariant(variants, "f1FaDebug");
+        assertNotNull("f1faDebug Variant null-check", f1faDebugVariant);
         new ProductFlavorTester(f1faDebugVariant.getMergedFlavor(), "F1faDebug Merged Flavor")
                 .test();
-        new VariantTester(f1faDebugVariant, projectDir, "flavors-f1fa-debug-unaligned.apk").test();
+        new VariantTester(f1faDebugVariant, projectDir, "flavors-f1-fa-debug-unaligned.apk").test();
     }
 
     public void testTicTacToe() throws Exception {
@@ -363,14 +474,18 @@
         ProjectData appModelData = map.get(":app");
         assertNotNull("app module model null-check", appModelData);
 
-        Dependencies dependencies = appModelData.model.getVariants().get("Debug").getMainArtifactInfo().getDependencies();
+        Collection<Variant> variants = appModelData.model.getVariants();
+        Variant debugVariant = getVariant(variants, "debug");
+        assertNotNull("debug variant null-check", debugVariant);
+
+        Dependencies dependencies = debugVariant.getMainArtifact().getDependencies();
         assertNotNull(dependencies);
 
-        List<AndroidLibrary> libs = dependencies.getLibraries();
+        Collection<AndroidLibrary> libs = dependencies.getLibraries();
         assertNotNull(libs);
         assertEquals(1, libs.size());
 
-        AndroidLibrary androidLibrary = libs.get(0);
+        AndroidLibrary androidLibrary = libs.iterator().next();
         assertNotNull(androidLibrary);
 
         assertEquals("Dependency project path", ":lib", androidLibrary.getProject());
@@ -388,36 +503,37 @@
 
         assertFalse("Library Project", model.isLibrary());
 
-        Map<String, Variant> variants = model.getVariants();
+        Collection<Variant> variants = model.getVariants();
+        Collection<ProductFlavorContainer> productFlavors = model.getProductFlavors();
 
-        ProductFlavorContainer flavor1 = model.getProductFlavors().get("flavor1");
+        ProductFlavorContainer flavor1 = getProductFlavor(productFlavors, "flavor1");
         assertNotNull(flavor1);
 
-        Variant flavor1Debug = variants.get("Flavor1Debug");
+        Variant flavor1Debug = getVariant(variants, "flavor1Debug");
         assertNotNull(flavor1Debug);
 
-        Dependencies dependencies = flavor1Debug.getMainArtifactInfo().getDependencies();
+        Dependencies dependencies = flavor1Debug.getMainArtifact().getDependencies();
         assertNotNull(dependencies);
-        List<AndroidLibrary> libs = dependencies.getLibraries();
+        Collection<AndroidLibrary> libs = dependencies.getLibraries();
         assertNotNull(libs);
         assertEquals(1, libs.size());
-        AndroidLibrary androidLibrary = libs.get(0);
+        AndroidLibrary androidLibrary = libs.iterator().next();
         assertNotNull(androidLibrary);
         // TODO: right now we can only test the folder name efficiently
         assertEquals("FlavorlibLib1Unspecified.aar", androidLibrary.getFolder().getName());
 
-        ProductFlavorContainer flavor2 = model.getProductFlavors().get("flavor2");
+        ProductFlavorContainer flavor2 = getProductFlavor(productFlavors, "flavor2");
         assertNotNull(flavor2);
 
-        Variant flavor2Debug = variants.get("Flavor2Debug");
+        Variant flavor2Debug = getVariant(variants, "flavor2Debug");
         assertNotNull(flavor2Debug);
 
-        dependencies = flavor2Debug.getMainArtifactInfo().getDependencies();
+        dependencies = flavor2Debug.getMainArtifact().getDependencies();
         assertNotNull(dependencies);
         libs = dependencies.getLibraries();
         assertNotNull(libs);
         assertEquals(1, libs.size());
-        androidLibrary = libs.get(0);
+        androidLibrary = libs.iterator().next();
         assertNotNull(androidLibrary);
         // TODO: right now we can only test the folder name efficiently
         assertEquals("FlavorlibLib2Unspecified.aar", androidLibrary.getFolder().getName());
@@ -430,29 +546,161 @@
         assertNotNull("Module app null-check", baseLibModelData);
         AndroidProject model = baseLibModelData.model;
 
-        Map<String, Variant> variants = model.getVariants();
+        Collection<Variant> variants = model.getVariants();
         assertEquals("Variant count", 2, variants.size());
 
-        Variant variant = variants.get("Release");
-        assertNotNull("Release variant null-check", variant);
+        Variant variant = getVariant(variants, "release");
+        assertNotNull("release variant null-check", variant);
 
-        ArtifactInfo mainInfo = variant.getMainArtifactInfo();
+        AndroidArtifact mainInfo = variant.getMainArtifact();
         assertNotNull("Main Artifact null-check", mainInfo);
 
         Dependencies dependencies = mainInfo.getDependencies();
         assertNotNull("Dependencies null-check", dependencies);
 
-        List<String> projects = dependencies.getProjects();
+        Collection<String> projects = dependencies.getProjects();
         assertNotNull("project dep list null-check", projects);
         assertEquals("project dep count", 1, projects.size());
-        assertEquals("dep on :util check", ":util", projects.get(0));
+        assertEquals("dep on :util check", ":util", projects.iterator().next());
 
-        List<File> jars = dependencies.getJars();
+        Collection<File> jars = dependencies.getJars();
         assertNotNull("jar dep list null-check", jars);
         // TODO these are jars coming from ':util' They shouldn't be there.
         assertEquals("jar dep count", 2, jars.size());
     }
 
+    public void testGenFolderApi() throws Exception {
+        // Load the custom model for the project
+        ProjectData projectData = getModelForProject("genFolderApi");
+
+        AndroidProject model = projectData.model;
+        File projectDir = projectData.projectDir;
+
+        File buildDir = new File(projectDir, "build");
+
+        for (Variant variant : model.getVariants()) {
+
+            AndroidArtifact mainInfo = variant.getMainArtifact();
+            assertNotNull(
+                    "Null-check on mainArtifactInfo for " + variant.getDisplayName(),
+                    mainInfo);
+
+            // get the generated source folders.
+            Collection<File> genFolder = mainInfo.getGeneratedSourceFolders();
+
+            // We're looking for a custom folder
+            String folderStart = new File(buildDir, "customCode").getAbsolutePath() + File.separatorChar;
+            boolean found = false;
+            for (File f : genFolder) {
+                if (f.getAbsolutePath().startsWith(folderStart)) {
+                    found = true;
+                    break;
+                }
+            }
+
+            assertTrue("custom generated source folder check", found);
+        }
+    }
+
+    public void testArtifactApi() throws Exception {
+        // Load the custom model for the project
+        ProjectData projectData = getModelForProject("artifactApi");
+
+        AndroidProject model = projectData.model;
+
+        // check the Artifact Meta Data
+        Collection<ArtifactMetaData> extraArtifacts = model.getExtraArtifacts();
+        assertNotNull("Extra artifact collection null-check", extraArtifacts);
+        assertEquals("Extra artifact size check", 2, extraArtifacts.size());
+
+        assertNotNull("instrument test metadata null-check",
+                getArtifactMetaData(extraArtifacts, ARTIFACT_INSTRUMENT_TEST));
+
+        // get the custom one.
+        ArtifactMetaData extraArtifactMetaData = getArtifactMetaData(extraArtifacts, "__test__");
+        assertNotNull("custom extra metadata null-check", extraArtifactMetaData);
+        assertFalse("custom extra meta data is Test check", extraArtifactMetaData.isTest());
+        assertEquals("custom extra meta data type check", ArtifactMetaData.TYPE_JAVA, extraArtifactMetaData.getType());
+
+        // check the extra source provider on the build Types.
+        for (BuildTypeContainer btContainer : model.getBuildTypes()) {
+            String name = btContainer.getBuildType().getName();
+            Collection<SourceProviderContainer> extraSourceProviderContainers = btContainer.getExtraSourceProviders();
+            assertNotNull(
+                    "Extra source provider containers for build type '" + name + "' null-check",
+                    extraSourceProviderContainers);
+            assertEquals(
+                    "Extra source provider containers for build type size '" + name + "' check",
+                    1,
+                    extraSourceProviderContainers.size());
+
+            SourceProviderContainer sourceProviderContainer = extraSourceProviderContainers.iterator().next();
+            assertNotNull(
+                    "Extra artifact source provider for " + name + " null check",
+                    sourceProviderContainer);
+
+            assertEquals(
+                    "Extra artifact source provider for " + name + " name check",
+                    "__test__",
+                    sourceProviderContainer.getArtifactName());
+
+            assertEquals(
+                    "Extra artifact source provider for " + name + " value check",
+                    "buildType:" + name,
+                    sourceProviderContainer.getSourceProvider().getManifestFile().getPath());
+        }
+
+        // check the extra source provider on the product flavors.
+        for (ProductFlavorContainer pfContainer : model.getProductFlavors()) {
+            String name = pfContainer.getProductFlavor().getName();
+            Collection<SourceProviderContainer> extraSourceProviderContainers = pfContainer.getExtraSourceProviders();
+            assertNotNull(
+                    "Extra source provider container for product flavor '" + name + "' null-check",
+                    extraSourceProviderContainers);
+            assertEquals(
+                    "Extra artifact source provider container for product flavor size '" + name + "' check",
+                    2,
+                    extraSourceProviderContainers.size());
+
+            assertNotNull(
+                    "Extra source provider container for product flavor '" + name + "': instTest check",
+                    getSourceProviderContainer(extraSourceProviderContainers, ARTIFACT_INSTRUMENT_TEST));
+
+
+            SourceProviderContainer sourceProviderContainer = getSourceProviderContainer(
+                    extraSourceProviderContainers, "__test__");
+            assertNotNull(
+                    "Custom source provider container for " + name + " null check",
+                    sourceProviderContainer);
+
+            assertEquals(
+                    "Custom artifact source provider for " + name + " name check",
+                    "__test__",
+                    sourceProviderContainer.getArtifactName());
+
+            assertEquals(
+                    "Extra artifact source provider for " + name + " value check",
+                    "productFlavor:" + name,
+                    sourceProviderContainer.getSourceProvider().getManifestFile().getPath());
+        }
+
+        // check the extra artifacts on the variants
+        for (Variant variant : model.getVariants()) {
+            String name = variant.getName();
+            Collection<JavaArtifact> javaArtifacts = variant.getExtraJavaArtifacts();
+            assertEquals(1, javaArtifacts.size());
+            JavaArtifact javaArtifact = javaArtifacts.iterator().next();
+            assertEquals("__test__", javaArtifact.getName());
+            assertEquals("assemble:" + name, javaArtifact.getAssembleTaskName());
+            assertEquals("compile:" + name, javaArtifact.getJavaCompileTaskName());
+            assertEquals(new File("classesFolder:" + name), javaArtifact.getClassesFolder());
+
+            SourceProvider variantSourceProvider = javaArtifact.getVariantSourceProvider();
+            assertNotNull(variantSourceProvider);
+            assertEquals("provider:" + name, variantSourceProvider.getManifestFile().getPath());
+        }
+    }
+
     /**
      * Returns the SDK folder as built from the Android source tree.
      * @return the SDK
@@ -505,6 +753,95 @@
         return new File(rootDir, "tests");
     }
 
+    @Nullable
+    private static Variant getVariant(
+            @NonNull Collection<Variant> items,
+            @NonNull String name) {
+        for (Variant item : items) {
+            if (name.equals(item.getName())) {
+                return item;
+            }
+        }
+
+        return null;
+    }
+
+    @Nullable
+    private static ProductFlavorContainer getProductFlavor(
+            @NonNull Collection<ProductFlavorContainer> items,
+            @NonNull String name) {
+        for (ProductFlavorContainer item : items) {
+            assertNotNull("ProductFlavorContainer list item null-check:" + name, item);
+            assertNotNull("ProductFlavorContainer.getProductFlavor() list item null-check: " + name, item.getProductFlavor());
+            assertNotNull("ProductFlavorContainer.getProductFlavor().getName() list item null-check: " + name, item.getProductFlavor().getName());
+            if (name.equals(item.getProductFlavor().getName())) {
+                return item;
+            }
+        }
+
+        return null;
+    }
+
+    @Nullable
+    private static ArtifactMetaData getArtifactMetaData(
+            @NonNull Collection<ArtifactMetaData> items,
+            @NonNull String name) {
+        for (ArtifactMetaData item : items) {
+            assertNotNull("ArtifactMetaData list item null-check:" + name, item);
+            assertNotNull("ArtifactMetaData.getName() list item null-check: " + name, item.getName());
+            if (name.equals(item.getName())) {
+                return item;
+            }
+        }
+
+        return null;
+    }
+
+    @Nullable
+    private static AndroidArtifact getAndroidArtifact(
+            @NonNull Collection<AndroidArtifact> items,
+            @NonNull String name) {
+        for (AndroidArtifact item : items) {
+            assertNotNull("AndroidArtifact list item null-check:" + name, item);
+            assertNotNull("AndroidArtifact.getName() list item null-check: " + name, item.getName());
+            if (name.equals(item.getName())) {
+                return item;
+            }
+        }
+
+        return null;
+    }
+
+    @Nullable
+    private static SigningConfig getSigningConfig(
+            @NonNull Collection<SigningConfig> items,
+            @NonNull String name) {
+        for (SigningConfig item : items) {
+            assertNotNull("SigningConfig list item null-check:" + name, item);
+            assertNotNull("SigningConfig.getName() list item null-check: " + name, item.getName());
+            if (name.equals(item.getName())) {
+                return item;
+            }
+        }
+
+        return null;
+    }
+
+    @Nullable
+    private static SourceProviderContainer getSourceProviderContainer(
+            @NonNull Collection<SourceProviderContainer> items,
+            @NonNull String name) {
+        for (SourceProviderContainer item : items) {
+            assertNotNull("SourceProviderContainer list item null-check:" + name, item);
+            assertNotNull("SourceProviderContainer.getName() list item null-check: " + name, item.getArtifactName());
+            if (name.equals(item.getArtifactName())) {
+                return item;
+            }
+        }
+
+        return null;
+    }
+
     private static final class ProductFlavorTester {
         @NonNull private final ProductFlavor productFlavor;
         @NonNull private final String name;
@@ -517,6 +854,8 @@
         private int renderscriptTargetApi = -1;
         private String testPackageName = null;
         private String testInstrumentationRunner = null;
+        private Boolean testHandleProfiling = null;
+        private Boolean testFunctionalTest = null;
 
         ProductFlavorTester(@NonNull ProductFlavor productFlavor, @NonNull String name) {
             this.productFlavor = productFlavor;
@@ -533,36 +872,46 @@
             return this;
         }
 
-         ProductFlavorTester setVersionName(String versionName) {
+        ProductFlavorTester setVersionName(String versionName) {
             this.versionName = versionName;
             return this;
         }
 
-         ProductFlavorTester setMinSdkVersion(int minSdkVersion) {
+        ProductFlavorTester setMinSdkVersion(int minSdkVersion) {
             this.minSdkVersion = minSdkVersion;
             return this;
         }
 
-         ProductFlavorTester setTargetSdkVersion(int targetSdkVersion) {
+        ProductFlavorTester setTargetSdkVersion(int targetSdkVersion) {
             this.targetSdkVersion = targetSdkVersion;
             return this;
         }
 
-         ProductFlavorTester setRenderscriptTargetApi(int renderscriptTargetApi) {
+        ProductFlavorTester setRenderscriptTargetApi(int renderscriptTargetApi) {
             this.renderscriptTargetApi = renderscriptTargetApi;
             return this;
         }
 
-         ProductFlavorTester setTestPackageName(String testPackageName) {
+        ProductFlavorTester setTestPackageName(String testPackageName) {
             this.testPackageName = testPackageName;
             return this;
         }
 
-         ProductFlavorTester setTestInstrumentationRunner(String testInstrumentationRunner) {
+        ProductFlavorTester setTestInstrumentationRunner(String testInstrumentationRunner) {
             this.testInstrumentationRunner = testInstrumentationRunner;
             return this;
         }
 
+        ProductFlavorTester setTestHandleProfiling(Boolean testHandleProfiling) {
+            this.testHandleProfiling = testHandleProfiling;
+            return this;
+        }
+
+        ProductFlavorTester setTestFunctionalTest(Boolean testFunctionalTest) {
+            this.testFunctionalTest = testFunctionalTest;
+            return this;
+        }
+
         void test() {
             assertEquals(name + ":packageName", packageName, productFlavor.getPackageName());
             assertEquals(name + ":VersionCode", versionCode, productFlavor.getVersionCode());
@@ -576,6 +925,10 @@
                     testPackageName, productFlavor.getTestPackageName());
             assertEquals(name + ":testInstrumentationRunner",
                     testInstrumentationRunner, productFlavor.getTestInstrumentationRunner());
+            assertEquals(name + ":testHandleProfiling",
+                    testHandleProfiling, productFlavor.getTestHandleProfiling());
+            assertEquals(name + ":testFunctionalTest",
+                    testFunctionalTest, productFlavor.getTestFunctionalTest());
         }
     }
 
@@ -652,20 +1005,23 @@
         }
 
         void test() {
-            testSinglePathSet("java", javaDir, sourceProvider.getJavaDirectories());
-            testSinglePathSet("resources", resourcesDir, sourceProvider.getResourcesDirectories());
-            testSinglePathSet("res", resDir, sourceProvider.getResDirectories());
-            testSinglePathSet("assets", assetsDir, sourceProvider.getAssetsDirectories());
-            testSinglePathSet("aidl", aidlDir, sourceProvider.getAidlDirectories());
-            testSinglePathSet("rs", renderscriptDir, sourceProvider.getRenderscriptDirectories());
-            testSinglePathSet("jni", jniDir, sourceProvider.getJniDirectories());
+            testSinglePathCollection("java", javaDir, sourceProvider.getJavaDirectories());
+            testSinglePathCollection("resources", resourcesDir, sourceProvider.getResourcesDirectories());
+            testSinglePathCollection("res", resDir, sourceProvider.getResDirectories());
+            testSinglePathCollection("assets", assetsDir, sourceProvider.getAssetsDirectories());
+            testSinglePathCollection("aidl", aidlDir, sourceProvider.getAidlDirectories());
+            testSinglePathCollection("rs", renderscriptDir, sourceProvider.getRenderscriptDirectories());
+            testSinglePathCollection("jni", jniDir, sourceProvider.getJniDirectories());
 
             assertEquals("AndroidManifest",
                     new File(projectDir, manifestFile).getAbsolutePath(),
                     sourceProvider.getManifestFile().getAbsolutePath());
         }
 
-        private void testSinglePathSet(String setName, String referencePath, Set<File> pathSet) {
+        private void testSinglePathCollection(
+                @NonNull String setName,
+                @NonNull String referencePath,
+                @NonNull Collection<File> pathSet) {
             assertEquals(1, pathSet.size());
             assertEquals(projectName + ": " + configName + "/" + setName,
                     new File(projectDir, referencePath).getAbsolutePath(),
@@ -687,13 +1043,19 @@
         }
 
         void test() {
+            AndroidArtifact artifact = variant.getMainArtifact();
+            assertNotNull("Main Artifact null-check", artifact);
+
             String variantName = variant.getName();
             File build = new File(projectDir,  "build");
             File apk = new File(build, "apk/" + outputFileName);
-            assertEquals(variantName + " output", apk, variant.getMainArtifactInfo().getOutputFile());
+            assertEquals(variantName + " output", apk, artifact.getOutputFile());
 
-            List<File> sourceFolders = variant.getMainArtifactInfo().getGeneratedSourceFolders();
+            Collection<File> sourceFolders = artifact.getGeneratedSourceFolders();
             assertEquals("Gen src Folder count", 4, sourceFolders.size());
+
+            File manifest = artifact.getGeneratedManifest();
+            assertNotNull(manifest);
         }
     }
 
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..f66ad4d
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1 @@
+org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=1024m
\ No newline at end of file
diff --git a/gradle/build.gradle b/gradle/build.gradle
index 16b99e6..fefe868 100644
--- a/gradle/build.gradle
+++ b/gradle/build.gradle
@@ -29,7 +29,12 @@
     groovy localGroovy()
     compile project(':builder')
 
-    runtime 'net.sf.proguard:proguard-gradle:4.9'
+    compile "com.android.tools:sdklib:$project.ext.baseAndroidVersion"
+    compile "com.android.tools:sdk-common:$project.ext.baseAndroidVersion"
+    compile "com.android.tools:common:$project.ext.baseAndroidVersion"
+
+    compile "com.android.tools.lint:lint:$project.ext.baseAndroidVersion"
+    compile 'net.sf.proguard:proguard-gradle:4.10'
 
     testCompile 'junit:junit:3.8.1'
 
@@ -52,7 +57,7 @@
 }
 
 dependencies{
-    provided 'net.sf.proguard:proguard-gradle:4.9'
+    provided 'net.sf.proguard:proguard-gradle:4.10'
 }
 
 //Include provided for compilation
@@ -67,26 +72,13 @@
     }
 }
 
-def getVersion() {
-    if (project.has("release")) {
-        return project.ext.baseVersion
-    }
-
-    return project.ext.baseVersion + '-SNAPSHOT'
-}
-
-version = getVersion()
 archivesBaseName = 'gradle'
-jar.manifest.attributes("Plugin-Version": version)
+project.ext.pomName = 'Gradle Plug-in for Android'
+project.ext.pomDesc = 'Gradle plug-in to build Android applications.'
 
-task publishLocal(type: Upload) {
-    configuration = configurations.archives
-    repositories {
-        mavenDeployer {
-            repository(url: uri("$rootProject.ext.androidHostOut/repo"))
-        }
-    }
-}
+apply from: '../publish.gradle'
+
+jar.manifest.attributes("Plugin-Version": version)
 publishLocal.dependsOn ':builder:publishLocal'
 
 task buildTest(type: Test, dependsOn: publishLocal) {
@@ -107,62 +99,6 @@
 
 check.dependsOn buildTest
 
-project.ext.sonatypeUsername = project.hasProperty('sonatypeUsername') ? sonatypeUsername : ""
-project.ext.sonatypePassword = project.hasProperty('sonatypePassword') ? sonatypePassword : ""
-
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            beforeDeployment { MavenDeployment deployment ->
-                if (!project.has("release")) {
-                    throw new StopExecutionException("uploadArchives must be called with the release.gradle init script")
-                }
-
-                if (project.ext.sonatypeUsername.length() == 0 || project.ext.sonatypePassword.length() == 0) {
-                    throw new StopExecutionException("uploadArchives cannot be called without sonatype username and password")
-                }
-
-                signing.signPom(deployment)
-            }
-
-            repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
-                authentication(userName: project.ext.sonatypeUsername, password: project.ext.sonatypePassword)
-            }
-
-            pom.project {
-                name 'Gradle Plug-in for Android'
-                description 'Gradle plug-in to build Android applications.'
-                url 'http://tools.android.com'
-                inceptionYear '2007'
-
-                licenses {
-                    license {
-                        name 'The Apache Software License, Version 2.0'
-                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                        distribution 'repo'
-                    }
-                }
-
-                scm {
-                    url "https://android.googlesource.com/platform/tools/build"
-                    connection "git://android.googlesource.com/platform/tools/build.git"
-                }
-
-                developers {
-                    developer {
-                        name 'The Android Open Source Project'
-                    }
-                }
-            }
-        }
-    }
-}
-
-// custom tasks for creating source/javadoc jars
-task sourcesJar(type: Jar, dependsOn:classes) {
-    classifier = 'sources'
-    from sourceSets.main.allSource
-}
 
 groovydoc {
     exclude     "**/internal/**"
@@ -178,15 +114,9 @@
     classifier  'javadoc'
     from        groovydoc.destinationDir
 }
- 
-// add javadoc/source jar tasks as artifacts
+
+// add javadoc jar tasks as artifacts
 artifacts {
-    archives jar
-    archives sourcesJar
     archives javadocJar
 }
 
-signing {
-    required { project.has("release") && gradle.taskGraph.hasTask("uploadArchives") }
-    sign configurations.archives
-}
diff --git a/gradle/src/build-test/groovy/com/android/build/gradle/AutomatedBuildTest.java b/gradle/src/build-test/groovy/com/android/build/gradle/AutomatedBuildTest.java
index b92239f..18e3277 100644
--- a/gradle/src/build-test/groovy/com/android/build/gradle/AutomatedBuildTest.java
+++ b/gradle/src/build-test/groovy/com/android/build/gradle/AutomatedBuildTest.java
@@ -35,10 +35,11 @@
 
     private static final String[] sBuiltProjects = new String[] {
             "aidl", "api", "applibtest", "assets", "attrOrder", "basic", "dependencies",
-            "dependencyChecker", "flavored", "flavorlib", "flavors", "libTestDep", "libsTest",
-            "localJars", "migrated", "multiproject", "multires", "overlay1", "overlay2",
-            "pkgOverride", "proguard", "proguardLib", "renderscript", "renderscriptInLib",
-            "renderscriptMultiSrc", "sameNamedLibs", "tictactoe" /*, "autorepo"*/
+            "dependencyChecker", "flavored", "flavorlib", "flavors", "genFolderApi",
+            "libProguardJarDep", "libProguardLibDep", "libTestDep", "libsTest", "localJars",
+            "migrated", "multiproject", "multires", "ndkSanAngeles", "ndkJniLib", "overlay1",
+            "overlay2", "pkgOverride", "proguard", "proguardLib", "renderscript", "renderscriptInLib",
+            "renderscriptMultiSrc", "rsSupportMode", "sameNamedLibs", "tictactoe" /*, "autorepo"*/
     };
 
     private static final String[] sReportProjects = new String[] {
@@ -50,6 +51,9 @@
         suite.setName("AutomatedBuildTest");
 
         for (String gradleVersion : BasePlugin.GRADLE_SUPPORTED_VERSIONS) {
+            if (isIgnoredGradleVersion(gradleVersion)) {
+                continue;
+            }
             // first the project we build on all available versions of Gradle
             for (String projectName : sBuiltProjects) {
                 String testName = "build_" + projectName + "_" + gradleVersion;
diff --git a/gradle/src/build-test/groovy/com/android/build/gradle/BuildTest.java b/gradle/src/build-test/groovy/com/android/build/gradle/BuildTest.java
index 8afb7ba..c81d0db 100644
--- a/gradle/src/build-test/groovy/com/android/build/gradle/BuildTest.java
+++ b/gradle/src/build-test/groovy/com/android/build/gradle/BuildTest.java
@@ -17,8 +17,10 @@
 package com.android.build.gradle;
 
 import com.android.build.gradle.internal.test.BaseTest;
+import com.google.common.collect.Lists;
 
 import java.io.File;
+import java.util.Collection;
 
 /**
  * Base class for build tests.
@@ -27,14 +29,28 @@
  * Android Source tree under out/host/<platform>/sdk/... (result of 'make sdk')
  */
 abstract class BuildTest extends BaseTest {
+    private static final Collection<String> IGNORED_GRADLE_VERSIONS = Lists.newArrayList();
 
     protected File testDir;
     protected File sdkDir;
+    protected File ndkDir;
 
     @Override
     protected void setUp() throws Exception {
         testDir = getTestDir();
         sdkDir = getSdkDir();
+        ndkDir = getNdkDir();
+    }
+
+    /**
+     * Indicates whether the given Gradle version should be ignored in tests (for example, when a Gradle version has
+     * not been publicly released yet.)
+     *
+     * @param gradleVersion the given Gradle version.
+     * @return {@code true} if the given Gradle version should be ignored, {@code false} otherwise.
+     */
+    protected static boolean isIgnoredGradleVersion(String gradleVersion) {
+      return IGNORED_GRADLE_VERSIONS.contains(gradleVersion);
     }
 
     protected File buildProject(String name, String gradleVersion) {
@@ -45,12 +61,10 @@
         File project = new File(testDir, name);
 
         File buildGradle = new File(project, "build.gradle");
-        if (!buildGradle.isFile()) {
-            return null;
-        }
+        assertTrue("Missing build.gradle for " + name, buildGradle.isFile());
 
         // build the project
-        runGradleTasks(sdkDir, gradleVersion, project, tasks);
+        runGradleTasks(sdkDir, ndkDir, gradleVersion, project, tasks);
 
         return project;
     }
diff --git a/gradle/src/build-test/groovy/com/android/build/gradle/ManualBuildTest.java b/gradle/src/build-test/groovy/com/android/build/gradle/ManualBuildTest.java
index 0ccfc24..e0c985b 100644
--- a/gradle/src/build-test/groovy/com/android/build/gradle/ManualBuildTest.java
+++ b/gradle/src/build-test/groovy/com/android/build/gradle/ManualBuildTest.java
@@ -16,6 +16,9 @@
 
 package com.android.build.gradle;
 
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+
 import javax.imageio.ImageIO;
 import java.awt.image.BufferedImage;
 import java.io.File;
@@ -29,36 +32,74 @@
  */
 public class ManualBuildTest extends BuildTest {
 
+    private final static int RED = 0xFFFF0000;
+    private final static int GREEN = 0xFF00FF00;
+    private final static int BLUE = 0xFF0000FF;
+
 
     public void testOverlay1Content() throws Exception {
         File project = buildProject("overlay1", BasePlugin.GRADLE_MIN_VERSION);
         File drawableOutput = new File(project, "build/res/all/debug/drawable");
 
-        checkImageColor(drawableOutput, "no_overlay.png", (int) 0xFF00FF00);
-        checkImageColor(drawableOutput, "type_overlay.png", (int) 0xFF00FF00);
+        checkImageColor(drawableOutput, "no_overlay.png", GREEN);
+        checkImageColor(drawableOutput, "type_overlay.png", GREEN);
     }
 
     public void testOverlay2Content() throws Exception {
         File project = buildProject("overlay2", BasePlugin.GRADLE_MIN_VERSION);
         File drawableOutput = new File(project, "build/res/all/one/debug/drawable");
 
-        checkImageColor(drawableOutput, "no_overlay.png", (int) 0xFF00FF00);
-        checkImageColor(drawableOutput, "type_overlay.png", (int) 0xFF00FF00);
-        checkImageColor(drawableOutput, "flavor_overlay.png", (int) 0xFF00FF00);
-        checkImageColor(drawableOutput, "type_flavor_overlay.png", (int) 0xFF00FF00);
+        checkImageColor(drawableOutput, "no_overlay.png", GREEN);
+        checkImageColor(drawableOutput, "type_overlay.png", GREEN);
+        checkImageColor(drawableOutput, "flavor_overlay.png", GREEN);
+        checkImageColor(drawableOutput, "type_flavor_overlay.png", GREEN);
+        checkImageColor(drawableOutput, "variant_type_flavor_overlay.png", GREEN);
+    }
+
+    public void testOverlay3Content() throws Exception {
+        File project = buildProject("overlay3", BasePlugin.GRADLE_MIN_VERSION);
+        File drawableOutput = new File(project, "build/res/all/freebeta/debug/drawable");
+
+        checkImageColor(drawableOutput, "no_overlay.png", GREEN);
+        checkImageColor(drawableOutput, "debug_overlay.png", GREEN);
+        checkImageColor(drawableOutput, "beta_overlay.png", GREEN);
+        checkImageColor(drawableOutput, "free_overlay.png", GREEN);
+        checkImageColor(drawableOutput, "free_beta_overlay.png", GREEN);
+        checkImageColor(drawableOutput, "free_beta_debug_overlay.png", GREEN);
+        checkImageColor(drawableOutput, "free_normal_overlay.png", RED);
+
+        drawableOutput = new File(project, "build/res/all/freenormal/debug/drawable");
+
+        checkImageColor(drawableOutput, "no_overlay.png", GREEN);
+        checkImageColor(drawableOutput, "debug_overlay.png", GREEN);
+        checkImageColor(drawableOutput, "beta_overlay.png", RED);
+        checkImageColor(drawableOutput, "free_overlay.png", GREEN);
+        checkImageColor(drawableOutput, "free_beta_overlay.png", RED);
+        checkImageColor(drawableOutput, "free_beta_debug_overlay.png", RED);
+        checkImageColor(drawableOutput, "free_normal_overlay.png", GREEN);
+
+        drawableOutput = new File(project, "build/res/all/paidbeta/debug/drawable");
+
+        checkImageColor(drawableOutput, "no_overlay.png", GREEN);
+        checkImageColor(drawableOutput, "debug_overlay.png", GREEN);
+        checkImageColor(drawableOutput, "beta_overlay.png", GREEN);
+        checkImageColor(drawableOutput, "free_overlay.png", RED);
+        checkImageColor(drawableOutput, "free_beta_overlay.png", RED);
+        checkImageColor(drawableOutput, "free_beta_debug_overlay.png", RED);
+        checkImageColor(drawableOutput, "free_normal_overlay.png", RED);
     }
 
     public void testRepo() {
         File repo = new File(testDir, "repo");
 
         try {
-            runGradleTasks(sdkDir, BasePlugin.GRADLE_MIN_VERSION,
+            runGradleTasks(sdkDir, ndkDir, BasePlugin.GRADLE_MIN_VERSION,
                     new File(repo, "util"), "clean", "uploadArchives");
-            runGradleTasks(sdkDir, BasePlugin.GRADLE_MIN_VERSION,
+            runGradleTasks(sdkDir, ndkDir, BasePlugin.GRADLE_MIN_VERSION,
                     new File(repo, "baseLibrary"), "clean", "uploadArchives");
-            runGradleTasks(sdkDir, BasePlugin.GRADLE_MIN_VERSION,
+            runGradleTasks(sdkDir, ndkDir, BasePlugin.GRADLE_MIN_VERSION,
                     new File(repo, "library"), "clean", "uploadArchives");
-            runGradleTasks(sdkDir, BasePlugin.GRADLE_MIN_VERSION,
+            runGradleTasks(sdkDir, ndkDir, BasePlugin.GRADLE_MIN_VERSION,
                     new File(repo, "app"), "clean", "assemble");
         } finally {
             // clean up the test repository.
@@ -67,11 +108,34 @@
         }
     }
 
+    // test whether a library project has its fields ProGuarded
+    public void testLibProguard() throws Exception {
+        File project = new File(testDir, "libProguard");
+        File fileOutput = new File(project, "build/proguard/release");
+
+        runGradleTasks(sdkDir, ndkDir, BasePlugin.GRADLE_MIN_VERSION,
+          project, "clean", "build");
+        checkFile(fileOutput, "mapping.txt", new String[]{"int proguardInt -> a"});
+
+    }
+
+    // test whether proguard.txt has been correctly merged
+    public void testLibProguardConsumerFile() throws Exception {
+        File project = new File(testDir, "libProguardConsumerFiles");
+        File debugFileOutput = new File(project, "build/bundles/debug");
+        File releaseFileOutput = new File(project, "build/bundles/release");
+
+        runGradleTasks(sdkDir, ndkDir, BasePlugin.GRADLE_MIN_VERSION,
+            project, "clean", "build");
+        checkFile(debugFileOutput, "proguard.txt", new String[]{"A"});
+        checkFile(releaseFileOutput, "proguard.txt", new String[]{"A", "B", "C"});
+    }
+
     public void test3rdPartyTests() throws Exception {
         // custom because we want to run deviceCheck even without devices, since we use
         // a fake DeviceProvider that doesn't use a device, but only record the calls made
         // to the DeviceProvider and the DeviceConnector.
-        runGradleTasks(sdkDir, BasePlugin.GRADLE_MIN_VERSION,
+        runGradleTasks(sdkDir, ndkDir, BasePlugin.GRADLE_MIN_VERSION,
                 new File(testDir, "3rdPartyTests"), "clean", "deviceCheck");
     }
 
@@ -86,4 +150,16 @@
                 expectedColor, rgb, f),
                 expectedColor, rgb);
     }
+
+    private static void checkFile(File folder, String fileName, String[] expectedContents)
+            throws IOException {
+        File f = new File(folder, fileName);
+        assertTrue("File '" + f.getAbsolutePath() + "' does not exist.", f.isFile());
+
+        String contents = Files.toString(f, Charsets.UTF_8);
+        for (String expectedContent : expectedContents) {
+            assertTrue("File '" + f.getAbsolutePath() + "' does not contain: " + expectedContent,
+                contents.contains(expectedContent));
+        }
+    }
 }
diff --git a/gradle/src/device-test/groovy/com/android/build/gradle/DeviceTest.java b/gradle/src/device-test/groovy/com/android/build/gradle/DeviceTest.java
index 93b53b6..0e410f1 100644
--- a/gradle/src/device-test/groovy/com/android/build/gradle/DeviceTest.java
+++ b/gradle/src/device-test/groovy/com/android/build/gradle/DeviceTest.java
@@ -39,8 +39,9 @@
 
     private static final String[] sBuiltProjects = new String[] {
         "api", "assets", "applibtest", "attrOrder", "basic", "dependencies", "flavored",
-        "flavorlib", "flavors", "libTestDep", "libsTest", "migrated", "multires", "overlay1",
-        "overlay2", "pkgOverride", "proguard", "proguardLib", "sameNamedLibs"
+        "flavorlib", "flavors", "libProguardJarDep", "libProguardLibDep", "libTestDep", "libsTest",
+        "migrated", "multires", "ndkJniLib", "overlay1", "overlay2", "pkgOverride", "proguard",
+        "proguardLib", "sameNamedLibs"
     };
 
     public static Test suite() {
@@ -48,6 +49,9 @@
         suite.setName("DeviceTest");
 
         for (String gradleVersion : BasePlugin.GRADLE_SUPPORTED_VERSIONS) {
+            if (isIgnoredGradleVersion(gradleVersion)) {
+                continue;
+            }
             // first the project we build on all available versions of Gradle
             for (String projectName : sBuiltProjects) {
                 String testName = "check_" + projectName + "_" + gradleVersion;
diff --git a/gradle/src/fromGradle/groovy/com/android/build/gradle/internal/test/report/ClassPageRenderer.java b/gradle/src/fromGradle/groovy/com/android/build/gradle/internal/test/report/ClassPageRenderer.java
index 223ca76..ee31146 100644
--- a/gradle/src/fromGradle/groovy/com/android/build/gradle/internal/test/report/ClassPageRenderer.java
+++ b/gradle/src/fromGradle/groovy/com/android/build/gradle/internal/test/report/ClassPageRenderer.java
@@ -21,7 +21,7 @@
 import com.google.common.collect.Sets;
 import org.gradle.api.internal.ErroringAction;
 import org.gradle.api.internal.html.SimpleHtmlWriter;
-import org.gradle.api.internal.tasks.testing.junit.report.TestFailure;
+import org.gradle.api.internal.tasks.testing.junit.result.TestFailure;
 import org.gradle.reporting.CodePanelRenderer;
 
 import java.io.IOException;
diff --git a/gradle/src/fromGradle/groovy/com/android/build/gradle/internal/test/report/TestResult.java b/gradle/src/fromGradle/groovy/com/android/build/gradle/internal/test/report/TestResult.java
index a4435d5..90ecc8a 100644
--- a/gradle/src/fromGradle/groovy/com/android/build/gradle/internal/test/report/TestResult.java
+++ b/gradle/src/fromGradle/groovy/com/android/build/gradle/internal/test/report/TestResult.java
@@ -15,7 +15,7 @@
  */
 package com.android.build.gradle.internal.test.report;
 
-import org.gradle.api.internal.tasks.testing.junit.report.TestFailure;
+import org.gradle.api.internal.tasks.testing.junit.result.TestFailure;
 import org.gradle.api.internal.tasks.testing.junit.report.TestResultModel;
 
 import java.util.ArrayList;
@@ -101,7 +101,7 @@
     public void addFailure(String message, String stackTrace,
                            String deviceName, String projectName, String flavorName) {
         classResults.failed(this, deviceName, projectName, flavorName);
-        failures.add(new TestFailure(message, stackTrace));
+        failures.add(new TestFailure(message, stackTrace, null));
     }
 
     public void ignored() {
diff --git a/gradle/src/main/groovy/com/android/build/gradle/AppPlugin.groovy b/gradle/src/main/groovy/com/android/build/gradle/AppPlugin.groovy
index 073aa7b..32a13ad 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/AppPlugin.groovy
+++ b/gradle/src/main/groovy/com/android/build/gradle/AppPlugin.groovy
@@ -15,7 +15,6 @@
  */
 
 package com.android.build.gradle
-
 import com.android.annotations.NonNull
 import com.android.annotations.Nullable
 import com.android.build.gradle.api.BaseVariant
@@ -57,7 +56,6 @@
 import static com.android.builder.BuilderConstants.LINT
 import static com.android.builder.BuilderConstants.RELEASE
 import static com.android.builder.BuilderConstants.UI_TEST
-
 /**
  * Gradle plugin class for 'application' projects.
  */
@@ -141,7 +139,7 @@
             throw new RuntimeException("BuildType names cannot collide with ProductFlavor names")
         }
 
-        def sourceSet = extension.sourceSetsContainer.create(name)
+        def sourceSet = extension.sourceSetsContainer.maybeCreate(name)
 
         BuildTypeData buildTypeData = new BuildTypeData(buildType, sourceSet, project)
         project.tasks.assemble.dependsOn buildTypeData.assembleTask
@@ -163,9 +161,9 @@
             throw new RuntimeException("ProductFlavor names cannot collide with BuildType names")
         }
 
-        def mainSourceSet = (DefaultAndroidSourceSet) extension.sourceSetsContainer.create(productFlavor.name)
+        def mainSourceSet = (DefaultAndroidSourceSet) extension.sourceSetsContainer.maybeCreate(productFlavor.name)
         String testName = "${INSTRUMENT_TEST}${productFlavor.name.capitalize()}"
-        def testSourceSet = (DefaultAndroidSourceSet) extension.sourceSetsContainer.create(testName)
+        def testSourceSet = (DefaultAndroidSourceSet) extension.sourceSetsContainer.maybeCreate(testName)
 
         ProductFlavorData<GroupableProductFlavorDsl> productFlavorData =
                 new ProductFlavorData<GroupableProductFlavorDsl>(
@@ -232,6 +230,12 @@
             }
         }
 
+        // Add a compile lint task
+        createLintCompileTask()
+
+        // create the lint tasks.
+        createLintTasks()
+
         // create the test tasks.
         createCheckTasks(!productFlavors.isEmpty(), false /*isLibrary*/)
 
@@ -289,13 +293,16 @@
 
         for (BuildTypeData buildTypeData : buildTypes.values()) {
             def variantConfig = new VariantConfiguration(
-                    defaultConfigData.productFlavor, defaultConfigData.sourceSet,
-                    buildTypeData.buildType, buildTypeData.sourceSet, project.name)
+                    defaultConfigData.productFlavor,
+                    defaultConfigData.sourceSet,
+                    buildTypeData.buildType,
+                    buildTypeData.sourceSet)
 
             // create the variant and get its internal storage object.
             ApplicationVariantData appVariantData = new ApplicationVariantData(variantConfig)
             VariantDependencies variantDep = VariantDependencies.compute(
-                    project, appVariantData.name, buildTypeData, defaultConfigData.mainProvider)
+                    project, appVariantData.variantConfiguration.fullName,
+                    buildTypeData, defaultConfigData.mainProvider)
             appVariantData.setVariantDependency(variantDep)
 
             variantDataList.add(appVariantData)
@@ -309,10 +316,11 @@
 
         // handle the test variant
         def testVariantConfig = new VariantConfiguration(
-                defaultConfigData.productFlavor, defaultConfigData.testSourceSet,
-                testData.buildType, null,
-                VariantConfiguration.Type.TEST, testedVariantData.variantConfiguration,
-                project.name)
+                defaultConfigData.productFlavor,
+                defaultConfigData.testSourceSet,
+                testData.buildType,
+                null,
+                VariantConfiguration.Type.TEST, testedVariantData.variantConfiguration)
 
         // create the internal storage for this variant.
         def testVariantData = new TestVariantData(testVariantConfig, testedVariantData)
@@ -322,7 +330,8 @@
 
         // dependencies for the test variant
         VariantDependencies variantDep = VariantDependencies.compute(
-                project, testVariantData.name, defaultConfigData.testProvider)
+                project, testVariantData.variantConfiguration.fullName,
+                defaultConfigData.testProvider)
         testVariantData.setVariantDependency(variantDep)
 
         // now loop on the VariantDependency and resolve them, and create the tasks
@@ -423,11 +432,21 @@
             variantProviders.add(buildTypeData)
 
             VariantConfiguration variantConfig = new VariantConfiguration(
-                    extension.defaultConfig, getDefaultConfigData().sourceSet,
-                    buildTypeData.buildType, buildTypeData.sourceSet, project.name)
+                    extension.defaultConfig,
+                    getDefaultConfigData().sourceSet,
+                    buildTypeData.buildType,
+                    buildTypeData.sourceSet)
 
             for (ProductFlavorData data : flavorDataList) {
-                variantConfig.addProductFlavor(data.productFlavor, data.sourceSet)
+                String dimensionName = "";
+                if (data.productFlavor instanceof GroupableProductFlavorDsl) {
+                    dimensionName = ((GroupableProductFlavorDsl) data.productFlavor).flavorGroup
+                }
+                variantConfig.addProductFlavor(
+                        data.productFlavor,
+                        data.sourceSet,
+                        dimensionName
+                )
                 variantProviders.add(data.mainProvider)
             }
 
@@ -436,8 +455,21 @@
 
             // create the variant and get its internal storage object.
             ApplicationVariantData appVariantData = new ApplicationVariantData(variantConfig)
+
+            DefaultAndroidSourceSet variantSourceSet = (DefaultAndroidSourceSet) extension.sourceSetsContainer.maybeCreate(variantConfig.fullName)
+            variantConfig.setVariantSourceProvider(variantSourceSet)
+            // TODO: hmm this won't work
+            //variantProviders.add(new ConfigurationProviderImpl(project, variantSourceSet))
+
+            if (flavorDataList.size() > 1) {
+                DefaultAndroidSourceSet multiFlavorSourceSet = (DefaultAndroidSourceSet) extension.sourceSetsContainer.maybeCreate(variantConfig.flavorName)
+                variantConfig.setMultiFlavorSourceProvider(multiFlavorSourceSet)
+                // TODO: hmm this won't work
+                //variantProviders.add(new ConfigurationProviderImpl(project, multiFlavorSourceSet))
+            }
+
             VariantDependencies variantDep = VariantDependencies.compute(
-                    project, appVariantData.name,
+                    project, appVariantData.variantConfiguration.fullName,
                     variantProviders.toArray(new ConfigurationProvider[variantProviders.size()]))
             appVariantData.setVariantDependency(variantDep)
 
@@ -452,10 +484,12 @@
 
         // handle test variant
         VariantConfiguration testVariantConfig = new VariantConfiguration(
-                extension.defaultConfig, getDefaultConfigData().testSourceSet,
-                testData.buildType, null,
+                extension.defaultConfig,
+                getDefaultConfigData().testSourceSet,
+                testData.buildType,
+                null,
                 VariantConfiguration.Type.TEST,
-                testedVariantData.variantConfiguration, project.name)
+                testedVariantData.variantConfiguration)
 
         /// add the container of dependencies
         // the order of the libraries is important. In descending order:
@@ -463,7 +497,14 @@
         List<ConfigurationProvider> testVariantProviders = []
 
         for (ProductFlavorData data : flavorDataList) {
-            testVariantConfig.addProductFlavor(data.productFlavor, data.testSourceSet)
+            String dimensionName = "";
+            if (data.productFlavor instanceof GroupableProductFlavorDsl) {
+                dimensionName = ((GroupableProductFlavorDsl) data.productFlavor).flavorGroup
+            }
+            testVariantConfig.addProductFlavor(
+                    data.productFlavor,
+                    data.testSourceSet,
+                    dimensionName)
             testVariantProviders.add(data.testProvider)
         }
 
@@ -478,7 +519,7 @@
 
         // dependencies for the test variant
         VariantDependencies variantDep = VariantDependencies.compute(
-                project, testVariantData.name,
+                project, testVariantData.variantConfiguration.fullName,
                 testVariantProviders.toArray(new ConfigurationProvider[testVariantProviders.size()]))
         testVariantData.setVariantDependency(variantDep)
 
@@ -527,7 +568,7 @@
             @NonNull ApplicationVariantData variant,
             @Nullable Task assembleTask) {
 
-        createPrepareDependenciesTask(variant)
+        createAnchorTasks(variant)
 
         // Add a task to process the manifest(s)
         createProcessManifestTask(variant, "manifests")
@@ -555,6 +596,9 @@
         // Add a compile task
         createCompileTask(variant, null/*testedVariant*/)
 
+        // Add NDK tasks
+        createNdkTasks(variant)
+
         addPackageTasks(variant, assembleTask)
     }
 }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/BaseExtension.groovy b/gradle/src/main/groovy/com/android/build/gradle/BaseExtension.groovy
index e800753..974fe16 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/BaseExtension.groovy
+++ b/gradle/src/main/groovy/com/android/build/gradle/BaseExtension.groovy
@@ -17,9 +17,12 @@
 
 import com.android.SdkConstants
 import com.android.annotations.NonNull
+import com.android.annotations.Nullable
 import com.android.build.gradle.api.AndroidSourceSet
+import com.android.build.gradle.api.BaseVariant
 import com.android.build.gradle.api.TestVariant
 import com.android.build.gradle.internal.CompileOptions
+import com.android.build.gradle.internal.SourceSetSourceProviderWrapper
 import com.android.build.gradle.internal.dsl.AaptOptionsImpl
 import com.android.build.gradle.internal.dsl.AndroidSourceSetFactory
 import com.android.build.gradle.internal.dsl.DexOptionsImpl
@@ -27,6 +30,9 @@
 import com.android.build.gradle.internal.test.TestOptions
 import com.android.builder.BuilderConstants
 import com.android.builder.DefaultProductFlavor
+import com.android.builder.model.BuildType
+import com.android.builder.model.ProductFlavor
+import com.android.builder.model.SourceProvider
 import com.android.builder.testing.api.DeviceProvider
 import com.android.builder.testing.api.TestServer
 import com.android.sdklib.repository.FullRevision
@@ -38,8 +44,8 @@
 import org.gradle.api.artifacts.ConfigurationContainer
 import org.gradle.api.internal.DefaultDomainObjectSet
 import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.api.tasks.SourceSet
 import org.gradle.internal.reflect.Instantiator
-
 /**
  * Base android extension for all android plugins.
  */
@@ -72,7 +78,7 @@
         this.plugin = plugin
 
         defaultConfig = instantiator.newInstance(ProductFlavorDsl.class, BuilderConstants.MAIN,
-                project.fileResolver)
+                project.fileResolver, instantiator)
 
         aaptOptions = instantiator.newInstance(AaptOptionsImpl.class)
         dexOptions = instantiator.newInstance(DexOptionsImpl.class)
@@ -201,6 +207,49 @@
         testVariantList.add(testVariant)
     }
 
+    public void registerArtifactType(@NonNull String name,
+                                     boolean isTest,
+                                     int artifactType) {
+        plugin.registerArtifactType(name, isTest, artifactType)
+    }
+
+    public void registerBuildTypeSourceProvider(
+            @NonNull String name,
+            @NonNull BuildType buildType,
+            @NonNull SourceProvider sourceProvider) {
+        plugin.registerBuildTypeSourceProvider(name, buildType, sourceProvider)
+    }
+
+    public void registerProductFlavorSourceProvider(
+            @NonNull String name,
+            @NonNull ProductFlavor productFlavor,
+            @NonNull SourceProvider sourceProvider) {
+        plugin.registerProductFlavorSourceProvider(name, productFlavor, sourceProvider)
+    }
+
+    public void registerJavaArtifact(
+            @NonNull String name,
+            @NonNull BaseVariant variant,
+            @NonNull String assembleTaskName,
+            @NonNull String javaCompileTaskName,
+            @NonNull File classesFolder,
+            @Nullable SourceProvider sourceProvider) {
+        plugin.registerJavaArtifact(name, variant, assembleTaskName, javaCompileTaskName,
+                classesFolder, sourceProvider)
+    }
+
+    public void registerMultiFlavorSourceProvider(
+            @NonNull String name,
+            @NonNull String flavorName,
+            @NonNull SourceProvider sourceProvider) {
+        plugin.registerMultiFlavorSourceProvider(name, flavorName, sourceProvider)
+    }
+
+    @NonNull
+    public SourceProvider wrapJavaSourceSet(@NonNull SourceSet sourceSet) {
+        return new SourceSetSourceProviderWrapper(sourceSet)
+    }
+
     public String getCompileSdkVersion() {
         return target
     }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/BasePlugin.groovy b/gradle/src/main/groovy/com/android/build/gradle/BasePlugin.groovy
index e85c784..1e5fb45 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/BasePlugin.groovy
+++ b/gradle/src/main/groovy/com/android/build/gradle/BasePlugin.groovy
@@ -19,6 +19,7 @@
 import com.android.annotations.NonNull
 import com.android.annotations.Nullable
 import com.android.build.gradle.api.AndroidSourceSet
+import com.android.build.gradle.api.BaseVariant
 import com.android.build.gradle.internal.BadPluginException
 import com.android.build.gradle.internal.LoggerWrapper
 import com.android.build.gradle.internal.ProductFlavorData
@@ -30,6 +31,8 @@
 import com.android.build.gradle.internal.dependency.SymbolFileProviderImpl
 import com.android.build.gradle.internal.dependency.VariantDependencies
 import com.android.build.gradle.internal.dsl.SigningConfigDsl
+import com.android.build.gradle.internal.model.ArtifactMetaDataImpl
+import com.android.build.gradle.internal.model.JavaArtifactImpl
 import com.android.build.gradle.internal.model.ModelBuilder
 import com.android.build.gradle.internal.tasks.AndroidReportTask
 import com.android.build.gradle.internal.tasks.DependencyReportTask
@@ -47,15 +50,19 @@
 import com.android.build.gradle.internal.variant.ApkVariantData
 import com.android.build.gradle.internal.variant.ApplicationVariantData
 import com.android.build.gradle.internal.variant.BaseVariantData
+import com.android.build.gradle.internal.variant.DefaultSourceProviderContainer
 import com.android.build.gradle.internal.variant.LibraryVariantData
 import com.android.build.gradle.internal.variant.TestVariantData
 import com.android.build.gradle.internal.variant.TestedVariantData
 import com.android.build.gradle.tasks.AidlCompile
 import com.android.build.gradle.tasks.Dex
 import com.android.build.gradle.tasks.GenerateBuildConfig
+import com.android.build.gradle.tasks.Lint
 import com.android.build.gradle.tasks.MergeAssets
 import com.android.build.gradle.tasks.MergeResources
+import com.android.build.gradle.tasks.NdkCompile
 import com.android.build.gradle.tasks.PackageApplication
+import com.android.build.gradle.tasks.PreDex
 import com.android.build.gradle.tasks.ProcessAndroidResources
 import com.android.build.gradle.tasks.ProcessAppManifest
 import com.android.build.gradle.tasks.ProcessTestManifest
@@ -67,17 +74,25 @@
 import com.android.builder.VariantConfiguration
 import com.android.builder.dependency.JarDependency
 import com.android.builder.dependency.LibraryDependency
+import com.android.builder.model.AndroidArtifact
+import com.android.builder.model.AndroidProject
+import com.android.builder.model.ArtifactMetaData
+import com.android.builder.model.BuildType
+import com.android.builder.model.JavaArtifact
 import com.android.builder.model.ProductFlavor
 import com.android.builder.model.SigningConfig
 import com.android.builder.model.SourceProvider
+import com.android.builder.model.SourceProviderContainer
 import com.android.builder.testing.ConnectedDeviceProvider
 import com.android.builder.testing.api.DeviceProvider
 import com.android.builder.testing.api.TestServer
 import com.android.utils.ILogger
 import com.google.common.collect.ArrayListMultimap
+import com.google.common.collect.ListMultimap
 import com.google.common.collect.Lists
 import com.google.common.collect.Maps
 import com.google.common.collect.Multimap
+import com.google.common.collect.Sets
 import org.gradle.api.DefaultTask
 import org.gradle.api.GradleException
 import org.gradle.api.Project
@@ -87,11 +102,14 @@
 import org.gradle.api.artifacts.ProjectDependency
 import org.gradle.api.artifacts.ResolvedArtifact
 import org.gradle.api.artifacts.SelfResolvingDependency
+import org.gradle.api.artifacts.result.DependencyResult
 import org.gradle.api.artifacts.result.ResolvedDependencyResult
 import org.gradle.api.artifacts.result.ResolvedModuleVersionResult
+import org.gradle.api.artifacts.result.UnresolvedDependencyResult
 import org.gradle.api.logging.LogLevel
 import org.gradle.api.plugins.JavaBasePlugin
 import org.gradle.api.plugins.JavaPlugin
+import org.gradle.api.specs.Specs
 import org.gradle.api.tasks.Copy
 import org.gradle.api.tasks.compile.JavaCompile
 import org.gradle.internal.reflect.Instantiator
@@ -113,13 +131,16 @@
 import static com.android.builder.BuilderConstants.FD_INSTRUMENT_TESTS
 import static com.android.builder.BuilderConstants.FD_REPORTS
 import static com.android.builder.BuilderConstants.INSTRUMENT_TEST
+import static java.io.File.separator
 
 /**
  * Base class for all Android plugins
  */
 public abstract class BasePlugin {
-    public static final String GRADLE_MIN_VERSION = "1.6"
-    public static final String[] GRADLE_SUPPORTED_VERSIONS = [ GRADLE_MIN_VERSION, "1.7" ]
+    protected final static String DIR_BUNDLES = "bundles";
+
+    public static final String GRADLE_MIN_VERSION = "1.9"
+    public static final String[] GRADLE_SUPPORTED_VERSIONS = [ GRADLE_MIN_VERSION ]
 
     public static final String INSTALL_GROUP = "Install"
 
@@ -142,13 +163,18 @@
     private boolean hasCreatedTasks = false
 
     private ProductFlavorData<DefaultProductFlavor> defaultConfigData
+    private final Collection<String> unresolvedDependencies = Sets.newHashSet();
+
     protected DefaultAndroidSourceSet mainSourceSet
     protected DefaultAndroidSourceSet testSourceSet
 
+    protected Task mainPreBuild
     protected Task uninstallAll
     protected Task assembleTest
     protected Task deviceCheck
     protected Task connectedCheck
+    protected Task lint
+    protected Task lintCompile
 
     protected BasePlugin(Instantiator instantiator, ToolingModelBuilderRegistry registry) {
         this.instantiator = instantiator
@@ -190,6 +216,21 @@
         connectedCheck.description = "Runs all device checks on currently connected devices."
         connectedCheck.group = JavaBasePlugin.VERIFICATION_GROUP
 
+        mainPreBuild = project.tasks.create("preBuild")
+
+        lint = project.tasks.create("lint", Lint)
+        lint.description = "Runs lint on all variants."
+        lint.group = JavaBasePlugin.VERIFICATION_GROUP
+        lint.setPlugin(this)
+        int count = variantDataList.size()
+        for (int i = 0 ; i < count ; i++) {
+            final BaseVariantData baseVariantData = variantDataList.get(i)
+            if (isLintVariant(baseVariantData)) {
+                lint.dependsOn baseVariantData.javaCompileTask
+            }
+        }
+        project.tasks.check.dependsOn lint
+
         project.afterEvaluate {
             createAndroidTasks(false)
         }
@@ -215,10 +256,15 @@
         }
 
         if (!foundMatch) {
+            File file = new File("gradle" + separator + "wrapper" + separator +
+                    "gradle-wrapper.properties");
             throw new BuildException(
-                    String.format(
-                            "Gradle version %s is required. Current version is %s",
-                            GRADLE_MIN_VERSION, project.getGradle().gradleVersion), null);
+                String.format(
+                    "Gradle version %s is required. Current version is %s. " +
+                    "If using the gradle wrapper, try editing the distributionUrl in %s " +
+                    "to gradle-%s-all.zip",
+                    GRADLE_MIN_VERSION, project.getGradle().gradleVersion, file.getAbsolutePath(),
+                    GRADLE_MIN_VERSION), null);
 
         }
     }
@@ -263,6 +309,10 @@
         return defaultConfigData
     }
 
+    Collection<String> getUnresolvedDependencies() {
+        return unresolvedDependencies
+    }
+
     SdkParser getSdkParser() {
         return sdk.parser
     }
@@ -272,7 +322,11 @@
     }
 
     File getSdkDirectory() {
-        return sdk.directory
+        return sdk.sdkDirectory
+    }
+
+    File getNdkDirectory() {
+        return sdk.ndkDirectory
     }
 
     ILogger getLogger() {
@@ -293,6 +347,9 @@
         if (androidBuilder == null) {
             SdkParser parser = getLoadedSdkParser()
             androidBuilder = new AndroidBuilder(parser, creator, logger, verbose)
+            if (this instanceof LibraryPlugin) {
+                androidBuilder.setBuildingLibrary(true);
+            }
 
             builders.put(variantData, androidBuilder)
         }
@@ -312,7 +369,8 @@
 
     protected void createProcessManifestTask(BaseVariantData variantData,
                                              String manifestOurDir) {
-        def processManifestTask = project.tasks.create("process${variantData.name}Manifest",
+        def processManifestTask = project.tasks.create(
+                "process${variantData.variantConfiguration.fullName.capitalize()}Manifest",
                 ProcessAppManifest)
         variantData.processManifestTask = processManifestTask
         processManifestTask.dependsOn variantData.prepareDependenciesTask
@@ -339,7 +397,7 @@
             getManifestDependencies(config.directLibraries)
         }
         processManifestTask.conventionMapping.versionCode = {
-            mergedFlavor.versionCode
+            config.versionCode
         }
         processManifestTask.conventionMapping.minSdkVersion = {
             mergedFlavor.minSdkVersion
@@ -349,13 +407,14 @@
         }
         processManifestTask.conventionMapping.manifestOutputFile = {
             project.file(
-                    "$project.buildDir/${manifestOurDir}/$variantData.dirName/AndroidManifest.xml")
+                    "$project.buildDir/${manifestOurDir}/${variantData.variantConfiguration.dirName}/AndroidManifest.xml")
         }
     }
 
     protected void createProcessTestManifestTask(BaseVariantData variantData,
                                                  String manifestOurDir) {
-        def processTestManifestTask = project.tasks.create("process${variantData.name}TestManifest",
+        def processTestManifestTask = project.tasks.create(
+                "process${variantData.variantConfiguration.fullName.capitalize()}Manifest",
                 ProcessTestManifest)
         variantData.processManifestTask = processTestManifestTask
         processTestManifestTask.dependsOn variantData.prepareDependenciesTask
@@ -380,27 +439,44 @@
         processTestManifestTask.conventionMapping.instrumentationRunner = {
             config.instrumentationRunner
         }
+        processTestManifestTask.conventionMapping.handleProfiling = {
+            config.handleProfiling
+        }
+        processTestManifestTask.conventionMapping.functionalTest = {
+            config.functionalTest
+        }
         processTestManifestTask.conventionMapping.libraries = {
             getManifestDependencies(config.directLibraries)
         }
         processTestManifestTask.conventionMapping.manifestOutputFile = {
             project.file(
-                    "$project.buildDir/${manifestOurDir}/$variantData.dirName/AndroidManifest.xml")
+                    "$project.buildDir/${manifestOurDir}/${variantData.variantConfiguration.dirName}/AndroidManifest.xml")
         }
     }
 
     protected void createRenderscriptTask(BaseVariantData variantData) {
         VariantConfiguration config = variantData.variantConfiguration
 
-        def renderscriptTask = project.tasks.create("compile${variantData.name}Renderscript",
+        def renderscriptTask = project.tasks.create(
+                "compile${variantData.variantConfiguration.fullName.capitalize()}Renderscript",
                 RenderscriptCompile)
         variantData.renderscriptCompileTask = renderscriptTask
 
+        ProductFlavor mergedFlavor = config.mergedFlavor
+        boolean ndkMode = mergedFlavor.renderscriptNdkMode
+
+        // only put this dependency if rs will generate Java code
+        if (!ndkMode) {
+            variantData.sourceGenTask.dependsOn renderscriptTask
+        }
+
         renderscriptTask.dependsOn variantData.prepareDependenciesTask
         renderscriptTask.plugin = this
         renderscriptTask.variant = variantData
 
-        renderscriptTask.targetApi = config.mergedFlavor.renderscriptTargetApi
+        renderscriptTask.targetApi = mergedFlavor.renderscriptTargetApi
+        renderscriptTask.supportMode = mergedFlavor.renderscriptSupportMode
+        renderscriptTask.ndkMode = ndkMode
         renderscriptTask.debugBuild = config.buildType.renderscriptDebugBuild
         renderscriptTask.optimLevel = config.buildType.renderscriptOptimLevel
 
@@ -408,11 +484,18 @@
         renderscriptTask.conventionMapping.importDirs = { config.renderscriptImports }
 
         renderscriptTask.conventionMapping.sourceOutputDir = {
-            project.file("$project.buildDir/source/rs/$variantData.dirName")
+            project.file("$project.buildDir/source/rs/${variantData.variantConfiguration.dirName}")
         }
         renderscriptTask.conventionMapping.resOutputDir = {
-            project.file("$project.buildDir/res/rs/$variantData.dirName")
+            project.file("$project.buildDir/res/rs/${variantData.variantConfiguration.dirName}")
         }
+        renderscriptTask.conventionMapping.objOutputDir = {
+            project.file("$project.buildDir/rs/${variantData.variantConfiguration.dirName}/obj")
+        }
+        renderscriptTask.conventionMapping.libOutputDir = {
+            project.file("$project.buildDir/rs/${variantData.variantConfiguration.dirName}/lib")
+        }
+        renderscriptTask.conventionMapping.ndkConfig = { config.ndkConfig }
     }
 
     protected void createMergeResourcesTask(@NonNull BaseVariantData variantData,
@@ -420,7 +503,7 @@
         MergeResources mergeResourcesTask = basicCreateMergeResourcesTask(
                 variantData,
                 "merge",
-                "$project.buildDir/res/all/$variantData.dirName",
+                "$project.buildDir/res/all/${variantData.variantConfiguration.dirName}",
                 true /*includeDependencies*/,
                 process9Patch)
         variantData.mergeResourcesTask = mergeResourcesTask
@@ -433,13 +516,14 @@
             final boolean includeDependencies,
             final boolean process9Patch) {
         MergeResources mergeResourcesTask = project.tasks.create(
-                "$taskNamePrefix${variantData.name}Resources", MergeResources)
+                "$taskNamePrefix${variantData.variantConfiguration.fullName.capitalize()}Resources",
+                MergeResources)
 
         mergeResourcesTask.dependsOn variantData.prepareDependenciesTask, variantData.renderscriptCompileTask
         mergeResourcesTask.plugin = this
         mergeResourcesTask.variant = variantData
         mergeResourcesTask.incrementalFolder = project.file(
-                "$project.buildDir/incremental/${taskNamePrefix}Resources/$variantData.dirName")
+                "$project.buildDir/incremental/${taskNamePrefix}Resources/${variantData.variantConfiguration.dirName}")
 
         mergeResourcesTask.process9Patch = process9Patch
 
@@ -458,17 +542,19 @@
                                          @Nullable String outputLocation,
                                          final boolean includeDependencies) {
         if (outputLocation == null) {
-            outputLocation = "$project.buildDir/assets/$variantData.dirName"
+            outputLocation = "$project.buildDir/assets/${variantData.variantConfiguration.dirName}"
         }
 
-        def mergeAssetsTask = project.tasks.create("merge${variantData.name}Assets", MergeAssets)
+        def mergeAssetsTask = project.tasks.create(
+                "merge${variantData.variantConfiguration.fullName.capitalize()}Assets",
+                MergeAssets)
         variantData.mergeAssetsTask = mergeAssetsTask
 
         mergeAssetsTask.dependsOn variantData.prepareDependenciesTask
         mergeAssetsTask.plugin = this
         mergeAssetsTask.variant = variantData
         mergeAssetsTask.incrementalFolder =
-            project.file("$project.buildDir/incremental/mergeAssets/$variantData.dirName")
+                project.file("$project.buildDir/incremental/mergeAssets/${variantData.variantConfiguration.dirName}")
 
         mergeAssetsTask.conventionMapping.inputAssetSets = {
             variantData.variantConfiguration.getAssetSets(includeDependencies)
@@ -478,11 +564,13 @@
 
     protected void createBuildConfigTask(BaseVariantData variantData) {
         def generateBuildConfigTask = project.tasks.create(
-                "generate${variantData.name}BuildConfig", GenerateBuildConfig)
+                "generate${variantData.variantConfiguration.fullName.capitalize()}BuildConfig",
+                GenerateBuildConfig)
         variantData.generateBuildConfigTask = generateBuildConfigTask
 
         VariantConfiguration variantConfiguration = variantData.variantConfiguration
 
+        variantData.sourceGenTask.dependsOn generateBuildConfigTask
         if (variantConfiguration.type == VariantConfiguration.Type.TEST) {
             // in case of a test project, the manifest is generated so we need to depend
             // on its creation.
@@ -492,31 +580,58 @@
         generateBuildConfigTask.plugin = this
         generateBuildConfigTask.variant = variantData
 
-        generateBuildConfigTask.conventionMapping.packageName = {
+        generateBuildConfigTask.conventionMapping.buildConfigPackageName = {
             variantConfiguration.originalPackageName
         }
 
+        generateBuildConfigTask.conventionMapping.appPackageName = {
+            variantConfiguration.packageName
+        }
+
+        generateBuildConfigTask.conventionMapping.versionName = {
+            variantConfiguration.versionName
+        }
+
+        generateBuildConfigTask.conventionMapping.versionCode = {
+            variantConfiguration.versionCode
+        }
+
         generateBuildConfigTask.conventionMapping.debuggable = {
             variantConfiguration.buildType.isDebuggable()
         }
 
-        generateBuildConfigTask.conventionMapping.javaLines = {
-            variantConfiguration.buildConfigLines
+        generateBuildConfigTask.conventionMapping.buildTypeName = {
+            variantConfiguration.buildType.name
+        }
+
+        generateBuildConfigTask.conventionMapping.flavorName = {
+            variantConfiguration.flavorName
+        }
+
+        generateBuildConfigTask.conventionMapping.flavorNamesWithDimensionNames = {
+            variantConfiguration.flavorNamesWithDimensionNames
+        }
+
+        generateBuildConfigTask.conventionMapping.items = {
+            variantConfiguration.buildConfigItems
         }
 
         generateBuildConfigTask.conventionMapping.sourceOutputDir = {
-            project.file("$project.buildDir/source/buildConfig/${variantData.dirName}")
+            project.file("$project.buildDir/source/buildConfig/${variantData.variantConfiguration.dirName}")
         }
     }
 
     protected void createProcessResTask(BaseVariantData variantData) {
-        createProcessResTask(variantData, "$project.buildDir/symbols/$variantData.dirName")
+        createProcessResTask(variantData, "$project.buildDir/symbols/${variantData.variantConfiguration.dirName}")
     }
 
     protected void createProcessResTask(BaseVariantData variantData, final String symbolLocation) {
-        def processResources = project.tasks.create("process${variantData.name}Resources",
+        def processResources = project.tasks.create(
+                "process${variantData.variantConfiguration.fullName.capitalize()}Resources",
                 ProcessAndroidResources)
         variantData.processResourcesTask = processResources
+
+        variantData.sourceGenTask.dependsOn processResources
         processResources.dependsOn variantData.processManifestTask, variantData.mergeResourcesTask, variantData.mergeAssetsTask
 
         processResources.plugin = this
@@ -545,32 +660,34 @@
 
         // TODO: unify with generateBuilderConfig, compileAidl, and library packaging somehow?
         processResources.conventionMapping.sourceOutputDir = {
-            project.file("$project.buildDir/source/r/$variantData.dirName")
+            project.file("$project.buildDir/source/r/${variantData.variantConfiguration.dirName}")
         }
         processResources.conventionMapping.textSymbolOutputDir = {
             project.file(symbolLocation)
         }
         processResources.conventionMapping.packageOutputFile = {
             project.file(
-                    "$project.buildDir/libs/${project.archivesBaseName}-${variantData.baseName}.ap_")
+                    "$project.buildDir/libs/${project.archivesBaseName}-${variantData.variantConfiguration.baseName}.ap_")
         }
-        if (variantData.runProguard) {
+        if (variantConfiguration.buildType.runProguard) {
             processResources.conventionMapping.proguardOutputFile = {
-                project.file("$project.buildDir/proguard/${variantData.dirName}/aapt_rules.txt")
+                project.file("$project.buildDir/proguard/${variantData.variantConfiguration.dirName}/aapt_rules.txt")
             }
         }
 
         processResources.conventionMapping.type = { variantConfiguration.type }
         processResources.conventionMapping.debuggable = { variantConfiguration.buildType.debuggable }
         processResources.conventionMapping.aaptOptions = { extension.aaptOptions }
+        processResources.conventionMapping.resourceConfigs = { variantConfiguration.mergedFlavor.resourceConfigurations }
     }
 
     protected void createProcessJavaResTask(BaseVariantData variantData) {
         VariantConfiguration variantConfiguration = variantData.variantConfiguration
 
-        Copy processResources = project.tasks.create("process${variantData.name}JavaRes",
+        Copy processResources = project.tasks.create(
+                "process${variantData.variantConfiguration.fullName.capitalize()}JavaRes",
                 ProcessResources);
-        variantData.processJavaResources = processResources
+        variantData.processJavaResourcesTask = processResources
 
         // set the input
         processResources.from(((AndroidSourceSet) variantConfiguration.defaultSourceSet).resources)
@@ -580,58 +697,65 @@
                     ((AndroidSourceSet) variantConfiguration.buildTypeSourceSet).resources)
         }
         if (variantConfiguration.hasFlavors()) {
-            for (SourceProvider flavorSourceSet : variantConfiguration.flavorSourceSets) {
+            for (SourceProvider flavorSourceSet : variantConfiguration.flavorSourceProviders) {
                 processResources.from(((AndroidSourceSet) flavorSourceSet).resources)
             }
         }
 
         processResources.conventionMapping.destinationDir = {
-            project.file("$project.buildDir/javaResources/$variantData.dirName")
+            project.file("$project.buildDir/javaResources/${variantData.variantConfiguration.dirName}")
         }
     }
 
     protected void createAidlTask(BaseVariantData variantData) {
         VariantConfiguration variantConfiguration = variantData.variantConfiguration
 
-        def compileTask = project.tasks.create("compile${variantData.name}Aidl", AidlCompile)
+        def compileTask = project.tasks.create(
+                "compile${variantData.variantConfiguration.fullName.capitalize()}Aidl",
+                AidlCompile)
         variantData.aidlCompileTask = compileTask
+
+        variantData.sourceGenTask.dependsOn compileTask
         variantData.aidlCompileTask.dependsOn variantData.prepareDependenciesTask
 
         compileTask.plugin = this
         compileTask.variant = variantData
         compileTask.incrementalFolder =
-            project.file("$project.buildDir/incremental/aidl/$variantData.dirName")
-
+                project.file("$project.buildDir/incremental/aidl/${variantData.variantConfiguration.dirName}")
 
         compileTask.conventionMapping.sourceDirs = { variantConfiguration.aidlSourceList }
         compileTask.conventionMapping.importDirs = { variantConfiguration.aidlImports }
 
         compileTask.conventionMapping.sourceOutputDir = {
-            project.file("$project.buildDir/source/aidl/$variantData.dirName")
+            project.file("$project.buildDir/source/aidl/${variantData.variantConfiguration.dirName}")
         }
     }
 
     protected void createCompileTask(BaseVariantData variantData,
                                      BaseVariantData testedVariantData) {
-        def compileTask = project.tasks.create("compile${variantData.name}", JavaCompile)
+        def compileTask = project.tasks.create(
+                "compile${variantData.variantConfiguration.fullName.capitalize()}Java",
+                JavaCompile)
         variantData.javaCompileTask = compileTask
-        compileTask.dependsOn variantData.processResourcesTask, variantData.generateBuildConfigTask, variantData.aidlCompileTask
+        compileTask.dependsOn variantData.sourceGenTask
 
         VariantConfiguration config = variantData.variantConfiguration
 
-        List<Object> sourceList = new ArrayList<Object>();
+        List<Object> sourceList = Lists.newArrayList()
         sourceList.add(((AndroidSourceSet) config.defaultSourceSet).java)
         sourceList.add({ variantData.processResourcesTask.sourceOutputDir })
         sourceList.add({ variantData.generateBuildConfigTask.sourceOutputDir })
         sourceList.add({ variantData.aidlCompileTask.sourceOutputDir })
-        sourceList.add({ variantData.renderscriptCompileTask.sourceOutputDir })
+        if (!config.mergedFlavor.renderscriptNdkMode) {
+            sourceList.add({ variantData.renderscriptCompileTask.sourceOutputDir })
+        }
 
         if (config.getType() != VariantConfiguration.Type.TEST) {
             sourceList.add(((AndroidSourceSet) config.buildTypeSourceSet).java)
         }
         if (config.hasFlavors()) {
-            for (SourceProvider flavorSourceSet : config.flavorSourceSets) {
-                sourceList.add(((AndroidSourceSet) flavorSourceSet).java)
+            for (SourceProvider flavorSourceProvider : config.flavorSourceProviders) {
+                sourceList.add(((AndroidSourceSet) flavorSourceProvider).java)
             }
         }
         compileTask.source = sourceList.toArray()
@@ -641,11 +765,11 @@
         // dependency.
         if (testedVariantData instanceof ApplicationVariantData) {
             compileTask.conventionMapping.classpath =  {
-                project.files(config.compileClasspath) + testedVariantData.javaCompileTask.classpath + testedVariantData.javaCompileTask.outputs.files
+                project.files(getAndroidBuilder(variantData).getCompileClasspath(config)) + testedVariantData.javaCompileTask.classpath + testedVariantData.javaCompileTask.outputs.files
             }
         } else {
             compileTask.conventionMapping.classpath =  {
-                project.files(config.compileClasspath)
+                project.files(getAndroidBuilder(variantData).getCompileClasspath(config))
             }
         }
 
@@ -654,10 +778,10 @@
         compileTask.dependsOn project.configurations.compile.buildDependencies
 
         compileTask.conventionMapping.destinationDir = {
-            project.file("$project.buildDir/classes/$variantData.dirName")
+            project.file("$project.buildDir/classes/${variantData.variantConfiguration.dirName}")
         }
         compileTask.conventionMapping.dependencyCacheDir = {
-            project.file("$project.buildDir/dependency-cache/$variantData.dirName")
+            project.file("$project.buildDir/dependency-cache/${variantData.variantConfiguration.dirName}")
         }
 
         // set source/target compatibility
@@ -667,6 +791,7 @@
         compileTask.conventionMapping.targetCompatibility = {
             extension.compileOptions.targetCompatibility.toString()
         }
+        compileTask.options.encoding = extension.compileOptions.encoding
 
         // setup the boot classpath just before the task actually runs since this will
         // force the sdk to be parsed.
@@ -675,6 +800,57 @@
         }
     }
 
+    protected void createNdkTasks(@NonNull BaseVariantData variantData) {
+        createNdkTasks(
+                variantData,
+                { project.file("$project.buildDir/ndk/${variantData.variantConfiguration.dirName}/lib") }
+        )
+    }
+
+    protected void createNdkTasks(@NonNull BaseVariantData variantData,
+                                  @NonNull Closure<File> soFolderClosure) {
+        NdkCompile ndkCompile = project.tasks.create(
+                "compile${variantData.variantConfiguration.fullName.capitalize()}Ndk",
+                NdkCompile)
+
+        ndkCompile.plugin = this
+        ndkCompile.variant = variantData
+        variantData.ndkCompileTask = ndkCompile
+
+        VariantConfiguration variantConfig = variantData.variantConfiguration
+
+        if (variantConfig.mergedFlavor.renderscriptNdkMode) {
+            ndkCompile.ndkRenderScriptMode = true
+            ndkCompile.dependsOn variantData.renderscriptCompileTask
+        } else {
+            ndkCompile.ndkRenderScriptMode = false
+        }
+
+        ndkCompile.conventionMapping.sourceFolders = {
+            List<File> sourceList = variantConfig.jniSourceList
+            if (variantConfig.mergedFlavor.renderscriptNdkMode) {
+                sourceList.add(variantData.renderscriptCompileTask.sourceOutputDir)
+            }
+
+            return sourceList
+        }
+
+        ndkCompile.conventionMapping.generatedMakefile = {
+            project.file("$project.buildDir/ndk/${variantData.variantConfiguration.dirName}/Android.mk")
+        }
+
+        ndkCompile.conventionMapping.ndkConfig = { variantConfig.ndkConfig }
+
+        ndkCompile.conventionMapping.debuggable = {
+            variantConfig.buildType.jniDebugBuild
+        }
+
+        ndkCompile.conventionMapping.objFolder = {
+            project.file("$project.buildDir/ndk/${variantData.variantConfiguration.dirName}/obj")
+        }
+        ndkCompile.conventionMapping.soFolder = soFolderClosure
+    }
+
     /**
      * Creates the tasks to build the test apk.
      *
@@ -688,10 +864,10 @@
         // to test both.
         if (!variantData.isSigned()) {
             throw new GradleException(
-                    "Tested Variant '${testedVariantData.name}' is not configured to create a signed APK.")
+                    "Tested Variant '${testedVariantData.variantConfiguration.fullName}' is not configured to create a signed APK.")
         }
 
-        createPrepareDependenciesTask(variantData)
+        createAnchorTasks(variantData)
 
         // Add a task to process the manifest
         createProcessTestManifestTask(variantData, "manifests")
@@ -727,6 +903,9 @@
         // Add a task to compile the test application
         createCompileTask(variantData, testedVariantData)
 
+        // Add NDK tasks
+        createNdkTasks(variantData)
+
         addPackageTasks(variantData, null)
 
         if (assembleTest != null) {
@@ -734,6 +913,54 @@
         }
     }
 
+    // TODO - should compile src/lint/java from src/lint/java and jar it into build/lint/lint.jar
+    protected void createLintCompileTask() {
+        lintCompile = project.tasks.create("compileLint", Task)
+        File outputDir = new File("$project.buildDir/lint")
+
+        lintCompile.doFirst{
+            // create the directory for lint output if it does not exist.
+            if (!outputDir.exists()) {
+                boolean mkdirs = outputDir.mkdirs();
+                if (!mkdirs) {
+                    throw new GradleException("Unable to create lint output directory.")
+                }
+            }
+        }
+    }
+
+    /** Is the given variant relevant for lint? */
+    private static boolean isLintVariant(@NonNull BaseVariantData baseVariantData) {
+        // Only create lint targets for variants like debug and release, not debugTest
+        VariantConfiguration config = baseVariantData.variantConfiguration
+        return config.getType() != VariantConfiguration.Type.TEST;
+    }
+
+    // Add tasks for running lint on individual variants. We've already added a
+    // lint task earlier which runs on all variants.
+    protected void createLintTasks() {
+        int count = variantDataList.size()
+        for (int i = 0 ; i < count ; i++) {
+            final BaseVariantData baseVariantData = variantDataList.get(i)
+            if (!isLintVariant(baseVariantData)) {
+                continue;
+            }
+
+            String variantName = baseVariantData.variantConfiguration.fullName
+            def capitalizedVariantName = variantName.capitalize()
+            Task lintCheck = project.tasks.create("lint" + capitalizedVariantName, Lint)
+            lintCheck.dependsOn baseVariantData.javaCompileTask, lintCompile
+            // Note that we don't do "lint.dependsOn lintCheck"; the "lint" target will
+            // on its own run through all variants (and compare results), it doesn't delegate
+            // to the individual tasks (since it needs to coordinate data collection and
+            // reporting)
+            lintCheck.setPlugin(this)
+            lintCheck.setVariantName(variantName)
+            lintCheck.description = "Runs lint on the " + capitalizedVariantName + " build"
+            lintCheck.group = JavaBasePlugin.VERIFICATION_GROUP
+        }
+    }
+
     protected void createCheckTasks(boolean hasFlavors, boolean isLibraryTest) {
         List<AndroidReportTask> reportTasks = Lists.newArrayListWithExpectedSize(2)
 
@@ -813,8 +1040,8 @@
                 // first the connected one.
                 def connectedTask = createDeviceProviderInstrumentTestTask(
                         hasFlavors ?
-                            "${connectedRootName}${baseVariantData.name}" : connectedRootName,
-                        "Installs and runs the tests for Build '${baseVariantData.name}' on connected devices.",
+                            "${connectedRootName}${baseVariantData.variantConfiguration.fullName.capitalize()}" : connectedRootName,
+                        "Installs and runs the tests for Build '${baseVariantData.variantConfiguration.fullName}' on connected devices.",
                         isLibraryTest ?
                             DeviceProviderInstrumentTestLibraryTask :
                             DeviceProviderInstrumentTestTask,
@@ -831,9 +1058,9 @@
                 for (DeviceProvider deviceProvider : providers) {
                     DefaultTask providerTask = createDeviceProviderInstrumentTestTask(
                             hasFlavors ?
-                                "${deviceProvider.name}${INSTRUMENT_TEST.capitalize()}${baseVariantData.name}" :
+                                "${deviceProvider.name}${INSTRUMENT_TEST.capitalize()}${baseVariantData.variantConfiguration.fullName.capitalize()}" :
                                 "${deviceProvider.name}${INSTRUMENT_TEST.capitalize()}",
-                            "Installs and runs the tests for Build '${baseVariantData.name}' using Provider '${deviceProvider.name.capitalize()}'.",
+                            "Installs and runs the tests for Build '${baseVariantData.variantConfiguration.fullName}' using Provider '${deviceProvider.name.capitalize()}'.",
                             isLibraryTest ?
                                 DeviceProviderInstrumentTestLibraryTask :
                                 DeviceProviderInstrumentTestTask,
@@ -856,10 +1083,10 @@
                 for (TestServer testServer : servers) {
                     DefaultTask serverTask = project.tasks.create(
                             hasFlavors ?
-                                "${testServer.name}${"upload".capitalize()}${baseVariantData.name}" :
+                                "${testServer.name}${"upload".capitalize()}${baseVariantData.variantConfiguration.fullName}" :
                                 "${testServer.name}${"upload".capitalize()}",
                             TestServerTask)
-                    serverTask.description = "Uploads APKs for Build '${baseVariantData.name}' to Test Server '${testServer.name.capitalize()}'."
+                    serverTask.description = "Uploads APKs for Build '${baseVariantData.variantConfiguration.fullName}' to Test Server '${testServer.name.capitalize()}'."
                     serverTask.group = JavaBasePlugin.VERIFICATION_GROUP
                     serverTask.dependsOn testVariantData.assembleTask, baseVariantData.assembleTask
 
@@ -870,7 +1097,7 @@
                         serverTask.conventionMapping.testedApk = { baseVariantData.outputFile }
                     }
 
-                    serverTask.conventionMapping.variantName = { baseVariantData.name }
+                    serverTask.conventionMapping.variantName = { baseVariantData.variantConfiguration.fullName }
 
                     deviceCheck.dependsOn serverTask
 
@@ -916,7 +1143,7 @@
 
         testTask.plugin = this
         testTask.variant = variantData
-        testTask.flavorName = variantData.flavorName
+        testTask.flavorName = variantData.variantConfiguration.flavorName.capitalize()
         testTask.deviceProvider = deviceProvider
 
         testTask.conventionMapping.testApp = { variantData.outputFile }
@@ -929,7 +1156,7 @@
                 extension.testOptions.resultsDir :
                 "$project.buildDir/$FD_INSTRUMENT_RESULTS"
 
-            String flavorFolder = variantData.flavorDirName
+            String flavorFolder = variantData.variantConfiguration.flavorName
             if (!flavorFolder.isEmpty()) {
                 flavorFolder = "$FD_FLAVORS/" + flavorFolder
             }
@@ -941,7 +1168,7 @@
                 extension.testOptions.reportDir :
                 "$project.buildDir/$FD_REPORTS/$FD_INSTRUMENT_TESTS"
 
-            String flavorFolder = variantData.flavorDirName
+            String flavorFolder = variantData.variantConfiguration.flavorName
             if (!flavorFolder.isEmpty()) {
                 flavorFolder = "$FD_FLAVORS/" + flavorFolder
             }
@@ -960,113 +1187,116 @@
      */
     protected void addPackageTasks(@NonNull ApkVariantData variantData,
                                    @Nullable Task assembleTask) {
-
         VariantConfiguration variantConfig = variantData.variantConfiguration
 
-        Closure libraryClosure = { project.files(variantConfig.packagedJars) }
-        Closure sourceClosure = { variantData.javaCompileTask.outputs.files }
-        Closure proguardFileClosure = { }
+        boolean runProguard = variantConfig.buildType.runProguard &&
+                (variantConfig.type != VariantConfiguration.Type.TEST ||
+                        (variantConfig.type == VariantConfiguration.Type.TEST &&
+                                variantConfig.testedConfig.type != VariantConfiguration.Type.LIBRARY))
 
-        if (!(variantData instanceof TestVariantData) && variantConfig.buildType.runProguard) {
-
-            def proguardTask = project.tasks.create("proguard${variantData.name}", ProGuardTask);
-            proguardTask.dependsOn variantData.javaCompileTask
-            variantData.proguardTask = proguardTask
-
-            File outFile = project.file(
-                    "${project.buildDir}/classes-proguard/${variantData.dirName}/classes.jar")
-
-            libraryClosure = { Collections.emptyList() }
-            sourceClosure = { Collections.emptyList() }
-            proguardFileClosure = { outFile }
-
-            // because the Proguard task acts on all the config right away and not when the
-            // task actually runs, let's configure it in its doFirst
-
-            proguardTask.doFirst {
-
-                // all the config files coming from build type, product flavors.
-                List<Object> proguardFiles = variantConfig.getProguardFiles(true /*includeLibs*/);
-                for (Object proguardFile : proguardFiles) {
-                    proguardTask.configuration(proguardFile)
-                }
-
-                // also the config file output by aapt
-                proguardTask.configuration(variantData.processResourcesTask.proguardOutputFile)
-
-                // injar: the compilation output
-                proguardTask.injars(variantData.javaCompileTask.destinationDir)
-
-                // injar: the dependencies
-                for (File inJar : variantConfig.packagedJars) {
-                    proguardTask.injars(inJar, filter: '!META-INF/MANIFEST.MF')
-                }
-
-                // libraryJars: the runtime jars
-                for (String runtimeJar : getRuntimeJarList()) {
-                    proguardTask.libraryjars(runtimeJar)
-                }
-
-                proguardTask.outjars(outFile)
-
-                proguardTask.dump("${project.buildDir}/proguard/${variantData.dirName}/dump.txt")
-                proguardTask.printseeds(
-                        "${project.buildDir}/proguard/${variantData.dirName}/seeds.txt")
-                proguardTask.printusage(
-                        "${project.buildDir}/proguard/${variantData.dirName}/usage.txt")
-                proguardTask.printmapping(
-                        "${project.buildDir}/proguard/${variantData.dirName}/mapping.txt")
-            }
-        }
-
-        // Add a dex task
-        def dexTaskName = "dex${variantData.name}"
-        def dexTask = project.tasks.create(dexTaskName, Dex)
+        // common dex task configuration
+        String dexTaskName = "dex${variantData.variantConfiguration.fullName.capitalize()}"
+        Dex dexTask = project.tasks.create(dexTaskName, Dex)
         variantData.dexTask = dexTask
-        if (variantData.proguardTask != null) {
-            dexTask.dependsOn variantData.proguardTask
-        } else {
-            dexTask.dependsOn variantData.javaCompileTask
-        }
 
         dexTask.plugin = this
         dexTask.variant = variantData
-        dexTask.incrementalFolder =
-                project.file("$project.buildDir/incremental/dex/$variantData.dirName")
 
-        dexTask.conventionMapping.libraries = libraryClosure
-        dexTask.conventionMapping.sourceFiles = sourceClosure
-        dexTask.conventionMapping.proguardedJar = proguardFileClosure
         dexTask.conventionMapping.outputFile = {
             project.file(
-                    "${project.buildDir}/libs/${project.archivesBaseName}-${variantData.baseName}.dex")
+                    "${project.buildDir}/libs/${project.archivesBaseName}-${variantData.variantConfiguration.baseName}.dex")
         }
         dexTask.dexOptions = extension.dexOptions
 
+        if (runProguard) {
+
+            // first proguard task.
+            BaseVariantData testedVariantData = variantData instanceof TestVariantData ? variantData.testedVariantData : null as BaseVariantData
+            File outFile = createProguardTasks(variantData, testedVariantData)
+
+            // then dexing task
+            dexTask.dependsOn variantData.proguardTask
+            dexTask.conventionMapping.inputFiles = { project.files(outFile) }
+            dexTask.conventionMapping.preDexedLibraries = { Collections.emptyList() }
+
+        } else {
+
+            // if required, pre-dexing task.
+            PreDex preDexTask = null;
+            boolean runPreDex = extension.dexOptions.preDexLibraries
+            if (runPreDex) {
+                def preDexTaskName = "preDex${variantData.variantConfiguration.fullName.capitalize()}"
+                preDexTask = project.tasks.create(preDexTaskName, PreDex)
+
+                preDexTask.dependsOn variantData.javaCompileTask
+                preDexTask.plugin = this
+                preDexTask.dexOptions = extension.dexOptions
+
+                preDexTask.conventionMapping.inputFiles = {
+                    project.files(getAndroidBuilder(variantData).getPackagedJars(variantConfig))
+                }
+                preDexTask.conventionMapping.outputFolder = {
+                    project.file(
+                            "${project.buildDir}/pre-dexed/${variantData.variantConfiguration.dirName}")
+                }
+            }
+
+            // then dexing task
+            dexTask.dependsOn variantData.javaCompileTask
+            if (runPreDex) {
+                dexTask.dependsOn preDexTask
+            }
+
+            dexTask.conventionMapping.inputFiles = { variantData.javaCompileTask.outputs.files }
+            if (runPreDex) {
+                dexTask.conventionMapping.preDexedLibraries = {
+                    project.fileTree(preDexTask.outputFolder).files
+                }
+            } else {
+                dexTask.conventionMapping.preDexedLibraries = {
+                    project.files(getAndroidBuilder(variantData).getPackagedJars(variantConfig))
+                }
+            }
+        }
+
         // Add a task to generate application package
-        def packageApp = project.tasks.create("package${variantData.name}", PackageApplication)
+        def packageApp = project.tasks.create(
+                "package${variantData.variantConfiguration.fullName.capitalize()}",
+                PackageApplication)
         variantData.packageApplicationTask = packageApp
-        packageApp.dependsOn variantData.processResourcesTask, dexTask, variantData.processJavaResources
+        packageApp.dependsOn variantData.processResourcesTask, dexTask, variantData.processJavaResourcesTask, variantData.ndkCompileTask
 
         packageApp.plugin = this
         packageApp.variant = variantData
 
-        VariantConfiguration config = variantData.variantConfiguration
-
         packageApp.conventionMapping.resourceFile = {
             variantData.processResourcesTask.packageOutputFile
         }
         packageApp.conventionMapping.dexFile = { dexTask.outputFile }
-
-        packageApp.conventionMapping.packagedJars = { config.packagedJars }
-
+        packageApp.conventionMapping.packagedJars = { getAndroidBuilder(variantData).getPackagedJars(variantConfig) }
         packageApp.conventionMapping.javaResourceDir = {
-            getOptionalDir(variantData.processJavaResources.destinationDir)
+            getOptionalDir(variantData.processJavaResourcesTask.destinationDir)
         }
+        packageApp.conventionMapping.jniFolders = {
+            // for now only the project's compilation output.
+            Set<File> set = Sets.newHashSet()
+            set.addAll(variantData.ndkCompileTask.soFolder)
+            set.addAll(variantData.renderscriptCompileTask.libOutputDir)
+            set.addAll(variantConfig.libraryJniFolders)
 
-        packageApp.conventionMapping.jniDebugBuild = { config.buildType.jniDebugBuild }
+            if (variantConfig.mergedFlavor.renderscriptSupportMode) {
+                File rsLibs = getAndroidBuilder(variantData).getSupportNativeLibFolder()
+                if (rsLibs.isDirectory()) {
+                    set.add(rsLibs);
+                }
+            }
 
-        SigningConfigDsl sc = (SigningConfigDsl) config.signingConfig
+            return set
+        }
+        packageApp.conventionMapping.abiFilters = { variantConfig.supportedAbis }
+        packageApp.conventionMapping.jniDebugBuild = { variantConfig.buildType.jniDebugBuild }
+
+        SigningConfigDsl sc = (SigningConfigDsl) variantConfig.signingConfig
         packageApp.conventionMapping.signingConfig = { sc }
         if (sc != null) {
             ValidateSigningTask validateSigningTask = validateSigningTaskMap.get(sc)
@@ -1084,8 +1314,8 @@
 
         def signedApk = variantData.isSigned()
         def apkName = signedApk ?
-            "${project.archivesBaseName}-${variantData.baseName}-unaligned.apk" :
-            "${project.archivesBaseName}-${variantData.baseName}-unsigned.apk"
+            "${project.archivesBaseName}-${variantData.variantConfiguration.baseName}-unaligned.apk" :
+            "${project.archivesBaseName}-${variantData.variantConfiguration.baseName}-unsigned.apk"
 
         packageApp.conventionMapping.outputFile = {
             project.file("$project.buildDir/apk/${apkName}")
@@ -1097,25 +1327,29 @@
         if (signedApk) {
             if (variantData.zipAlign) {
                 // Add a task to zip align application package
-                def zipAlignTask = project.tasks.create("zipalign${variantData.name}", ZipAlign)
+                def zipAlignTask = project.tasks.create(
+                        "zipalign${variantData.variantConfiguration.fullName.capitalize()}",
+                        ZipAlign)
                 variantData.zipAlignTask = zipAlignTask
 
                 zipAlignTask.dependsOn packageApp
                 zipAlignTask.conventionMapping.inputFile = { packageApp.outputFile }
                 zipAlignTask.conventionMapping.outputFile = {
                     project.file(
-                            "$project.buildDir/apk/${project.archivesBaseName}-${variantData.baseName}.apk")
+                            "$project.buildDir/apk/${project.archivesBaseName}-${variantData.variantConfiguration.baseName}.apk")
                 }
                 zipAlignTask.conventionMapping.zipAlignExe = { getSdkParser().zipAlign }
 
                 appTask = zipAlignTask
                 outputFileTask = zipAlignTask
                 variantData.outputFile = project.file(
-                        "$project.buildDir/apk/${project.archivesBaseName}-${variantData.baseName}.apk")
+                        "$project.buildDir/apk/${project.archivesBaseName}-${variantData.variantConfiguration.baseName}.apk")
             }
 
             // Add a task to install the application package
-            def installTask = project.tasks.create("install${variantData.name}", InstallTask)
+            def installTask = project.tasks.create(
+                    "install${variantData.variantConfiguration.fullName.capitalize()}",
+                    InstallTask)
             installTask.description = "Installs the " + variantData.description
             installTask.group = INSTALL_GROUP
             installTask.dependsOn appTask
@@ -1127,7 +1361,7 @@
 
         // Add an assemble task
         if (assembleTask == null) {
-            assembleTask = project.tasks.create("assemble${variantData.name}")
+            assembleTask = project.tasks.create("assemble${variantData.variantConfiguration.fullName.capitalize()}")
             assembleTask.description = "Assembles the " + variantData.description
             assembleTask.group = org.gradle.api.plugins.BasePlugin.BUILD_GROUP
         }
@@ -1137,7 +1371,9 @@
         variantData.outputFile = { outputFileTask.outputFile }
 
         // add an uninstall task
-        def uninstallTask = project.tasks.create("uninstall${variantData.name}", UninstallTask)
+        def uninstallTask = project.tasks.create(
+                "uninstall${variantData.variantConfiguration.fullName.capitalize()}",
+                UninstallTask)
         uninstallTask.description = "Uninstalls the " + variantData.description
         uninstallTask.group = INSTALL_GROUP
         uninstallTask.variant = variantData
@@ -1147,6 +1383,146 @@
         uninstallAll.dependsOn uninstallTask
     }
 
+    /**
+     * Creates the proguarding task for the given Variant.
+     * @param variantData the variant data.
+     * @param testedVariantData optional. variant data representing the tested variant, null if the
+     *                          variant is not a test variant
+     * @return outFile file outputted by proguard
+     */
+    @NonNull
+    protected File createProguardTasks(@NonNull BaseVariantData variantData,
+                                       @Nullable BaseVariantData testedVariantData) {
+        VariantConfiguration variantConfig = variantData.variantConfiguration
+
+        def proguardTask = project.tasks.create(
+                "proguard${variantData.variantConfiguration.fullName.capitalize()}",
+                ProGuardTask)
+        proguardTask.dependsOn variantData.javaCompileTask
+        if (testedVariantData != null) {
+            proguardTask.dependsOn testedVariantData.proguardTask
+        }
+
+        variantData.proguardTask = proguardTask
+
+        // --- Output File ---
+
+        File outFile;
+        if (variantData instanceof LibraryVariantData) {
+            outFile = project.file(
+                    "${project.buildDir}/$DIR_BUNDLES/${variantData.variantConfiguration.dirName}/classes.jar")
+        } else {
+            outFile = project.file(
+                    "${project.buildDir}/classes-proguard/${variantData.variantConfiguration.dirName}/classes.jar")
+        }
+
+        // --- Proguard Config ---
+
+        if (testedVariantData != null) {
+            // don't remove any code in tested app
+            proguardTask.dontshrink()
+            proguardTask.keepnames("class * extends junit.framework.TestCase")
+            proguardTask.keepclassmembers("class * extends junit.framework.TestCase {\n" +
+                    "    void test*(...);\n" +
+                    "}")
+
+            // input the mapping from the tested app so that we can deal with obfuscated code
+            proguardTask.applymapping("${project.buildDir}/proguard/${testedVariantData.variantConfiguration.dirName}/mapping.txt")
+
+            // for tested app, we only care about their aapt config since the base
+            // configs are the same files anyway.
+            proguardTask.configuration(testedVariantData.processResourcesTask.proguardOutputFile)
+        }
+
+        // all the config files coming from build type, product flavors.
+        List<Object> proguardFiles = variantConfig.getProguardFiles(true /*includeLibs*/)
+        for (Object proguardFile : proguardFiles) {
+            proguardTask.configuration(proguardFile)
+        }
+
+        // also the config file output by aapt
+        proguardTask.configuration(variantData.processResourcesTask.proguardOutputFile)
+
+        // --- InJars / LibraryJars ---
+
+        List<File> packagedJars = getAndroidBuilder(variantData).getPackagedJars(variantConfig)
+
+        if (variantData instanceof LibraryVariantData) {
+            String packageName = variantConfig.getPackageFromManifest()
+            if (packageName == null) {
+                throw new BuildException("Failed to read manifest", null)
+            }
+            packageName = packageName.replace('.', '/');
+
+            // injar: the compilation output
+            // exclude R files and such from output
+            String exclude = '!' + packageName + "/R.class"
+            exclude += (', !' + packageName + "/R\$*.class")
+            exclude += (', !' + packageName + "/Manifest.class")
+            exclude += (', !' + packageName + "/Manifest\$*.class")
+            exclude += (', !' + packageName + "/BuildConfig.class")
+            proguardTask.injars(variantData.javaCompileTask.destinationDir, filter: exclude)
+
+            // include R files and such for compilation
+            String include = exclude.replace('!', '')
+            proguardTask.libraryjars(variantData.javaCompileTask.destinationDir, filter: include)
+
+            // injar: the local dependencies, filter out local jars from packagedJars
+            Object[] jars = LibraryPlugin.getLocalJarFileList(variantData.variantDependency)
+            for (Object inJar : jars) {
+                if (packagedJars.contains(inJar)) {
+                    packagedJars.remove(inJar);
+                }
+                proguardTask.injars((File) inJar, filter: '!META-INF/MANIFEST.MF')
+            }
+
+            // libjar: the library dependencies
+            for (File libJar : packagedJars) {
+                proguardTask.libraryjars(libJar, filter: '!META-INF/MANIFEST.MF')
+            }
+
+            // ensure local jars keep their package names
+            proguardTask.keeppackagenames()
+        } else {
+            // injar: the compilation output
+            proguardTask.injars(variantData.javaCompileTask.destinationDir)
+
+            // injar: the dependencies
+            for (File inJar : packagedJars) {
+                proguardTask.injars(inJar, filter: '!META-INF/MANIFEST.MF')
+            }
+        }
+
+        // libraryJars: the runtime jars
+        for (String runtimeJar : getRuntimeJarList()) {
+            proguardTask.libraryjars(runtimeJar)
+        }
+
+        if (testedVariantData != null) {
+            // input the tested app as library
+            proguardTask.libraryjars(testedVariantData.javaCompileTask.destinationDir)
+            // including its dependencies
+            List<File> testedPackagedJars = getAndroidBuilder(testedVariantData).getPackagedJars(testedVariantData.variantConfiguration)
+            for (File inJar : testedPackagedJars) {
+                proguardTask.libraryjars(inJar, filter: '!META-INF/MANIFEST.MF')
+            }
+        }
+
+        // --- Out files ---
+
+        proguardTask.outjars(outFile)
+
+        proguardTask.dump("${project.buildDir}/proguard/${variantData.variantConfiguration.dirName}/dump.txt")
+        proguardTask.printseeds(
+                "${project.buildDir}/proguard/${variantData.variantConfiguration.dirName}/seeds.txt")
+        proguardTask.printusage(
+                "${project.buildDir}/proguard/${variantData.variantConfiguration.dirName}/usage.txt")
+        proguardTask.printmapping(
+                "${project.buildDir}/proguard/${variantData.variantConfiguration.dirName}/mapping.txt")
+
+        return outFile
+    }
+
     private void createReportTasks() {
         def dependencyReportTask = project.tasks.create("androidDependencies", DependencyReportTask)
         dependencyReportTask.setDescription("Displays the Android dependencies of the project")
@@ -1159,16 +1535,17 @@
         signingReportTask.setGroup("Android")
     }
 
+    protected void createAnchorTasks(@NonNull BaseVariantData variantData) {
+        variantData.preBuildTask = project.tasks.create(
+                "pre${variantData.variantConfiguration.fullName.capitalize()}Build")
+        variantData.preBuildTask.dependsOn mainPreBuild
 
-    //----------------------------------------------------------------------------------------------
-    //------------------------------ START DEPENDENCY STUFF ----------------------------------------
-    //----------------------------------------------------------------------------------------------
-
-
-    protected void createPrepareDependenciesTask(@NonNull BaseVariantData variantData) {
-        def prepareDependenciesTask = project.tasks.create("prepare${variantData.name}Dependencies",
+        def prepareDependenciesTask = project.tasks.create(
+                "prepare${variantData.variantConfiguration.fullName.capitalize()}Dependencies",
                 PrepareDependenciesTask)
+
         variantData.prepareDependenciesTask = prepareDependenciesTask
+        prepareDependenciesTask.dependsOn variantData.preBuildTask
 
         prepareDependenciesTask.plugin = this
         prepareDependenciesTask.variant = variantData
@@ -1179,19 +1556,135 @@
         prepareDependenciesTask.addChecker(configurationDependencies.checker)
 
         for (LibraryDependencyImpl lib : configurationDependencies.libraries) {
-            addDependencyToPrepareTask(prepareDependenciesTask, lib)
+            addDependencyToPrepareTask(variantData, prepareDependenciesTask, lib)
         }
+
+        // also create sourceGenTask
+        variantData.sourceGenTask = project.tasks.create(
+                "generate${variantData.variantConfiguration.fullName.capitalize()}Sources")
     }
 
-    def addDependencyToPrepareTask(PrepareDependenciesTask prepareDependenciesTask,
-                                   LibraryDependencyImpl lib) {
+
+    private final Map<String, ArtifactMetaData> extraArtifactMap = Maps.newHashMap()
+    private final ListMultimap<String, AndroidArtifact> extraAndroidArtifacts = ArrayListMultimap.create()
+    private final ListMultimap<String, JavaArtifact> extraJavaArtifacts = ArrayListMultimap.create()
+    private final ListMultimap<String, SourceProviderContainer> extraVariantSourceProviders = ArrayListMultimap.create()
+    private final ListMultimap<String, SourceProviderContainer> extraBuildTypeSourceProviders = ArrayListMultimap.create()
+    private final ListMultimap<String, SourceProviderContainer> extraProductFlavorSourceProviders = ArrayListMultimap.create()
+    private final ListMultimap<String, SourceProviderContainer> extraMultiFlavorSourceProviders = ArrayListMultimap.create()
+
+
+    public Collection<ArtifactMetaData> getExtraArtifacts() {
+        return extraArtifactMap.values()
+    }
+
+    public Collection<AndroidArtifact> getExtraAndroidArtifacts(@NonNull String variantName) {
+        return extraAndroidArtifacts.get(variantName)
+    }
+
+    public Collection<JavaArtifact> getExtraJavaArtifacts(@NonNull String variantName) {
+        return extraJavaArtifacts.get(variantName)
+    }
+
+    public Collection<SourceProviderContainer> getExtraVariantSourceProviders(@NonNull String variantName) {
+        return extraVariantSourceProviders.get(variantName)
+    }
+
+    public Collection<SourceProviderContainer> getExtraFlavorSourceProviders(@NonNull String flavorName) {
+        return extraProductFlavorSourceProviders.get(flavorName)
+    }
+
+    public Collection<SourceProviderContainer> getExtraBuildTypeSourceProviders(@NonNull String buildTypeName) {
+        return extraBuildTypeSourceProviders.get(buildTypeName)
+    }
+
+    public void registerArtifactType(@NonNull String name,
+                                     boolean isTest,
+                                     int artifactType) {
+
+        if (extraArtifactMap.get(name) != null) {
+            throw new IllegalArgumentException("Artifact with name $name already registered.")
+        }
+
+        extraArtifactMap.put(name, new ArtifactMetaDataImpl(name, isTest, artifactType))
+    }
+
+    public void registerBuildTypeSourceProvider(@NonNull String name,
+                                                @NonNull BuildType buildType,
+                                                @NonNull SourceProvider sourceProvider) {
+        if (extraArtifactMap.get(name) == null) {
+            throw new IllegalArgumentException(
+                    "Artifact with name $name is not yet registered. Use registerArtifactType()")
+        }
+
+        extraBuildTypeSourceProviders.put(buildType.name,
+                new DefaultSourceProviderContainer(name, sourceProvider))
+
+    }
+
+    public void registerProductFlavorSourceProvider(@NonNull String name,
+                                                    @NonNull ProductFlavor productFlavor,
+                                                    @NonNull SourceProvider sourceProvider) {
+        if (extraArtifactMap.get(name) == null) {
+            throw new IllegalArgumentException(
+                    "Artifact with name $name is not yet registered. Use registerArtifactType()")
+        }
+
+        extraProductFlavorSourceProviders.put(productFlavor.name,
+                new DefaultSourceProviderContainer(name, sourceProvider))
+
+    }
+
+    public void registerMultiFlavorSourceProvider(@NonNull String name,
+                                                  @NonNull String flavorName,
+                                                  @NonNull SourceProvider sourceProvider) {
+        if (extraArtifactMap.get(name) == null) {
+            throw new IllegalArgumentException(
+                    "Artifact with name $name is not yet registered. Use registerArtifactType()")
+        }
+
+        extraMultiFlavorSourceProviders.put(flavorName,
+                new DefaultSourceProviderContainer(name, sourceProvider))
+    }
+
+    public void registerJavaArtifact(
+            @NonNull String name,
+            @NonNull BaseVariant variant,
+            @NonNull String assembleTaskName,
+            @NonNull String javaCompileTaskName,
+            @NonNull File classesFolder,
+            @Nullable SourceProvider sourceProvider) {
+        ArtifactMetaData artifactMetaData = extraArtifactMap.get(name)
+        if (artifactMetaData == null) {
+            throw new IllegalArgumentException(
+                    "Artifact with name $name is not yet registered. Use registerArtifactType()")
+        }
+        if (artifactMetaData.type != ArtifactMetaData.TYPE_JAVA) {
+            throw new IllegalArgumentException(
+                    "Artifact with name $name is not of type JAVA")
+        }
+
+        JavaArtifact artifact = new JavaArtifactImpl(
+                name, assembleTaskName, javaCompileTaskName, classesFolder,
+                null, sourceProvider, null)
+        extraJavaArtifacts.put(variant.name, artifact)
+    }
+
+    //----------------------------------------------------------------------------------------------
+    //------------------------------ START DEPENDENCY STUFF ----------------------------------------
+    //----------------------------------------------------------------------------------------------
+
+    def addDependencyToPrepareTask(@NonNull BaseVariantData variantData,
+                                   @NonNull PrepareDependenciesTask prepareDependenciesTask,
+                                   @NonNull LibraryDependencyImpl lib) {
         def prepareLibTask = prepareTaskMap.get(lib)
         if (prepareLibTask != null) {
             prepareDependenciesTask.dependsOn prepareLibTask
+            prepareLibTask.dependsOn variantData.preBuildTask
         }
 
         for (LibraryDependencyImpl childLib : lib.dependencies) {
-            addDependencyToPrepareTask(prepareDependenciesTask, childLib)
+            addDependencyToPrepareTask(variantData, prepareDependenciesTask, childLib)
         }
     }
 
@@ -1246,14 +1739,23 @@
 
         variantDeps.checker = new DependencyChecker(variantDeps, logger)
 
+        Set<String> currentUnresolvedDependencies = Sets.newHashSet()
+
         // TODO - defer downloading until required -- This is hard to do as we need the info to build the variant config.
         List<LibraryDependencyImpl> bundles = []
         List<JarDependency> jars = []
         List<JarDependency> localJars = []
         collectArtifacts(compileClasspath, artifacts)
-        compileClasspath.incoming.resolutionResult.root.dependencies.each { ResolvedDependencyResult dep ->
-            addDependency(dep.selected, variantDeps, bundles, jars, modules,
-                    artifacts, reverseMap)
+        def dependencies = compileClasspath.incoming.resolutionResult.root.dependencies
+        dependencies.each { DependencyResult dep ->
+            if (dep instanceof ResolvedDependencyResult) {
+                addDependency(dep.selected, variantDeps, bundles, jars, modules, artifacts, reverseMap)
+            } else if (dep instanceof UnresolvedDependencyResult) {
+                def attempted = dep.attempted;
+                if (attempted != null) {
+                    currentUnresolvedDependencies.add(attempted.toString())
+                }
+            }
         }
 
         // also need to process local jar files, as they are not processed by the
@@ -1273,21 +1775,26 @@
         // in compile and remove all dependencies already in compile to get package-only jar
         // files.
         Configuration packageClasspath = variantDeps.packageConfiguration
-        Set<File> compileFiles = compileClasspath.files
-        Set<File> packageFiles = packageClasspath.files
 
-        for (File f : packageFiles) {
-            if (compileFiles.contains(f)) {
-                continue
-            }
+        if (!compileClasspath.resolvedConfiguration.hasError()) {
+            Set<File> compileFiles = compileClasspath.files
+            Set<File> packageFiles = packageClasspath.files
 
-            if (f.getName().toLowerCase().endsWith(".jar")) {
-                jars.add(new JarDependency(f, false /*compiled*/, true /*packaged*/))
-            } else {
-                throw new RuntimeException("Package-only dependency '" +
-                        f.absolutePath +
-                        "' is not supported")
+            for (File f : packageFiles) {
+                if (compileFiles.contains(f)) {
+                    continue
+                }
+
+                if (f.getName().toLowerCase().endsWith(".jar")) {
+                    jars.add(new JarDependency(f, false /*compiled*/, true /*packaged*/))
+                } else {
+                    throw new RuntimeException("Package-only dependency '" +
+                            f.absolutePath +
+                            "' is not supported")
+                }
             }
+        } else if (!currentUnresolvedDependencies.isEmpty()) {
+            unresolvedDependencies.addAll(currentUnresolvedDependencies)
         }
 
         variantDeps.addLibraries(bundles)
@@ -1308,7 +1815,15 @@
 
     static def collectArtifacts(Configuration configuration, Map<ModuleVersionIdentifier,
                          List<ResolvedArtifact>> artifacts) {
-        configuration.resolvedConfiguration.resolvedArtifacts.each { ResolvedArtifact artifact ->
+        boolean buildModelOnly = Boolean.getBoolean(AndroidProject.BUILD_MODEL_ONLY_SYSTEM_PROPERTY);
+        def allArtifacts
+        if (buildModelOnly) {
+            allArtifacts = configuration.resolvedConfiguration.lenientConfiguration.getArtifacts(Specs.satisfyAll())
+        } else {
+            allArtifacts = configuration.resolvedConfiguration.resolvedArtifacts
+        }
+
+        allArtifacts.each { ResolvedArtifact artifact ->
             def id = artifact.moduleVersion.id
             List<ResolvedArtifact> moduleArtifacts = artifacts[id]
             if (moduleArtifacts == null) {
@@ -1337,9 +1852,12 @@
             modules[id] = bundlesForThisModule
 
             def nestedBundles = []
-            moduleVersion.dependencies.each { ResolvedDependencyResult dep ->
-                addDependency(dep.selected, configDependencies, nestedBundles,
-                        jars, modules, artifacts, reverseMap)
+            def dependencies = moduleVersion.dependencies
+            dependencies.each { DependencyResult dep ->
+                if (dep instanceof ResolvedDependencyResult) {
+                    addDependency(dep.selected, configDependencies, nestedBundles,
+                            jars, modules, artifacts, reverseMap)
+                }
             }
 
             def moduleArtifacts = artifacts[id]
@@ -1443,17 +1961,25 @@
     }
 
     private static String getLocalVersion() {
-        Class clazz = BasePlugin.class
-        String className = clazz.getSimpleName() + ".class"
-        String classPath = clazz.getResource(className).toString()
-        if (!classPath.startsWith("jar")) {
-            // Class not from JAR, unlikely
-            return null
+        try {
+            Class clazz = BasePlugin.class
+            String className = clazz.getSimpleName() + ".class"
+            String classPath = clazz.getResource(className).toString()
+            if (!classPath.startsWith("jar")) {
+                // Class not from JAR, unlikely
+                return null
+            }
+            String manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) +
+                    "/META-INF/MANIFEST.MF";
+            Manifest manifest = new Manifest(new URL(manifestPath).openStream());
+            Attributes attr = manifest.getMainAttributes();
+            return attr.getValue("Plugin-Version");
+        } catch (Throwable t) {
+            return null;
         }
-        String manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) +
-                "/META-INF/MANIFEST.MF";
-        Manifest manifest = new Manifest(new URL(manifestPath).openStream());
-        Attributes attr = manifest.getMainAttributes();
-        return attr.getValue("Plugin-Version");
+    }
+
+    public Project getProject() {
+        return project
     }
 }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/LibraryExtension.groovy b/gradle/src/main/groovy/com/android/build/gradle/LibraryExtension.groovy
index 6d6015b..e1cf6a3 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/LibraryExtension.groovy
+++ b/gradle/src/main/groovy/com/android/build/gradle/LibraryExtension.groovy
@@ -45,9 +45,12 @@
                 BuilderConstants.DEBUG)
         debugSigningConfig.initDebug()
 
-        debug = instantiator.newInstance(BuildTypeDsl.class, BuilderConstants.DEBUG, project.fileResolver)
+        debug = instantiator.newInstance(BuildTypeDsl.class,
+                BuilderConstants.DEBUG, project.fileResolver, instantiator)
         debug.init(debugSigningConfig)
-        release = instantiator.newInstance(BuildTypeDsl.class, BuilderConstants.RELEASE, project.fileResolver)
+
+        release = instantiator.newInstance(BuildTypeDsl.class,
+                BuilderConstants.RELEASE, project.fileResolver, instantiator)
         release.init(null)
     }
 
diff --git a/gradle/src/main/groovy/com/android/build/gradle/LibraryPlugin.groovy b/gradle/src/main/groovy/com/android/build/gradle/LibraryPlugin.groovy
index 93420e8..14cce5e 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/LibraryPlugin.groovy
+++ b/gradle/src/main/groovy/com/android/build/gradle/LibraryPlugin.groovy
@@ -45,6 +45,7 @@
 import org.gradle.api.Project
 import org.gradle.api.internal.project.ProjectInternal
 import org.gradle.api.plugins.MavenPlugin
+import org.gradle.api.tasks.Copy
 import org.gradle.api.tasks.Sync
 import org.gradle.api.tasks.bundling.Jar
 import org.gradle.api.tasks.bundling.Zip
@@ -58,8 +59,6 @@
  */
 public class LibraryPlugin extends BasePlugin implements Plugin<Project> {
 
-    private final static String DIR_BUNDLES = "bundles";
-
     LibraryExtension extension
     BuildTypeData debugBuildTypeData
     BuildTypeData releaseBuildTypeData
@@ -80,9 +79,9 @@
         // create the source sets for the build type.
         // the ones for the main product flavors are handled by the base plugin.
         DefaultAndroidSourceSet debugSourceSet =
-            (DefaultAndroidSourceSet) extension.sourceSetsContainer.create(BuilderConstants.DEBUG)
+            (DefaultAndroidSourceSet) extension.sourceSetsContainer.maybeCreate(BuilderConstants.DEBUG)
         DefaultAndroidSourceSet releaseSourceSet =
-            (DefaultAndroidSourceSet) extension.sourceSetsContainer.create(BuilderConstants.RELEASE)
+            (DefaultAndroidSourceSet) extension.sourceSetsContainer.maybeCreate(BuilderConstants.RELEASE)
 
         debugBuildTypeData = new BuildTypeData(extension.debug, debugSourceSet, project)
         releaseBuildTypeData = new BuildTypeData(extension.release, releaseSourceSet, project)
@@ -127,23 +126,29 @@
         LibraryVariantData releaseVariantData = createLibVariant(defaultConfigData,
                 releaseBuildTypeData)
 
+        // Add a compile lint task before library is bundled
+        createLintCompileTask()
+
         // Need to create the tasks for these before doing the test variant as it
         // references the debug variant and its output
         createLibraryVariant(debugVariantData, false)
         createLibraryVariant(releaseVariantData, true)
 
         VariantConfiguration testVariantConfig = new VariantConfiguration(
-                defaultConfigData.productFlavor, defaultConfigData.testSourceSet,
-                debugBuildTypeData.buildType, null,
+                defaultConfigData.productFlavor,
+                defaultConfigData.testSourceSet,
+                debugBuildTypeData.buildType,
+                null,
                 VariantConfiguration.Type.TEST,
-                debugVariantData.variantConfiguration, project.name)
+                debugVariantData.variantConfiguration)
 
         TestVariantData testVariantData = new TestVariantData(testVariantConfig, debugVariantData)
         // link the testVariant to the tested variant in the other direction
         debugVariantData.setTestVariantData(testVariantData);
 
         // dependencies for the test variant
-        variantDep = VariantDependencies.compute(project, testVariantData.name,
+        variantDep = VariantDependencies.compute(project,
+                testVariantData.variantConfiguration.fullName,
                 defaultConfigData.testProvider, debugVariantData.variantDependency)
         testVariantData.setVariantDependency(variantDep)
 
@@ -151,6 +156,9 @@
 
         createTestVariant(testVariantData, debugVariantData)
 
+        // create the lint tasks.
+        createLintTasks()
+
         // create the test tasks.
         createCheckTasks(false /*hasFlavors*/, true /*isLibrary*/)
 
@@ -161,14 +169,16 @@
     protected LibraryVariantData createLibVariant(@NonNull ProductFlavorData configData,
                                                   @NonNull BuildTypeData buildTypeData) {
         VariantConfiguration variantConfig = new VariantConfiguration(
-                configData.productFlavor, configData.sourceSet,
-                buildTypeData.buildType, buildTypeData.sourceSet,
-                VariantConfiguration.Type.LIBRARY, project.name)
+                configData.productFlavor,
+                configData.sourceSet,
+                buildTypeData.buildType,
+                buildTypeData.sourceSet,
+                VariantConfiguration.Type.LIBRARY)
 
         LibraryVariantData variantData = new LibraryVariantData(variantConfig)
 
         VariantDependencies debugVariantDep = VariantDependencies.compute(
-                project, variantData.name,
+                project, variantData.variantConfiguration.fullName,
                 buildTypeData, configData.mainProvider)
         variantData.setVariantDependency(debugVariantDep)
 
@@ -185,12 +195,7 @@
         VariantConfiguration variantConfig = variantData.variantConfiguration
         DefaultBuildType buildType = variantConfig.buildType
 
-        String packageName = variantConfig.getPackageFromManifest()
-        if (packageName == null) {
-            throw new BuildException("Failed to read manifest", null)
-        }
-
-        createPrepareDependenciesTask(variantData)
+        createAnchorTasks(variantData)
 
         // Add a task to process the manifest(s)
         createProcessManifestTask(variantData, DIR_BUNDLES)
@@ -206,13 +211,13 @@
         // the dependencies. This is what gets packaged in the aar.
         MergeResources packageRes = basicCreateMergeResourcesTask(variantData,
                 "package",
-                "$project.buildDir/$DIR_BUNDLES/${variantData.dirName}/res",
+                "$project.buildDir/$DIR_BUNDLES/${variantData.variantConfiguration.dirName}/res",
                 false /*includeDependencies*/,
                 false /*process9Patch*/)
 
         // Add a task to merge the assets folders
         createMergeAssetsTask(variantData,
-                "$project.buildDir/$DIR_BUNDLES/${variantData.dirName}/assets",
+                "$project.buildDir/$DIR_BUNDLES/${variantData.variantConfiguration.dirName}/assets",
                 false /*includeDependencies*/)
 
         // Add a task to create the BuildConfig class
@@ -220,7 +225,8 @@
 
         // Add a task to generate resource source files, directing the location
         // of the r.txt file to be directly in the bundle.
-        createProcessResTask(variantData, "$project.buildDir/$DIR_BUNDLES/${variantData.dirName}")
+        createProcessResTask(variantData,
+                "$project.buildDir/$DIR_BUNDLES/${variantData.variantConfiguration.dirName}")
 
         // process java resources
         createProcessJavaResTask(variantData)
@@ -230,61 +236,101 @@
         // Add a compile task
         createCompileTask(variantData, null/*testedVariant*/)
 
-        // jar the classes.
-        Jar jar = project.tasks.create("package${buildType.name.capitalize()}Jar", Jar);
-        jar.dependsOn variantData.javaCompileTask, variantData.processJavaResources
-        jar.from(variantData.javaCompileTask.outputs);
-        jar.from(variantData.processJavaResources.destinationDir)
-
-        jar.destinationDir = project.file("$project.buildDir/$DIR_BUNDLES/${variantData.dirName}")
-        jar.archiveName = "classes.jar"
-        packageName = packageName.replace('.', '/');
-        jar.exclude(packageName + "/R.class")
-        jar.exclude(packageName + "/R\$*.class")
-        jar.exclude(packageName + "/Manifest.class")
-        jar.exclude(packageName + "/Manifest\$*.class")
-        jar.exclude(packageName + "/BuildConfig.class")
+        // Add NDK tasks
+        createNdkTasks(
+                variantData,
+                { project.file("$project.buildDir/$DIR_BUNDLES/${variantData.variantConfiguration.dirName}/jni") });
 
         // package the aidl files into the bundle folder
-        Sync packageAidl = project.tasks.create("package${variantData.name}Aidl", Sync)
+        Sync packageAidl = project.tasks.create(
+                "package${variantData.variantConfiguration.fullName.capitalize()}Aidl",
+                Sync)
         // packageAidl from 3 sources. the order is important to make sure the override works well.
         packageAidl.from(variantConfig.aidlSourceList).include("**/*.aidl")
         packageAidl.into(project.file(
-                "$project.buildDir/$DIR_BUNDLES/${variantData.dirName}/$SdkConstants.FD_AIDL"))
+                "$project.buildDir/$DIR_BUNDLES/${variantData.variantConfiguration.dirName}/$SdkConstants.FD_AIDL"))
 
         // package the renderscript header files files into the bundle folder
-        Sync packageRenderscript = project.tasks.create("package${variantData.name}Renderscript",
+        Sync packageRenderscript = project.tasks.create(
+                "package${variantData.variantConfiguration.fullName.capitalize()}Renderscript",
                 Sync)
         // package from 3 sources. the order is important to make sure the override works well.
         packageRenderscript.from(variantConfig.renderscriptSourceList).include("**/*.rsh")
         packageRenderscript.into(project.file(
-                "$project.buildDir/$DIR_BUNDLES/${variantData.dirName}/$SdkConstants.FD_RENDERSCRIPT"))
+                "$project.buildDir/$DIR_BUNDLES/${variantData.variantConfiguration.dirName}/$SdkConstants.FD_RENDERSCRIPT"))
 
-        // package the renderscript header files files into the bundle folder
-        Sync packageLocalJar = project.tasks.create("package${variantData.name}LocalJar", Sync)
-        packageLocalJar.from(getLocalJarFileList(variantData.variantDependency))
-        packageLocalJar.into(project.file(
-                "$project.buildDir/$DIR_BUNDLES/${variantData.dirName}/$SdkConstants.LIBS_FOLDER"))
-
-        // merge the proguard files together
-        MergeFileTask mergeFileTask = project.tasks.create("merge${variantData.name}ProguardFiles",
+        // merge consumer proguard files from different build types and flavors
+        MergeFileTask mergeProGuardFileTask = project.tasks.create(
+                "merge${variantData.variantConfiguration.fullName.capitalize()}ProguardFiles",
                 MergeFileTask)
-        mergeFileTask.conventionMapping.inputFiles = {
-            project.files(variantConfig.getProguardFiles(false)).files }
-        mergeFileTask.conventionMapping.outputFile = {
+        mergeProGuardFileTask.conventionMapping.inputFiles = {
+            project.files(variantConfig.getConsumerProguardFiles()).files }
+        mergeProGuardFileTask.conventionMapping.outputFile = {
             project.file(
-                    "$project.buildDir/$DIR_BUNDLES/${variantData.dirName}/$LibraryBundle.FN_PROGUARD_TXT")
+                    "$project.buildDir/$DIR_BUNDLES/${variantData.variantConfiguration.dirName}/$LibraryBundle.FN_PROGUARD_TXT")
         }
 
-        Zip bundle = project.tasks.create("bundle${variantData.name}", Zip)
-        bundle.dependsOn packageRes, jar, packageAidl, packageRenderscript, packageLocalJar, mergeFileTask
-        bundle.setDescription("Assembles a bundle containing the library in ${variantData.name}.");
+        // copy lint.jar into the bundle folder
+        Copy lintCopy = project.tasks.create(
+                "copy${variantData.variantConfiguration.fullName.capitalize()}Lint",
+                Copy)
+        lintCopy.dependsOn lintCompile
+        lintCopy.from("$project.buildDir/lint/lint.jar")
+        lintCopy.into("$project.buildDir/$DIR_BUNDLES/$variantData.variantConfiguration.dirName")
+
+        Zip bundle = project.tasks.create(
+                "bundle${variantData.variantConfiguration.fullName.capitalize()}",
+                Zip)
+
+        if (variantConfig.buildType.runProguard) {
+            // run proguard on output of compile task
+            createProguardTasks(variantData, null)
+
+            // hack since bundle can't depend on variantData.proguardTask
+            mergeProGuardFileTask.dependsOn variantData.proguardTask
+
+            bundle.dependsOn packageRes, packageAidl, packageRenderscript, mergeProGuardFileTask, lintCopy, variantData.ndkCompileTask
+        } else {
+            Sync packageLocalJar = project.tasks.create(
+                    "package${variantData.variantConfiguration.fullName.capitalize()}LocalJar",
+                    Sync)
+            packageLocalJar.from(getLocalJarFileList(variantData.variantDependency))
+            packageLocalJar.into(project.file(
+                    "$project.buildDir/$DIR_BUNDLES/${variantData.variantConfiguration.dirName}/$SdkConstants.LIBS_FOLDER"))
+
+            // jar the classes.
+            Jar jar = project.tasks.create("package${buildType.name.capitalize()}Jar", Jar);
+            jar.dependsOn variantData.javaCompileTask, variantData.processJavaResourcesTask
+            jar.from(variantData.javaCompileTask.outputs);
+            jar.from(variantData.processJavaResourcesTask.destinationDir)
+
+            jar.destinationDir = project.file(
+                    "$project.buildDir/$DIR_BUNDLES/${variantData.variantConfiguration.dirName}")
+            jar.archiveName = "classes.jar"
+
+            String packageName = variantConfig.getPackageFromManifest()
+            if (packageName == null) {
+                throw new BuildException("Failed to read manifest", null)
+            }
+            packageName = packageName.replace('.', '/');
+
+            jar.exclude(packageName + "/R.class")
+            jar.exclude(packageName + "/R\$*.class")
+            jar.exclude(packageName + "/Manifest.class")
+            jar.exclude(packageName + "/Manifest\$*.class")
+            jar.exclude(packageName + "/BuildConfig.class")
+
+            bundle.dependsOn packageRes, jar, packageAidl, packageRenderscript, packageLocalJar,
+                    mergeProGuardFileTask, lintCopy, variantData.ndkCompileTask
+        }
+
+        bundle.setDescription("Assembles a bundle containing the library in ${variantData.variantConfiguration.fullName.capitalize()}.");
         bundle.destinationDir = project.file("$project.buildDir/libs")
         bundle.extension = BuilderConstants.EXT_LIB_ARCHIVE
-        if (variantData.baseName != BuilderConstants.RELEASE) {
-            bundle.classifier = variantData.baseName
+        if (variantData.variantConfiguration.baseName != BuilderConstants.RELEASE) {
+            bundle.classifier = variantData.variantConfiguration.baseName
         }
-        bundle.from(project.file("$project.buildDir/$DIR_BUNDLES/${variantData.dirName}"))
+        bundle.from(project.file("$project.buildDir/$DIR_BUNDLES/${variantData.variantConfiguration.dirName}"))
 
         variantData.packageLibTask = bundle
         variantData.outputFile = bundle.archivePath
@@ -302,7 +348,7 @@
         // configure the variant to be testable.
         variantConfig.output = new LibraryBundle(
                 bundle.archivePath,
-                project.file("$project.buildDir/$DIR_BUNDLES/${variantData.dirName}"),
+                project.file("$project.buildDir/$DIR_BUNDLES/${variantData.variantConfiguration.dirName}"),
                 variantData.getName()) {
 
             @Nullable
diff --git a/gradle/src/main/groovy/com/android/build/gradle/api/ApkVariant.java b/gradle/src/main/groovy/com/android/build/gradle/api/ApkVariant.java
index d28b6dd..404625e 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/api/ApkVariant.java
+++ b/gradle/src/main/groovy/com/android/build/gradle/api/ApkVariant.java
@@ -21,7 +21,6 @@
 import com.android.build.gradle.tasks.Dex;
 import com.android.build.gradle.tasks.PackageApplication;
 import com.android.build.gradle.tasks.ZipAlign;
-import com.android.builder.DefaultBuildType;
 import com.android.builder.DefaultProductFlavor;
 import com.android.builder.model.SigningConfig;
 import org.gradle.api.DefaultTask;
@@ -33,11 +32,6 @@
  */
 public interface ApkVariant extends BaseVariant {
 
-    /**
-     * Returns the {@link com.android.builder.DefaultBuildType} for this build variant.
-     */
-    @NonNull
-    DefaultBuildType getBuildType();
 
     /**
      * Returns the list of {@link com.android.builder.DefaultProductFlavor} for this build variant.
diff --git a/gradle/src/main/groovy/com/android/build/gradle/api/BaseVariant.java b/gradle/src/main/groovy/com/android/build/gradle/api/BaseVariant.java
index 190bf47..4357456 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/api/BaseVariant.java
+++ b/gradle/src/main/groovy/com/android/build/gradle/api/BaseVariant.java
@@ -25,11 +25,14 @@
 import com.android.build.gradle.tasks.ProcessAndroidResources;
 import com.android.build.gradle.tasks.ProcessManifest;
 import com.android.build.gradle.tasks.RenderscriptCompile;
+import com.android.builder.DefaultBuildType;
+import com.android.builder.DefaultProductFlavor;
 import org.gradle.api.Task;
 import org.gradle.api.tasks.Copy;
 import org.gradle.api.tasks.compile.JavaCompile;
 
 import java.io.File;
+import java.util.Collection;
 
 /**
  * A Build variant and all its public data. This is the base class for items common to apps,
@@ -68,6 +71,27 @@
     String getBaseName();
 
     /**
+     * Returns the flavor name of the variant. This is a concatenation of all the
+     * applied flavors
+     * @return the name of the flavors, or an empty string if there is not flavors.
+     */
+    @NonNull
+    String getFlavorName();
+
+    /**
+     * Returns the {@link com.android.builder.DefaultBuildType} for this build variant.
+     */
+    @NonNull
+    DefaultBuildType getBuildType();
+
+    /**
+     * Returns a {@link com.android.builder.DefaultProductFlavor} that represents the merging
+     * of the default config and the flavors of this build variant.
+     */
+    @NonNull
+    DefaultProductFlavor getConfig();
+
+    /**
      * Returns the output file for this build variants. Depending on the configuration, this could
      * be an apk (regular and test project) or a bundled library (library project).
      *
@@ -137,4 +161,52 @@
      */
     @Nullable
     Task getAssemble();
+
+    /**
+     * Adds new Java source folders to the model.
+     *
+     * These source folders will not be used for the default build
+     * system, but will be passed along the default Java source folders
+     * to whoever queries the model.
+     *
+     * @param sourceFolders the source folders where the generated source code is.
+     */
+    void addJavaSourceFoldersToModel(@NonNull File... sourceFolders);
+
+    /**
+     * Adds new Java source folders to the model.
+     *
+     * These source folders will not be used for the default build
+     * system, but will be passed along the default Java source folders
+     * to whoever queries the model.
+     *
+     * @param sourceFolders the source folders where the generated source code is.
+     */
+    void addJavaSourceFoldersToModel(@NonNull Collection<File> sourceFolders);
+
+    /**
+     * Adds to the variant a task that generates Java source code.
+     *
+     * This will make the compileJava task depend on this task and add the
+     * new source folders as compilation inputs.
+     *
+     * The new source folders are also added to the model.
+     *
+     * @param task the task
+     * @param sourceFolders the source folders where the generated source code is.
+     */
+    void registerJavaGeneratingTask(@NonNull Task task, @NonNull File... sourceFolders);
+
+    /**
+     * Adds to the variant a task that generates Java source code.
+     *
+     * This will make the compileJava task depend on this task and add the
+     * new source folders as compilation inputs.
+     *
+     * The new source folders are also added to the model.
+     *
+     * @param task the task
+     * @param sourceFolders the source folders where the generated source code is.
+     */
+    void registerJavaGeneratingTask(@NonNull Task task, @NonNull Collection<File> sourceFolders);
 }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/api/LibraryVariant.java b/gradle/src/main/groovy/com/android/build/gradle/api/LibraryVariant.java
index 5bcfd9f..8db077d 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/api/LibraryVariant.java
+++ b/gradle/src/main/groovy/com/android/build/gradle/api/LibraryVariant.java
@@ -16,10 +16,7 @@
 
 package com.android.build.gradle.api;
 
-import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.builder.DefaultBuildType;
-import com.android.builder.DefaultProductFlavor;
 import org.gradle.api.tasks.bundling.Zip;
 
 /**
@@ -28,19 +25,6 @@
 public interface LibraryVariant extends BaseVariant {
 
     /**
-     * Returns the {@link com.android.builder.DefaultBuildType} for this build variant.
-     */
-    @NonNull
-    DefaultBuildType getBuildType();
-
-    /**
-     * Returns a {@link com.android.builder.DefaultProductFlavor} that represents the merging
-     * of the default config and the flavors of this build variant.
-     */
-    @NonNull
-    DefaultProductFlavor getConfig();
-
-    /**
      * Returns the build variant that will test this build variant.
      *
      * Will return null if this build variant is a test build already.
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/AndroidAsciiReportRenderer.java b/gradle/src/main/groovy/com/android/build/gradle/internal/AndroidAsciiReportRenderer.java
index 2de5ad0..6704129 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/AndroidAsciiReportRenderer.java
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/AndroidAsciiReportRenderer.java
@@ -31,6 +31,7 @@
 import org.gradle.util.GUtil;
 
 import java.io.IOException;
+import java.util.Collection;
 import java.util.List;
 
 import static org.gradle.logging.StyledTextOutput.Style.Description;
@@ -69,7 +70,8 @@
         renderer.visit(new Action<StyledTextOutput>() {
             @Override
             public void execute(StyledTextOutput styledTextOutput) {
-                getTextOutput().withStyle(Identifier).text(variantData.getName());
+                getTextOutput().withStyle(Identifier).text(
+                        variantData.getVariantConfiguration().getFullName());
                 getTextOutput().withStyle(Description).text("");
             }
         }, true);
@@ -131,15 +133,16 @@
     }
 
     private void renderChildren(@NonNull List<LibraryDependency> libraries,
-                                @Nullable List<JarDependency> localJars) {
+                                @Nullable Collection<JarDependency> localJars) {
         renderer.startChildren();
         if (localJars != null) {
             final boolean emptyChildren = libraries.isEmpty();
             final int count = localJars.size();
 
-            for (int i = 0; i < count; i++) {
-                JarDependency jarDependency = localJars.get(i);
+            int i = 0;
+            for (JarDependency jarDependency : localJars) {
                 render(jarDependency, emptyChildren && i == count - 1);
+                i++;
             }
         }
 
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/CompileOptions.groovy b/gradle/src/main/groovy/com/android/build/gradle/internal/CompileOptions.groovy
index 9ee110c..de70e31 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/CompileOptions.groovy
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/CompileOptions.groovy
@@ -25,4 +25,5 @@
 
     JavaVersion sourceCompatibility = JavaVersion.VERSION_1_6
     JavaVersion targetCompatibility = JavaVersion.VERSION_1_6
+    String encoding = "UTF-8"
 }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/LintGradleClient.java b/gradle/src/main/groovy/com/android/build/gradle/internal/LintGradleClient.java
new file mode 100644
index 0000000..3bf7b8d
--- /dev/null
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/LintGradleClient.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2013 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.build.gradle.internal;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.BasePlugin;
+import com.android.builder.model.AndroidProject;
+import com.android.builder.model.Variant;
+import com.android.tools.lint.LintCliClient;
+import com.android.tools.lint.LintCliFlags;
+import com.android.tools.lint.Warning;
+import com.android.tools.lint.client.api.IssueRegistry;
+import com.android.tools.lint.client.api.LintRequest;
+import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.Project;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+public class LintGradleClient extends LintCliClient {
+    private final AndroidProject mModelProject;
+    private final String mVariantName;
+    private final BasePlugin mPlugin;
+    private List<File> mCustomRules = Lists.newArrayList();
+
+    public LintGradleClient(
+            @NonNull IssueRegistry registry,
+            @NonNull LintCliFlags flags,
+            @NonNull BasePlugin plugin,
+            @NonNull AndroidProject modelProject,
+            @Nullable String variantName) {
+        super(flags);
+        mPlugin = plugin;
+        mModelProject = modelProject;
+        mVariantName = variantName;
+        mRegistry = registry;
+    }
+
+    @NonNull
+    public BasePlugin getPlugin() {
+        return mPlugin;
+    }
+
+    public void setCustomRules(List<File> customRules) {
+        mCustomRules = customRules;
+    }
+
+    @Override
+    public List<File> findRuleJars(@NonNull Project project) {
+        return mCustomRules;
+    }
+
+    @Override
+    protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
+        // Should not be called by lint since we supply an explicit set of projects
+        // to the LintRequest
+        throw new IllegalStateException();
+    }
+
+    @Override
+    public File getSdkHome() {
+        File sdkHome = mPlugin.getSdkDirectory();
+        if (sdkHome != null) {
+            return sdkHome;
+        }
+        return super.getSdkHome();
+    }
+
+    @Override
+    @NonNull
+    protected LintRequest createLintRequest(@NonNull List<File> files) {
+        return new LintGradleRequest(this, mModelProject, mPlugin, mVariantName, files);
+    }
+
+    /** Run lint with the given registry and return the resulting warnings */
+    @NonNull
+    public List<Warning> run(@NonNull IssueRegistry registry) throws IOException {
+        run(registry, Collections.<File>emptyList());
+        return mWarnings;
+    }
+
+    /**
+     * Given a list of results from separate variants, merge them into a single
+     * list of warnings, and mark their
+     * @param warningMap a map from variant to corresponding warnings
+     * @param project the project model
+     * @return a merged list of issues
+     */
+    @NonNull
+    public static List<Warning> merge(
+            @NonNull Map<Variant,List<Warning>> warningMap,
+            @NonNull AndroidProject project) {
+        // Easy merge?
+        if (warningMap.size() == 1) {
+            return warningMap.values().iterator().next();
+        }
+        int maxCount = 0;
+        for (List<Warning> warnings : warningMap.values()) {
+            int size = warnings.size();
+            maxCount = Math.max(size, maxCount);
+        }
+        if (maxCount == 0) {
+            return Collections.emptyList();
+        }
+
+        int totalVariantCount = project.getVariants().size();
+
+        List<Warning> merged = Lists.newArrayListWithExpectedSize(2 * maxCount);
+
+        // Map fro issue to message to line number to file name to canonical warning
+        Map<Issue,Map<String, Map<Integer, Map<String, Warning>>>> map =
+                Maps.newHashMapWithExpectedSize(2 * maxCount);
+
+        for (Map.Entry<Variant,List<Warning>> entry : warningMap.entrySet()) {
+            Variant variant = entry.getKey();
+            List<Warning> warnings = entry.getValue();
+            for (Warning warning : warnings) {
+                Map<String,Map<Integer,Map<String,Warning>>> messageMap = map.get(warning.issue);
+                if (messageMap == null) {
+                    messageMap = Maps.newHashMap();
+                    map.put(warning.issue, messageMap);
+                }
+                Map<Integer, Map<String, Warning>> lineMap = messageMap.get(warning.message);
+                if (lineMap == null) {
+                    lineMap = Maps.newHashMap();
+                    messageMap.put(warning.message, lineMap);
+                }
+                Map<String, Warning> fileMap = lineMap.get(warning.line);
+                if (fileMap == null) {
+                    fileMap = Maps.newHashMap();
+                    lineMap.put(warning.line, fileMap);
+                }
+                String fileName = warning.file != null ? warning.file.getName() : "<unknown>";
+                Warning canonical = fileMap.get(fileName);
+                if (canonical == null) {
+                    canonical = warning;
+                    fileMap.put(fileName, canonical);
+                    canonical.variants = Sets.newHashSet();
+                    canonical.gradleProject = project;
+                }
+                merged.add(canonical);
+                canonical.variants.add(variant);
+            }
+        }
+
+        // Clear out variants on any nodes that don't define all
+        for (Warning warning : merged) {
+            if (warning.variants != null && warning.variants.size() == totalVariantCount) {
+                // If this error is present in all variants, just clear it out
+                warning.variants = null;
+            }
+
+        }
+
+        Collections.sort(merged);
+        return merged;
+    }
+}
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/LintGradleProject.java b/gradle/src/main/groovy/com/android/build/gradle/internal/LintGradleProject.java
new file mode 100644
index 0000000..79304d8
--- /dev/null
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/LintGradleProject.java
@@ -0,0 +1,460 @@
+package com.android.build.gradle.internal;
+
+import com.google.common.base.Charsets;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.common.io.Files;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.builder.model.AndroidArtifact;
+import com.android.builder.model.AndroidLibrary;
+import com.android.builder.model.AndroidProject;
+import com.android.builder.model.BuildTypeContainer;
+import com.android.builder.model.Dependencies;
+import com.android.builder.model.ProductFlavor;
+import com.android.builder.model.ProductFlavorContainer;
+import com.android.builder.model.SourceProvider;
+import com.android.builder.model.Variant;
+import com.android.sdklib.AndroidTargetHash;
+import com.android.sdklib.AndroidVersion;
+import com.android.tools.lint.detector.api.Project;
+import com.android.utils.Pair;
+import com.android.utils.XmlUtils;
+
+import org.w3c.dom.Document;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import static java.io.File.separatorChar;
+
+/**
+ * An implementation of Lint's {@link Project} class wrapping a Gradle model (project or
+ * library)
+ */
+public class LintGradleProject extends Project {
+    private LintGradleProject(
+            @NonNull LintGradleClient client,
+            @NonNull File dir,
+            @NonNull File referenceDir,
+            @NonNull File manifest) {
+        super(client, dir, referenceDir);
+        mGradleProject = true;
+        mMergeManifests = true;
+        mDirectLibraries = Lists.newArrayList();
+        readManifest(manifest);
+    }
+
+    /**
+     * Creates a {@link com.android.build.gradle.internal.LintGradleProject} from
+     * the given {@link com.android.builder.model.AndroidProject} definition for
+     * a given {@link com.android.builder.model.Variant}, and returns it along with
+     * a set of lint custom rule jars applicable for the given model project.
+     *
+     * @param client the client
+     * @param project the model project
+     * @param variant the variant
+     * @param gradleProject the gradle project
+     * @return a pair of new project and list of custom rule jars
+     */
+    @NonNull
+    public static Pair<LintGradleProject, List<File>> create(
+            @NonNull LintGradleClient client,
+            @NonNull AndroidProject project,
+            @NonNull Variant variant,
+            @NonNull org.gradle.api.Project gradleProject) {
+        File dir = gradleProject.getRootDir();
+        AppGradleProject lintProject = new AppGradleProject(client, dir,
+                dir, project, variant);
+
+        List<File> customRules = Lists.newArrayList();
+        File appLintJar = new File(gradleProject.getBuildDir(),
+                "lint" + separatorChar + "lint.jar");
+        if (appLintJar.exists()) {
+            customRules.add(appLintJar);
+        }
+
+        Set<AndroidLibrary> libraries = Sets.newHashSet();
+        Dependencies dependencies = variant.getMainArtifact().getDependencies();
+        for (AndroidLibrary library : dependencies.getLibraries()) {
+            lintProject.addDirectLibrary(createLibrary(client, library, libraries, customRules));
+        }
+
+        return Pair.<LintGradleProject,List<File>>of(lintProject, customRules);
+    }
+
+
+    @Override
+    protected void initialize() {
+        // Deliberately not calling super; that code is for ADT compatibility
+    }
+
+    protected void readManifest(File manifest) {
+        if (manifest.exists()) {
+            try {
+                String xml = Files.toString(manifest, Charsets.UTF_8);
+                Document document = XmlUtils.parseDocumentSilently(xml, true);
+                readManifest(document);
+            } catch (IOException e) {
+                mClient.log(e, "Could not read manifest %1$s", manifest);
+            }
+        }
+    }
+
+    @Override
+    public boolean isGradleProject() {
+        return true;
+    }
+
+    void addDirectLibrary(@NonNull Project project) {
+        mDirectLibraries.add(project);
+    }
+
+    @NonNull
+    private static LibraryProject createLibrary(@NonNull LintGradleClient client,
+            @NonNull AndroidLibrary library,
+            @NonNull Set<AndroidLibrary> seen, List<File> customRules) {
+        seen.add(library);
+        File dir = library.getFolder();
+        LibraryProject project = new LibraryProject(client, dir, dir, library);
+
+        File ruleJar = library.getLintJar();
+        if (ruleJar.exists()) {
+            customRules.add(ruleJar);
+        }
+
+        for (AndroidLibrary dependent : library.getLibraryDependencies()) {
+            if (!seen.contains(dependent)) {
+                project.addDirectLibrary(createLibrary(client, dependent, seen, customRules));
+            }
+        }
+
+        return project;
+    }
+
+    private static class AppGradleProject extends LintGradleProject {
+        private AndroidProject mProject;
+        private Variant mVariant;
+        private List<SourceProvider> mProviders;
+
+        private AppGradleProject(
+                @NonNull LintGradleClient client,
+                @NonNull File dir,
+                @NonNull File referenceDir,
+                @NonNull AndroidProject project,
+                @NonNull Variant variant) {
+            super(client, dir, referenceDir, variant.getMainArtifact().getGeneratedManifest());
+
+            mProject = project;
+            mVariant = variant;
+        }
+
+        @Override
+        public boolean isLibrary() {
+            return mProject.isLibrary();
+        }
+
+        @Override
+        public AndroidProject getGradleProjectModel() {
+            return mProject;
+        }
+
+        @Override
+        public Variant getCurrentVariant() {
+            return mVariant;
+        }
+
+        private List<SourceProvider> getSourceProviders() {
+            if (mProviders == null) {
+                List<SourceProvider> providers = Lists.newArrayList();
+                AndroidArtifact mainArtifact = mVariant.getMainArtifact();
+
+                SourceProvider defaultProvider = mProject.getDefaultConfig().getSourceProvider();
+                if (defaultProvider != null) {
+                    providers.add(defaultProvider);
+                }
+
+                for (String flavorName : mVariant.getProductFlavors()) {
+                    for (ProductFlavorContainer flavor : mProject.getProductFlavors()) {
+                        if (flavorName.equals(flavor.getProductFlavor().getName())) {
+                            SourceProvider provider = flavor.getSourceProvider();
+                            if (provider != null) {
+                                providers.add(provider);
+                            }
+                            break;
+                        }
+                    }
+                }
+
+                SourceProvider multiProvider = mainArtifact.getMultiFlavorSourceProvider();
+                if (multiProvider != null) {
+                    providers.add(multiProvider);
+                }
+
+                String buildTypeName = mVariant.getBuildType();
+                for (BuildTypeContainer buildType : mProject.getBuildTypes()) {
+                    if (buildTypeName.equals(buildType.getBuildType().getName())) {
+                        SourceProvider provider = buildType.getSourceProvider();
+                        if (provider != null) {
+                            providers.add(provider);
+                        }
+                        break;
+                    }
+                }
+
+                SourceProvider variantProvider =  mainArtifact.getVariantSourceProvider();
+                if (variantProvider != null) {
+                    providers.add(variantProvider);
+                }
+
+                mProviders = providers;
+            }
+
+            return mProviders;
+        }
+
+        @NonNull
+        @Override
+        public List<File> getManifestFiles() {
+            if (mManifestFiles == null) {
+                mManifestFiles = Lists.newArrayList();
+                for (SourceProvider provider : getSourceProviders()) {
+                    File manifestFile = provider.getManifestFile();
+                    if (manifestFile.exists()) { // model returns path whether or not it exists
+                        mManifestFiles.add(manifestFile);
+                    }
+                }
+            }
+
+            return mManifestFiles;
+        }
+
+        @Override
+        public List<File> getProguardFiles() {
+            if (mProguardFiles == null) {
+                ProductFlavor flavor = mProject.getDefaultConfig().getProductFlavor();
+                mProguardFiles = Lists.newArrayList();
+                mProguardFiles.addAll(flavor.getProguardFiles());
+                // TODO: This is currently broken:
+                //mProguardFiles.addAll(flavor.getConsumerProguardFiles());
+                // It will throw
+                //org.gradle.tooling.model.UnsupportedMethodException: Unsupported method: BaseConfig.getConsumerProguardFiles().
+                //  The version of Gradle you connect to does not support that method.
+                //  To resolve the problem you can change/upgrade the target version of Gradle you connect to.
+                //  Alternatively, you can ignore this exception and read other information from the model.
+                //at org.gradle.tooling.model.internal.Exceptions.unsupportedMethod(Exceptions.java:33)
+                //at org.gradle.tooling.internal.adapter.ProtocolToModelAdapter$InvocationHandlerImpl.invoke(ProtocolToModelAdapter.java:239)
+                //at com.sun.proxy.$Proxy129.getConsumerProguardFiles(Unknown Source)
+            }
+
+            return mProguardFiles;
+        }
+
+        @NonNull
+        @Override
+        public List<File> getResourceFolders() {
+            if (mResourceFolders == null) {
+                mResourceFolders = Lists.newArrayList();
+                for (SourceProvider provider : getSourceProviders()) {
+                    Collection<File> resDirs = provider.getResDirectories();
+                    for (File res : resDirs) {
+                        if (res.exists()) { // model returns path whether or not it exists
+                            mResourceFolders.add(res);
+                        }
+                    }
+                }
+            }
+
+            return mResourceFolders;
+        }
+
+        @NonNull
+        @Override
+        public List<File> getJavaSourceFolders() {
+            if (mJavaSourceFolders == null) {
+                mJavaSourceFolders = Lists.newArrayList();
+                for (SourceProvider provider : getSourceProviders()) {
+                    Collection<File> resDirs = provider.getJavaDirectories();
+                    for (File res : resDirs) {
+                        if (res.exists()) { // model returns path whether or not it exists
+                            mJavaSourceFolders.add(res);
+                        }
+                    }
+                }
+            }
+
+            return mJavaSourceFolders;
+        }
+
+        @NonNull
+        @Override
+        public List<File> getJavaClassFolders() {
+            if (mJavaClassFolders == null) {
+                mJavaClassFolders = new ArrayList<File>(1);
+                File outputClassFolder = mVariant.getMainArtifact().getClassesFolder();
+                if (outputClassFolder != null && outputClassFolder.exists()) {
+                    mJavaClassFolders.add(outputClassFolder);
+                }
+            }
+
+            return mJavaClassFolders;
+        }
+
+        @Override
+        public List<File> getJavaLibraries() {
+            if (mJavaLibraries == null) {
+                Collection<File> jars = mVariant.getMainArtifact().getDependencies().getJars();
+                mJavaLibraries = Lists.newArrayListWithExpectedSize(jars.size());
+                for (File jar : jars) {
+                    if (jar.exists()) {
+                        mJavaLibraries.add(jar);
+                    }
+                }
+            }
+            return mJavaLibraries;
+        }
+
+        @Nullable
+        @Override
+        public String getPackage() {
+            // For now, lint only needs the manifest package; not the potentially variant specific
+            // package. As part of the Gradle work on the Lint API we should make two separate
+            // package lookup methods -- one for the manifest package, one for the build package
+            if (mPackage == null) { // only used as a fallback in case manifest somehow is null
+                String packageName = mProject.getDefaultConfig().getProductFlavor().getPackageName();
+                if (packageName != null) {
+                    return packageName;
+                }
+            }
+
+            return mPackage; // from manifest
+        }
+
+        @Override
+        public int getMinSdk() {
+            int minSdk = mProject.getDefaultConfig().getProductFlavor().getMinSdkVersion();
+            if (minSdk != -1) {
+                return minSdk;
+            }
+
+            return mMinSdk; // from manifest
+        }
+
+        @Override
+        public int getTargetSdk() {
+            int targetSdk = mProject.getDefaultConfig().getProductFlavor().getTargetSdkVersion();
+            if (targetSdk != -1) {
+                return targetSdk;
+            }
+
+            return targetSdk; // from manifest
+        }
+
+        @Override
+        public int getBuildSdk() {
+            String compileTarget = mProject.getCompileTarget();
+            if (compileTarget != null) {
+                AndroidVersion version = AndroidTargetHash.getPlatformVersion(compileTarget);
+                if (version != null) {
+                    return version.getApiLevel();
+                }
+            }
+
+            return super.getBuildSdk();
+        }
+    }
+
+    private static class LibraryProject extends LintGradleProject {
+        private AndroidLibrary mLibrary;
+
+        private LibraryProject(
+                @NonNull LintGradleClient client,
+                @NonNull File dir,
+                @NonNull File referenceDir,
+                @NonNull AndroidLibrary library) {
+            super(client, dir, referenceDir, library.getManifest());
+            mLibrary = library;
+
+            // TODO: Make sure we don't use this project for any source library projects!
+            mReportIssues = false;
+        }
+
+        @Override
+        public boolean isLibrary() {
+            return true;
+        }
+
+        @Override
+        public AndroidLibrary getGradleLibraryModel() {
+            return mLibrary;
+        }
+
+        @Override
+        public Variant getCurrentVariant() {
+            return null;
+        }
+
+        @NonNull
+        @Override
+        public List<File> getManifestFiles() {
+            if (mManifestFiles == null) {
+                File manifest = mLibrary.getManifest();
+                if (manifest.exists()) {
+                    mManifestFiles = Collections.singletonList(manifest);
+                } else {
+                    mManifestFiles = Collections.emptyList();
+                }
+            }
+
+            return mManifestFiles;
+        }
+
+        @NonNull
+        @Override
+        public List<File> getResourceFolders() {
+            if (mResourceFolders == null) {
+                File folder = mLibrary.getResFolder();
+                if (folder.exists()) {
+                    mResourceFolders = Collections.singletonList(folder);
+                } else {
+                    mResourceFolders = Collections.emptyList();
+                }
+            }
+
+            return mResourceFolders;
+        }
+
+        @NonNull
+        @Override
+        public List<File> getJavaSourceFolders() {
+            return Collections.emptyList();
+        }
+
+        @NonNull
+        @Override
+        public List<File> getJavaClassFolders() {
+            return Collections.emptyList();
+        }
+
+        @NonNull
+        @Override
+        public List<File> getJavaLibraries() {
+            if (mJavaLibraries == null) {
+                File jarFile = mLibrary.getJarFile();
+                if (jarFile.exists()) {
+                    mJavaLibraries = Collections.singletonList(jarFile);
+                } else {
+                    mJavaLibraries = Collections.emptyList();
+                }
+            }
+
+            return mJavaLibraries;
+        }
+    }
+}
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/LintGradleRequest.java b/gradle/src/main/groovy/com/android/build/gradle/internal/LintGradleRequest.java
new file mode 100644
index 0000000..b5674f9
--- /dev/null
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/LintGradleRequest.java
@@ -0,0 +1,63 @@
+package com.android.build.gradle.internal;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.BasePlugin;
+import com.android.builder.model.AndroidProject;
+import com.android.builder.model.Variant;
+import com.android.tools.lint.client.api.LintRequest;
+import com.android.tools.lint.detector.api.Project;
+import com.android.utils.Pair;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+class LintGradleRequest extends LintRequest {
+    @NonNull private final LintGradleClient mLintClient;
+    @NonNull private final BasePlugin mPlugin;
+    @NonNull private final String mVariantName;
+    @NonNull private final AndroidProject mModelProject;
+
+    public LintGradleRequest(
+            @NonNull LintGradleClient client,
+            @NonNull AndroidProject modelProject,
+            @NonNull BasePlugin plugin,
+            @Nullable String variantName,
+            @NonNull List<File> files) {
+        super(client, files);
+        mLintClient = client;
+        mModelProject = modelProject;
+        mPlugin = plugin;
+        mVariantName = variantName;
+    }
+
+    @Nullable
+    @Override
+    public Collection<Project> getProjects() {
+        if (mProjects == null) {
+            Variant variant = findVariant(mModelProject, mVariantName);
+            assert variant != null : mVariantName;
+            Pair<LintGradleProject,List<File>> result = LintGradleProject.create(
+                    mLintClient, mModelProject, variant, mPlugin.getProject());
+            mProjects = Collections.<Project>singletonList(result.getFirst());
+            mLintClient.setCustomRules(result.getSecond());
+        }
+
+        return mProjects;
+    }
+
+    private static Variant findVariant(@NonNull AndroidProject project,
+            @NonNull String variantName) {
+        if (variantName != null) {
+            for (Variant variant : project.getVariants()) {
+                if (variantName.equals(variant.getName())) {
+                    return variant;
+                }
+            }
+        }
+
+        return null;
+    }
+}
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/LoggerWrapper.groovy b/gradle/src/main/groovy/com/android/build/gradle/internal/LoggerWrapper.groovy
index c57f1e8..575c07e 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/LoggerWrapper.groovy
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/LoggerWrapper.groovy
@@ -15,6 +15,7 @@
  */
 package com.android.build.gradle.internal
 
+import com.android.ide.common.res2.MergingException
 import com.android.utils.ILogger
 import org.gradle.api.logging.LogLevel
 import org.gradle.api.logging.Logger
@@ -32,6 +33,17 @@
 
     @Override
     void error(Throwable throwable, String s, Object... objects) {
+        if (throwable instanceof MergingException) {
+            // MergingExceptions have a known cause: they aren't internal errors, they
+            // are errors in the user's code, so a full exception is not helpful (and
+            // these exceptions should include a pointer to the user's error right in
+            // the message).
+            //
+            // Furthermore, these exceptions are already caught by the MergeResources
+            // and MergeAsset tasks, so don't duplicate the output
+            return
+        }
+
         if (objects != null && objects.length > 0) {
             s = String.format(s, objects)
         }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/Sdk.groovy b/gradle/src/main/groovy/com/android/build/gradle/internal/Sdk.groovy
index 022239c..88c8726 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/Sdk.groovy
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/Sdk.groovy
@@ -43,6 +43,7 @@
 
     private boolean isSdkParserInitialized = false
     private File androidSdkDir
+    private File androidNdkDir
     private boolean isPlatformSdk = false
     private BaseExtension extension
 
@@ -80,7 +81,7 @@
         if (isPlatformSdk) {
             parser = new PlatformSdkParser(androidSdkDir.absolutePath)
         } else {
-            parser = new DefaultSdkParser(androidSdkDir.absolutePath)
+            parser = new DefaultSdkParser(androidSdkDir.absolutePath, androidNdkDir)
         }
 
         List<File> repositories = parser.repositories
@@ -130,11 +131,16 @@
         return theParser
     }
 
-    public File getDirectory() {
+    public File getSdkDirectory() {
         checkLocation()
         return androidSdkDir
     }
 
+    public File getNdkDirectory() {
+        checkLocation()
+        return androidNdkDir
+    }
+
     private void checkLocation() {
         // don't complain in test mode
         if (TEST_SDK_DIR != null) {
@@ -179,6 +185,12 @@
                             "No sdk.dir property defined in local.properties file.")
                 }
             }
+
+            def ndkDirProp = properties.getProperty('ndk.dir')
+            if (ndkDirProp != null) {
+                androidNdkDir = new File(ndkDirProp)
+            }
+
         } else {
             String envVar = System.getenv("ANDROID_HOME")
             if (envVar != null) {
@@ -189,6 +201,11 @@
                     androidSdkDir = new File(property)
                 }
             }
+
+            envVar = System.getenv("ANDROID_NDK_HOME")
+            if (envVar != null) {
+                androidNdkDir = new File(envVar)
+            }
         }
     }
 }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/SourceSetSourceProviderWrapper.java b/gradle/src/main/groovy/com/android/build/gradle/internal/SourceSetSourceProviderWrapper.java
new file mode 100644
index 0000000..0cc3f7c
--- /dev/null
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/SourceSetSourceProviderWrapper.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2013 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.build.gradle.internal;
+
+import com.android.annotations.NonNull;
+import com.android.builder.model.SourceProvider;
+import org.gradle.api.tasks.SourceSet;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * An implementation of SourceProvider that's wrapper around a Java SourceSet.
+ * This is useful for the case where we store SourceProviders but don't want to
+ * query the content of the SourceSet at the moment the SourceProvider is created.
+ */
+public class SourceSetSourceProviderWrapper implements SourceProvider {
+
+    @NonNull
+    private final SourceSet sourceSet;
+
+    public SourceSetSourceProviderWrapper(@NonNull SourceSet sourceSet) {
+
+        this.sourceSet = sourceSet;
+    }
+
+    @NonNull
+    @Override
+    public File getManifestFile() {
+        throw new IllegalAccessError("Shouldn't access manifest from SourceSetSourceProviderWrapper");
+    }
+
+    @NonNull
+    @Override
+    public Collection<File> getJavaDirectories() {
+        return sourceSet.getAllJava().getSrcDirs();
+    }
+
+    @NonNull
+    @Override
+    public Collection<File> getResourcesDirectories() {
+        return sourceSet.getResources().getSrcDirs();
+    }
+
+    @NonNull
+    @Override
+    public Collection<File> getAidlDirectories() {
+        return Collections.emptyList();
+    }
+
+    @NonNull
+    @Override
+    public Collection<File> getRenderscriptDirectories() {
+        return Collections.emptyList();
+    }
+
+    @NonNull
+    @Override
+    public Collection<File> getJniDirectories() {
+        return Collections.emptyList();
+    }
+
+    @NonNull
+    @Override
+    public Collection<File> getResDirectories() {
+        return Collections.emptyList();
+    }
+
+    @NonNull
+    @Override
+    public Collection<File> getAssetsDirectories() {
+        return Collections.emptyList();
+    }
+}
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/api/ApplicationVariantImpl.java b/gradle/src/main/groovy/com/android/build/gradle/internal/api/ApplicationVariantImpl.java
index efbbad3..fcc3d0b 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/api/ApplicationVariantImpl.java
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/api/ApplicationVariantImpl.java
@@ -21,23 +21,13 @@
 import com.android.build.gradle.api.ApplicationVariant;
 import com.android.build.gradle.api.TestVariant;
 import com.android.build.gradle.internal.variant.ApplicationVariantData;
-import com.android.build.gradle.tasks.AidlCompile;
+import com.android.build.gradle.internal.variant.BaseVariantData;
 import com.android.build.gradle.tasks.Dex;
-import com.android.build.gradle.tasks.GenerateBuildConfig;
-import com.android.build.gradle.tasks.MergeAssets;
-import com.android.build.gradle.tasks.MergeResources;
 import com.android.build.gradle.tasks.PackageApplication;
-import com.android.build.gradle.tasks.ProcessAndroidResources;
-import com.android.build.gradle.tasks.ProcessManifest;
-import com.android.build.gradle.tasks.RenderscriptCompile;
 import com.android.build.gradle.tasks.ZipAlign;
-import com.android.builder.DefaultBuildType;
 import com.android.builder.DefaultProductFlavor;
 import com.android.builder.model.SigningConfig;
 import org.gradle.api.DefaultTask;
-import org.gradle.api.Task;
-import org.gradle.api.tasks.Copy;
-import org.gradle.api.tasks.compile.JavaCompile;
 
 import java.io.File;
 import java.util.List;
@@ -46,7 +36,7 @@
  * implementation of the {@link ApplicationVariant} interface around an
  * {@link ApplicationVariantData} object.
  */
-public class ApplicationVariantImpl implements ApplicationVariant {
+public class ApplicationVariantImpl extends BaseVariantImpl implements ApplicationVariant {
 
     @NonNull
     private final ApplicationVariantData variantData;
@@ -57,42 +47,17 @@
         this.variantData = variantData;
     }
 
+    @Override
+    protected BaseVariantData getVariantData() {
+        return variantData;
+    }
+
     public void setTestVariant(@Nullable TestVariant testVariant) {
         this.testVariant = testVariant;
     }
 
     @Override
     @NonNull
-    public String getName() {
-        return variantData.getName();
-    }
-
-    @Override
-    @NonNull
-    public String getDescription() {
-        return variantData.getDescription();
-    }
-
-    @Override
-    @NonNull
-    public String getDirName() {
-        return variantData.getDirName();
-    }
-
-    @Override
-    @NonNull
-    public String getBaseName() {
-        return variantData.getBaseName();
-    }
-
-    @Override
-    @NonNull
-    public DefaultBuildType getBuildType() {
-        return variantData.getVariantConfiguration().getBuildType();
-    }
-
-    @Override
-    @NonNull
     public List<DefaultProductFlavor> getProductFlavors() {
         return variantData.getVariantConfiguration().getFlavorConfigs();
     }
@@ -104,12 +69,6 @@
     }
 
     @Override
-    @NonNull
-    public File getOutputFile() {
-        return variantData.getOutputFile();
-    }
-
-    @Override
     public void setOutputFile(@NonNull File outputFile) {
         if (variantData.zipAlignTask != null) {
             variantData.zipAlignTask.setOutputFile(outputFile);
@@ -125,57 +84,6 @@
     }
 
     @Override
-    @NonNull
-    public ProcessManifest getProcessManifest() {
-        return variantData.processManifestTask;
-    }
-
-    @Override
-    @NonNull
-    public AidlCompile getAidlCompile() {
-        return variantData.aidlCompileTask;
-    }
-
-    @Override
-    @NonNull
-    public RenderscriptCompile getRenderscriptCompile() {
-        return variantData.renderscriptCompileTask;
-    }
-
-    @Override
-    public MergeResources getMergeResources() {
-        return variantData.mergeResourcesTask;
-    }
-
-    @Override
-    public MergeAssets getMergeAssets() {
-        return variantData.mergeAssetsTask;
-    }
-
-    @Override
-    @NonNull
-    public ProcessAndroidResources getProcessResources() {
-        return variantData.processResourcesTask;
-    }
-
-    @Override
-    public GenerateBuildConfig getGenerateBuildConfig() {
-        return variantData.generateBuildConfigTask;
-    }
-
-    @Override
-    @NonNull
-    public JavaCompile getJavaCompile() {
-        return variantData.javaCompileTask;
-    }
-
-    @Override
-    @NonNull
-    public Copy getProcessJavaResources() {
-        return variantData.processJavaResources;
-    }
-
-    @Override
     public Dex getDex() {
         return variantData.dexTask;
     }
@@ -191,11 +99,6 @@
     }
 
     @Override
-    public Task getAssemble() {
-        return variantData.assembleTask;
-    }
-
-    @Override
     public DefaultTask getInstall() {
         return variantData.installTask;
     }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/api/BaseVariantImpl.java b/gradle/src/main/groovy/com/android/build/gradle/internal/api/BaseVariantImpl.java
new file mode 100644
index 0000000..894fe8a
--- /dev/null
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/api/BaseVariantImpl.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2013 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.build.gradle.internal.api;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.api.BaseVariant;
+import com.android.build.gradle.internal.variant.BaseVariantData;
+import com.android.build.gradle.tasks.AidlCompile;
+import com.android.build.gradle.tasks.GenerateBuildConfig;
+import com.android.build.gradle.tasks.MergeAssets;
+import com.android.build.gradle.tasks.MergeResources;
+import com.android.build.gradle.tasks.ProcessAndroidResources;
+import com.android.build.gradle.tasks.ProcessManifest;
+import com.android.build.gradle.tasks.RenderscriptCompile;
+import com.android.builder.DefaultBuildType;
+import com.android.builder.DefaultProductFlavor;
+import org.gradle.api.Task;
+import org.gradle.api.tasks.Copy;
+import org.gradle.api.tasks.compile.JavaCompile;
+
+import java.io.File;
+import java.util.Collection;
+
+abstract class BaseVariantImpl implements BaseVariant {
+
+    protected abstract BaseVariantData getVariantData();
+
+    @Override
+    @NonNull
+    public String getName() {
+        return getVariantData().getVariantConfiguration().getFullName();
+    }
+
+    @Override
+    @NonNull
+    public String getDescription() {
+        return getVariantData().getDescription();
+    }
+
+    @Override
+    @NonNull
+    public String getDirName() {
+        return getVariantData().getVariantConfiguration().getDirName();
+    }
+
+    @Override
+    @NonNull
+    public String getBaseName() {
+        return getVariantData().getVariantConfiguration().getBaseName();
+    }
+
+    @NonNull
+    @Override
+    public String getFlavorName() {
+        return getVariantData().getVariantConfiguration().getFlavorName();
+    }
+
+    @Override
+    @NonNull
+    public DefaultBuildType getBuildType() {
+        return getVariantData().getVariantConfiguration().getBuildType();
+    }
+
+    @NonNull
+    @Override
+    public DefaultProductFlavor getConfig() {
+        return getVariantData().getVariantConfiguration().getDefaultConfig();
+    }
+
+    @Override
+    @NonNull
+    public File getOutputFile() {
+        return getVariantData().getOutputFile();
+    }
+
+    @Override
+    @NonNull
+    public ProcessManifest getProcessManifest() {
+        return getVariantData().processManifestTask;
+    }
+
+    @Override
+    @NonNull
+    public AidlCompile getAidlCompile() {
+        return getVariantData().aidlCompileTask;
+    }
+
+    @Override
+    @NonNull
+    public RenderscriptCompile getRenderscriptCompile() {
+        return getVariantData().renderscriptCompileTask;
+    }
+
+    @Override
+    public MergeResources getMergeResources() {
+        return getVariantData().mergeResourcesTask;
+    }
+
+    @Override
+    public MergeAssets getMergeAssets() {
+        return getVariantData().mergeAssetsTask;
+    }
+
+    @Override
+    @NonNull
+    public ProcessAndroidResources getProcessResources() {
+        return getVariantData().processResourcesTask;
+    }
+
+    @Override
+    public GenerateBuildConfig getGenerateBuildConfig() {
+        return getVariantData().generateBuildConfigTask;
+    }
+
+    @Override
+    @NonNull
+    public JavaCompile getJavaCompile() {
+        return getVariantData().javaCompileTask;
+    }
+
+    @Override
+    @NonNull
+    public Copy getProcessJavaResources() {
+        return getVariantData().processJavaResourcesTask;
+    }
+
+    @Override
+    @Nullable
+    public Task getAssemble() {
+        return getVariantData().assembleTask;
+    }
+
+    @Override
+    public void addJavaSourceFoldersToModel(@NonNull File... generatedSourceFolders) {
+        getVariantData().addJavaSourceFoldersToModel(generatedSourceFolders);
+    }
+
+    @Override
+    public void addJavaSourceFoldersToModel(@NonNull Collection<File> generatedSourceFolders) {
+        getVariantData().addJavaSourceFoldersToModel(generatedSourceFolders);
+    }
+
+    @Override
+    public void registerJavaGeneratingTask(@NonNull Task task, @NonNull File... sourceFolders) {
+        getVariantData().registerJavaGeneratingTask(task, sourceFolders);
+    }
+
+    @Override
+    public void registerJavaGeneratingTask(@NonNull Task task, @NonNull Collection<File> sourceFolders) {
+        getVariantData().registerJavaGeneratingTask(task, sourceFolders);
+    }
+}
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/api/DefaultAndroidSourceSet.java b/gradle/src/main/groovy/com/android/build/gradle/internal/api/DefaultAndroidSourceSet.java
index aab4146..0520fa5 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/api/DefaultAndroidSourceSet.java
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/api/DefaultAndroidSourceSet.java
@@ -267,7 +267,6 @@
 
     // --- SourceProvider
 
-
     @NonNull
     @Override
     public Set<File> getJavaDirectories() {
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/api/LibraryVariantImpl.java b/gradle/src/main/groovy/com/android/build/gradle/internal/api/LibraryVariantImpl.java
index a4acfd6..f48a529 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/api/LibraryVariantImpl.java
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/api/LibraryVariantImpl.java
@@ -20,20 +20,9 @@
 import com.android.annotations.Nullable;
 import com.android.build.gradle.api.LibraryVariant;
 import com.android.build.gradle.api.TestVariant;
+import com.android.build.gradle.internal.variant.BaseVariantData;
 import com.android.build.gradle.internal.variant.LibraryVariantData;
-import com.android.build.gradle.tasks.AidlCompile;
-import com.android.build.gradle.tasks.GenerateBuildConfig;
-import com.android.build.gradle.tasks.MergeAssets;
-import com.android.build.gradle.tasks.MergeResources;
-import com.android.build.gradle.tasks.ProcessAndroidResources;
-import com.android.build.gradle.tasks.ProcessManifest;
-import com.android.build.gradle.tasks.RenderscriptCompile;
-import com.android.builder.DefaultBuildType;
-import com.android.builder.DefaultProductFlavor;
-import org.gradle.api.Task;
-import org.gradle.api.tasks.Copy;
 import org.gradle.api.tasks.bundling.Zip;
-import org.gradle.api.tasks.compile.JavaCompile;
 
 import java.io.File;
 
@@ -41,7 +30,7 @@
  * implementation of the {@link LibraryVariant} interface around a
  * {@link LibraryVariantData} object.
  */
-public class LibraryVariantImpl implements LibraryVariant {
+public class LibraryVariantImpl extends BaseVariantImpl implements LibraryVariant {
 
     @NonNull
     private final LibraryVariantData variantData;
@@ -52,48 +41,17 @@
         this.variantData = variantData;
     }
 
+    @Override
+    protected BaseVariantData getVariantData() {
+        return variantData;
+    }
+
     public void setTestVariant(@Nullable TestVariant testVariant) {
         this.testVariant = testVariant;
     }
 
     @Override
     @NonNull
-    public String getName() {
-        return variantData.getName();
-    }
-
-    @Override
-    @NonNull
-    public String getDescription() {
-        return variantData.getDescription();
-    }
-
-    @Override
-    @NonNull
-    public String getDirName() {
-        return variantData.getDirName();
-    }
-
-    @Override
-    @NonNull
-    public String getBaseName() {
-        return variantData.getBaseName();
-    }
-
-    @Override
-    @NonNull
-    public DefaultBuildType getBuildType() {
-        return variantData.getVariantConfiguration().getBuildType();
-    }
-
-    @NonNull
-    @Override
-    public DefaultProductFlavor getConfig() {
-        return variantData.getVariantConfiguration().getDefaultConfig();
-    }
-
-    @Override
-    @NonNull
     public File getOutputFile() {
         return variantData.packageLibTask.getArchivePath();
     }
@@ -111,63 +69,7 @@
     }
 
     @Override
-    @NonNull
-    public ProcessManifest getProcessManifest() {
-        return variantData.processManifestTask;
-    }
-
-    @Override
-    @NonNull
-    public AidlCompile getAidlCompile() {
-        return variantData.aidlCompileTask;
-    }
-
-    @Override
-    @NonNull
-    public RenderscriptCompile getRenderscriptCompile() {
-        return variantData.renderscriptCompileTask;
-    }
-
-    @Override
-    public MergeResources getMergeResources() {
-        return variantData.mergeResourcesTask;
-    }
-
-    @Override
-    public MergeAssets getMergeAssets() {
-        return variantData.mergeAssetsTask;
-    }
-
-    @Override
-    @NonNull
-    public ProcessAndroidResources getProcessResources() {
-        return variantData.processResourcesTask;
-    }
-
-    @Override
-    public GenerateBuildConfig getGenerateBuildConfig() {
-        return variantData.generateBuildConfigTask;
-    }
-
-    @Override
-    @NonNull
-    public JavaCompile getJavaCompile() {
-        return variantData.javaCompileTask;
-    }
-
-    @Override
-    @NonNull
-    public Copy getProcessJavaResources() {
-        return variantData.processJavaResources;
-    }
-
-    @Override
     public Zip getPackageLibrary() {
         return variantData.packageLibTask;
     }
-
-    @Override
-    public Task getAssemble() {
-        return variantData.assembleTask;
-    }
 }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/api/TestVariantImpl.java b/gradle/src/main/groovy/com/android/build/gradle/internal/api/TestVariantImpl.java
index 305c2e5..073c49d 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/api/TestVariantImpl.java
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/api/TestVariantImpl.java
@@ -19,24 +19,14 @@
 import com.android.annotations.NonNull;
 import com.android.build.gradle.api.BaseVariant;
 import com.android.build.gradle.api.TestVariant;
+import com.android.build.gradle.internal.variant.BaseVariantData;
 import com.android.build.gradle.internal.variant.TestVariantData;
-import com.android.build.gradle.tasks.AidlCompile;
 import com.android.build.gradle.tasks.Dex;
-import com.android.build.gradle.tasks.GenerateBuildConfig;
-import com.android.build.gradle.tasks.MergeAssets;
-import com.android.build.gradle.tasks.MergeResources;
 import com.android.build.gradle.tasks.PackageApplication;
-import com.android.build.gradle.tasks.ProcessAndroidResources;
-import com.android.build.gradle.tasks.ProcessManifest;
-import com.android.build.gradle.tasks.RenderscriptCompile;
 import com.android.build.gradle.tasks.ZipAlign;
-import com.android.builder.DefaultBuildType;
 import com.android.builder.DefaultProductFlavor;
 import com.android.builder.model.SigningConfig;
 import org.gradle.api.DefaultTask;
-import org.gradle.api.Task;
-import org.gradle.api.tasks.Copy;
-import org.gradle.api.tasks.compile.JavaCompile;
 
 import java.io.File;
 import java.util.List;
@@ -44,7 +34,7 @@
 /**
  * implementation of the {@link TestVariant} interface around an {@link TestVariantData} object.
  */
-public class TestVariantImpl implements TestVariant {
+public class TestVariantImpl extends BaseVariantImpl implements TestVariant {
 
     @NonNull
     private final TestVariantData variantData;
@@ -56,33 +46,8 @@
     }
 
     @Override
-    @NonNull
-    public String getName() {
-        return variantData.getName();
-    }
-
-    @Override
-    @NonNull
-    public String getDescription() {
-        return variantData.getDescription();
-    }
-
-    @Override
-    @NonNull
-    public String getDirName() {
-        return variantData.getDirName();
-    }
-
-    @Override
-    @NonNull
-    public String getBaseName() {
-        return variantData.getBaseName();
-    }
-
-    @Override
-    @NonNull
-    public DefaultBuildType getBuildType() {
-        return variantData.getVariantConfiguration().getBuildType();
+    protected BaseVariantData getVariantData() {
+        return variantData;
     }
 
     @Override
@@ -98,12 +63,6 @@
     }
 
     @Override
-    @NonNull
-    public File getOutputFile() {
-        return variantData.getOutputFile();
-    }
-
-    @Override
     public void setOutputFile(@NonNull File outputFile) {
         if (variantData.zipAlignTask != null) {
             variantData.zipAlignTask.setOutputFile(outputFile);
@@ -123,57 +82,6 @@
     }
 
     @Override
-    @NonNull
-    public ProcessManifest getProcessManifest() {
-        return variantData.processManifestTask;
-    }
-
-    @Override
-    @NonNull
-    public AidlCompile getAidlCompile() {
-        return variantData.aidlCompileTask;
-    }
-
-    @Override
-    @NonNull
-    public RenderscriptCompile getRenderscriptCompile() {
-        return variantData.renderscriptCompileTask;
-    }
-
-    @Override
-    public MergeResources getMergeResources() {
-        return variantData.mergeResourcesTask;
-    }
-
-    @Override
-    public MergeAssets getMergeAssets() {
-        return variantData.mergeAssetsTask;
-    }
-
-    @Override
-    @NonNull
-    public ProcessAndroidResources getProcessResources() {
-        return variantData.processResourcesTask;
-    }
-
-    @Override
-    public GenerateBuildConfig getGenerateBuildConfig() {
-        return variantData.generateBuildConfigTask;
-    }
-
-    @Override
-    @NonNull
-    public JavaCompile getJavaCompile() {
-        return variantData.javaCompileTask;
-    }
-
-    @Override
-    @NonNull
-    public Copy getProcessJavaResources() {
-        return variantData.processJavaResources;
-    }
-
-    @Override
     public Dex getDex() {
         return variantData.dexTask;
     }
@@ -189,11 +97,6 @@
     }
 
     @Override
-    public Task getAssemble() {
-        return variantData.assembleTask;
-    }
-
-    @Override
     public DefaultTask getInstall() {
         return variantData.installTask;
     }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/AaptOptionsImpl.groovy b/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/AaptOptionsImpl.groovy
index 449a43f..d50371e 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/AaptOptionsImpl.groovy
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/AaptOptionsImpl.groovy
@@ -45,7 +45,7 @@
     }
 
     @Override
-    List<String> getNoCompress() {
+    Collection<String> getNoCompress() {
         return noCompressList
     }
 
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/BuildTypeDsl.groovy b/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/BuildTypeDsl.groovy
index 6c4f97f..6b51bc4 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/BuildTypeDsl.groovy
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/BuildTypeDsl.groovy
@@ -16,11 +16,16 @@
 
 package com.android.build.gradle.internal.dsl
 import com.android.annotations.NonNull
+import com.android.annotations.Nullable
+import com.android.annotations.VisibleForTesting
+import com.android.builder.AndroidBuilder
 import com.android.builder.BuilderConstants
 import com.android.builder.DefaultBuildType
+import com.android.builder.model.NdkConfig
 import com.android.builder.model.SigningConfig
+import org.gradle.api.Action
 import org.gradle.api.internal.file.FileResolver
-
+import org.gradle.internal.reflect.Instantiator
 /**
  * DSL overlay to make methods that accept String... work.
  */
@@ -30,9 +35,28 @@
     @NonNull
     private final FileResolver fileResolver
 
-    BuildTypeDsl(@NonNull String name, @NonNull FileResolver fileResolver) {
+    private final NdkConfigDsl ndkConfig
+
+    public BuildTypeDsl(@NonNull String name,
+                 @NonNull FileResolver fileResolver,
+                 @NonNull Instantiator instantiator) {
         super(name)
         this.fileResolver = fileResolver
+        ndkConfig = instantiator.newInstance(NdkConfigDsl.class)
+    }
+
+    @VisibleForTesting
+    BuildTypeDsl(@NonNull String name,
+                 @NonNull FileResolver fileResolver) {
+        super(name)
+        this.fileResolver = fileResolver
+        ndkConfig = null
+    }
+
+    @Override
+    @Nullable
+    public NdkConfig getNdkConfig() {
+        return ndkConfig;
     }
 
     public void init(SigningConfig debugSigningConfig) {
@@ -58,12 +82,11 @@
 
     // -- DSL Methods. TODO remove once the instantiator does what I expect it to do.
 
-    public void buildConfig(String... lines) {
-        setBuildConfig(lines)
-    }
-
-    public void buildConfig(String line) {
-        setBuildConfig(line)
+    public void buildConfigField(
+            @NonNull String type,
+            @NonNull String name,
+            @NonNull String value) {
+        addBuildConfigField(AndroidBuilder.createClassField(type, name, value));
     }
 
     @NonNull
@@ -86,4 +109,23 @@
         }
         return this;
     }
+
+    @NonNull
+    public BuildTypeDsl consumerProguardFiles(Object... proguardFileArray) {
+        consumerProguardFiles.addAll(fileResolver.resolveFiles(proguardFileArray).files);
+        return this;
+    }
+
+    @NonNull
+    public BuildTypeDsl setConsumerProguardFiles(Iterable<?> proguardFileIterable) {
+        consumerProguardFiles.clear();
+        for (Object proguardFile : proguardFileIterable) {
+            consumerProguardFiles.add(fileResolver.resolve(proguardFile));
+        }
+        return this;
+    }
+
+    void ndk(Action<NdkConfigDsl> action) {
+        action.execute(ndkConfig)
+    }
 }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/BuildTypeFactory.java b/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/BuildTypeFactory.java
index 3bdfe19..486727e 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/BuildTypeFactory.java
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/BuildTypeFactory.java
@@ -40,6 +40,6 @@
 
     @Override
     public DefaultBuildType create(String name) {
-        return instantiator.newInstance(BuildTypeDsl.class, name, fileResolver);
+        return instantiator.newInstance(BuildTypeDsl.class, name, fileResolver, instantiator);
     }
 }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/DexOptionsImpl.groovy b/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/DexOptionsImpl.groovy
index 899337a..767b9d3 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/DexOptionsImpl.groovy
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/DexOptionsImpl.groovy
@@ -26,7 +26,13 @@
     private boolean coreLibraryFlag
 
     @Input
-    private boolean isIncrementalFlag = true
+    private boolean isIncrementalFlag = false
+
+    @Input
+    private boolean isPreDexLibrariesFlag = true
+
+    @Input
+    private boolean isJumboModeFlag = false
 
     @Input
     @Optional
@@ -50,6 +56,24 @@
         return isIncrementalFlag
     }
 
+    @Override
+    boolean getPreDexLibraries() {
+        return isPreDexLibrariesFlag
+    }
+
+    void setPreDexLibraries(boolean flag) {
+        isPreDexLibrariesFlag = flag
+    }
+
+    public void setJumboMode(boolean flag) {
+        isJumboModeFlag = flag
+    }
+
+    @Override
+    boolean getJumboMode() {
+        return isJumboModeFlag
+    }
+
     public void setJavaMaxHeapSize(String theJavaMaxHeapSize) {
         if (theJavaMaxHeapSize.matches("\\d+[kKmMgGtT]?")) {
             javaMaxHeapSize = theJavaMaxHeapSize
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/GroupableProductFlavorDsl.groovy b/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/GroupableProductFlavorDsl.groovy
index d60334b..621ac85 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/GroupableProductFlavorDsl.groovy
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/GroupableProductFlavorDsl.groovy
@@ -18,6 +18,7 @@
 
 import com.android.annotations.NonNull
 import org.gradle.api.internal.file.FileResolver
+import org.gradle.internal.reflect.Instantiator
 
 /**
  * A version of ProductFlavorDsl that can receive a group name
@@ -27,7 +28,10 @@
 
     String flavorGroup
 
-    public GroupableProductFlavorDsl(@NonNull String name, @NonNull FileResolver fileResolver) {
-        super(name, fileResolver)
+    public GroupableProductFlavorDsl(
+            @NonNull String name,
+            @NonNull FileResolver fileResolver,
+            @NonNull Instantiator instantiator) {
+        super(name, fileResolver, instantiator)
     }
 }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/GroupableProductFlavorFactory.java b/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/GroupableProductFlavorFactory.java
index 7ad9043..ed422d9 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/GroupableProductFlavorFactory.java
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/GroupableProductFlavorFactory.java
@@ -40,6 +40,7 @@
 
     @Override
     public GroupableProductFlavorDsl create(String name) {
-        return instantiator.newInstance(GroupableProductFlavorDsl.class, name, fileResolver);
+        return instantiator.newInstance(GroupableProductFlavorDsl.class,
+                name, fileResolver, instantiator);
     }
 }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/NdkConfigDsl.java b/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/NdkConfigDsl.java
new file mode 100644
index 0000000..6b708e5
--- /dev/null
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/NdkConfigDsl.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2013 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.build.gradle.internal.dsl;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.builder.model.NdkConfig;
+import com.google.common.collect.Sets;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.Optional;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Implementation of NdkConfig to be used in the gradle DSL.
+ */
+public class NdkConfigDsl implements NdkConfig, Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private String moduleName;
+    private String cFlags;
+    private Set<String> ldLibs;
+    private Set<String> abiFilters;
+    private String stl;
+
+    public NdkConfigDsl() {
+    }
+
+    public NdkConfigDsl(@NonNull NdkConfigDsl ndkConfig) {
+        moduleName = ndkConfig.moduleName;
+        cFlags = ndkConfig.cFlags;
+        setLdLibs(ndkConfig.ldLibs);
+        setAbiFilters(ndkConfig.abiFilters);
+    }
+
+    @Override
+    @Input @Optional
+    public String getModuleName() {
+        return moduleName;
+    }
+
+    public void setModuleName(String moduleName) {
+        this.moduleName = moduleName;
+    }
+
+    @Override
+    @Input @Optional
+    public String getcFlags() {
+        return cFlags;
+    }
+
+    public void setcFlags(String cFlags) {
+        this.cFlags = cFlags;
+    }
+
+    @Override
+    @Input @Optional
+    public Set<String> getLdLibs() {
+        return ldLibs;
+    }
+
+    @NonNull
+    public NdkConfigDsl ldLibs(String lib) {
+        if (ldLibs == null) {
+            ldLibs = Sets.newHashSet();
+        }
+        ldLibs.add(lib);
+        return this;
+    }
+
+    @NonNull
+    public NdkConfigDsl ldLibs(String... libs) {
+        if (ldLibs == null) {
+            ldLibs = Sets.newHashSetWithExpectedSize(libs.length);
+        }
+        Collections.addAll(ldLibs, libs);
+        return this;
+    }
+
+    @NonNull
+    public NdkConfigDsl setLdLibs(Collection<String> libs) {
+        if (libs != null) {
+            if (abiFilters == null) {
+                abiFilters = Sets.newHashSetWithExpectedSize(libs.size());
+            } else {
+                abiFilters.clear();
+            }
+            for (String filter : libs) {
+                abiFilters.add(filter);
+            }
+        } else {
+            abiFilters = null;
+        }
+        return this;
+    }
+
+
+    @Override
+    @Input @Optional
+    public Set<String> getAbiFilters() {
+        return abiFilters;
+    }
+
+    @NonNull
+    public NdkConfigDsl abiFilter(String filter) {
+        if (abiFilters == null) {
+            abiFilters = Sets.newHashSetWithExpectedSize(2);
+        }
+        abiFilters.add(filter);
+        return this;
+    }
+
+    @NonNull
+    public NdkConfigDsl abiFilters(String... filters) {
+        if (abiFilters == null) {
+            abiFilters = Sets.newHashSetWithExpectedSize(2);
+        }
+        Collections.addAll(abiFilters, filters);
+        return this;
+    }
+
+    @NonNull
+    public NdkConfigDsl setAbiFilters(Collection<String> filters) {
+        if (filters != null) {
+            if (abiFilters == null) {
+                abiFilters = Sets.newHashSetWithExpectedSize(filters.size());
+            } else {
+                abiFilters.clear();
+            }
+            for (String filter : filters) {
+                abiFilters.add(filter);
+            }
+        } else {
+            abiFilters = null;
+        }
+        return this;
+    }
+
+    @Override
+    @Nullable
+    public String getStl() {
+        return stl;
+    }
+
+    public void setStl(String stl) {
+        this.stl = stl;
+    }
+}
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/ProductFlavorDsl.groovy b/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/ProductFlavorDsl.groovy
index 91a27b1..669fc8f 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/ProductFlavorDsl.groovy
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/ProductFlavorDsl.groovy
@@ -15,10 +15,15 @@
  */
 
 package com.android.build.gradle.internal.dsl
-import com.android.annotations.NonNull
-import com.android.builder.DefaultProductFlavor
-import org.gradle.api.internal.file.FileResolver
 
+import com.android.annotations.NonNull
+import com.android.annotations.Nullable
+import com.android.builder.AndroidBuilder
+import com.android.builder.DefaultProductFlavor
+import com.android.builder.model.NdkConfig
+import org.gradle.api.Action
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.internal.reflect.Instantiator
 /**
  * DSL overlay to make methods that accept String... work.
  */
@@ -28,39 +33,80 @@
     @NonNull
     private final FileResolver fileResolver
 
-    ProductFlavorDsl(String name, @NonNull FileResolver fileResolver) {
+    private final NdkConfigDsl ndkConfig
+
+    ProductFlavorDsl(@NonNull String name,
+                     @NonNull FileResolver fileResolver,
+                     @NonNull Instantiator instantiator) {
         super(name)
         this.fileResolver = fileResolver
+
+        ndkConfig = instantiator.newInstance(NdkConfigDsl.class)
+    }
+
+    @Override
+    @Nullable
+    public NdkConfig getNdkConfig() {
+        return ndkConfig;
     }
 
     // -- DSL Methods. TODO remove once the instantiator does what I expect it to do.
 
-    public void buildConfig(String... lines) {
-        setBuildConfig(lines)
-    }
-
-    public void buildConfig(String line) {
-        setBuildConfig(line)
+    public void buildConfigField(
+            @NonNull String type,
+            @NonNull String name,
+            @NonNull String value) {
+        addBuildConfigField(AndroidBuilder.createClassField(type, name, value));
     }
 
     @NonNull
     public ProductFlavorDsl proguardFile(Object proguardFile) {
-        proguardFiles.add(fileResolver.resolve(proguardFile));
-        return this;
+        proguardFiles.add(fileResolver.resolve(proguardFile))
+        return this
     }
 
     @NonNull
     public ProductFlavorDsl proguardFiles(Object... proguardFileArray) {
-        proguardFiles.addAll(fileResolver.resolveFiles(proguardFileArray).files);
-        return this;
+        proguardFiles.addAll(fileResolver.resolveFiles(proguardFileArray).files)
+        return this
     }
 
     @NonNull
     public ProductFlavorDsl setProguardFiles(Iterable<?> proguardFileIterable) {
-        proguardFiles.clear();
+        proguardFiles.clear()
         for (Object proguardFile : proguardFileIterable) {
-            proguardFiles.add(fileResolver.resolve(proguardFile));
+            proguardFiles.add(fileResolver.resolve(proguardFile))
         }
-        return this;
+        return this
+    }
+
+    @NonNull
+    public ProductFlavorDsl consumerProguardFiles(Object... proguardFileArray) {
+        consumerProguardFiles.addAll(fileResolver.resolveFiles(proguardFileArray).files)
+        return this
+    }
+
+    @NonNull
+    public ProductFlavorDsl setconsumerProguardFiles(Iterable<?> proguardFileIterable) {
+        consumerProguardFiles.clear()
+        for (Object proguardFile : proguardFileIterable) {
+            consumerProguardFiles.add(fileResolver.resolve(proguardFile))
+        }
+        return this
+    }
+
+    void ndk(Action<NdkConfigDsl> action) {
+        action.execute(ndkConfig)
+    }
+
+    void resConfig(@NonNull String config) {
+        addResourceConfiguration(config);
+    }
+
+    void resConfigs(@NonNull String... config) {
+        addResourceConfigurations(config);
+    }
+    void resConfigs(@NonNull Collection<String> config) {
+        addResourceConfigurations(config);
     }
 }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/model/AndroidArtifactImpl.java b/gradle/src/main/groovy/com/android/build/gradle/internal/model/AndroidArtifactImpl.java
new file mode 100644
index 0000000..f27a055
--- /dev/null
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/model/AndroidArtifactImpl.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2013 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.build.gradle.internal.model;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.builder.model.AndroidArtifact;
+import com.android.builder.model.Dependencies;
+import com.android.builder.model.SourceProvider;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * Implementation of AndroidArtifact that is serializable
+ */
+public class AndroidArtifactImpl extends BaseArtifactImpl implements AndroidArtifact, Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @NonNull
+    private final File outputFile;
+    private final boolean isSigned;
+    @Nullable
+    private final String signingConfigName;
+    @NonNull
+    private final String packageName;
+    @NonNull
+    private final String sourceGenTaskName;
+    @NonNull
+    private final File generatedManifest;
+    @NonNull
+    private final List<File> generatedSourceFolders;
+    @NonNull
+    private final List<File> generatedResourceFolders;
+
+    AndroidArtifactImpl(@NonNull String name,
+                        @NonNull String assembleTaskName,
+                        @NonNull File outputFile,
+                        boolean isSigned,
+                        @Nullable String signingConfigName,
+                        @NonNull String packageName,
+                        @NonNull String sourceGenTaskName,
+                        @NonNull String javaCompileTaskName,
+                        @NonNull File generatedManifest,
+                        @NonNull List<File> generatedSourceFolders,
+                        @NonNull List<File> generatedResourceFolders,
+                        @NonNull File classesFolder,
+                        @NonNull Dependencies dependencies,
+                        @Nullable SourceProvider variantSourceProvider,
+                        @Nullable SourceProvider multiFlavorSourceProviders) {
+        super(name, assembleTaskName, javaCompileTaskName, classesFolder, dependencies,
+                variantSourceProvider, multiFlavorSourceProviders);
+
+        this.outputFile = outputFile;
+        this.isSigned = isSigned;
+        this.signingConfigName = signingConfigName;
+        this.packageName = packageName;
+        this.sourceGenTaskName = sourceGenTaskName;
+        this.generatedManifest = generatedManifest;
+        this.generatedSourceFolders = generatedSourceFolders;
+        this.generatedResourceFolders = generatedResourceFolders;
+    }
+
+    @NonNull
+    @Override
+    public File getOutputFile() {
+        return outputFile;
+    }
+
+    @Override
+    public boolean isSigned() {
+        return isSigned;
+    }
+
+    @Nullable
+    @Override
+    public String getSigningConfigName() {
+        return signingConfigName;
+    }
+
+    @NonNull
+    @Override
+    public String getPackageName() {
+        return packageName;
+    }
+
+    @NonNull
+    @Override
+    public String getSourceGenTaskName() {
+        return sourceGenTaskName;
+    }
+
+    @NonNull
+    @Override
+    public File getGeneratedManifest() {
+        return generatedManifest;
+    }
+
+    @NonNull
+    @Override
+    public List<File> getGeneratedSourceFolders() {
+        return generatedSourceFolders;
+    }
+
+    @NonNull
+    @Override
+    public List<File> getGeneratedResourceFolders() {
+        return generatedResourceFolders;
+    }
+}
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/model/AndroidLibraryImpl.java b/gradle/src/main/groovy/com/android/build/gradle/internal/model/AndroidLibraryImpl.java
index d931e28..8635ce8 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/model/AndroidLibraryImpl.java
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/model/AndroidLibraryImpl.java
@@ -23,6 +23,7 @@
 
 import java.io.File;
 import java.io.Serializable;
+import java.util.Collection;
 import java.util.List;
 
 public class AndroidLibraryImpl implements AndroidLibrary, Serializable {
@@ -35,9 +36,11 @@
     @NonNull
     private final File folder;
     @NonNull
+    private final File manifest;
+    @NonNull
     private final File jarFile;
     @NonNull
-    private final List<File> localJars;
+    private final Collection<File> localJars;
     @NonNull
     private final File resFolder;
     @NonNull
@@ -61,6 +64,7 @@
         this.dependencies = dependencies;
         bundle = libraryDependency.getBundle();
         folder = libraryDependency.getFolder();
+        manifest = libraryDependency.getManifest();
         jarFile = libraryDependency.getJarFile();
         localJars = libraryDependency.getLocalJars();
         resFolder = libraryDependency.getResFolder();
@@ -100,13 +104,19 @@
 
     @NonNull
     @Override
+    public File getManifest() {
+        return manifest;
+    }
+
+    @NonNull
+    @Override
     public File getJarFile() {
         return jarFile;
     }
 
     @NonNull
     @Override
-    public List<File> getLocalJars() {
+    public Collection<File> getLocalJars() {
         return localJars;
     }
 
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/model/ArtifactInfoImpl.java b/gradle/src/main/groovy/com/android/build/gradle/internal/model/ArtifactInfoImpl.java
deleted file mode 100644
index d400261..0000000
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/model/ArtifactInfoImpl.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2013 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.build.gradle.internal.model;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.builder.model.ArtifactInfo;
-import com.android.builder.model.Dependencies;
-
-import java.io.File;
-import java.io.Serializable;
-import java.util.List;
-
-/**
- * Implementation of ArtifactInfo that is serializable
- */
-public class ArtifactInfoImpl implements ArtifactInfo, Serializable {
-
-    @NonNull
-    private final File outputFile;
-    private final boolean isSigned;
-    @Nullable
-    private final String signingConfigName;
-    @NonNull
-    private final String assembleTaskName;
-    @NonNull
-    private final String packageName;
-    @NonNull
-    private final List<File> generatedSourceFolders;
-    @NonNull
-    private final List<File> generatedResourceFolders;
-    @NonNull
-    private final File classesFolder;
-    @NonNull
-    private final Dependencies dependencies;
-
-
-    ArtifactInfoImpl(@NonNull  String assembleTaskName,
-                     @NonNull  File outputFile,
-                               boolean isSigned,
-                     @Nullable String signingConfigName,
-                     @NonNull  String packageName,
-                     @NonNull  List<File> generatedSourceFolders,
-                     @NonNull  List<File> generatedResourceFolders,
-                     @NonNull  File classesFolder,
-                     @NonNull  Dependencies dependencies) {
-        this.assembleTaskName = assembleTaskName;
-        this.outputFile = outputFile;
-        this.isSigned = isSigned;
-        this.signingConfigName = signingConfigName;
-        this.packageName = packageName;
-        this.generatedSourceFolders = generatedSourceFolders;
-        this.generatedResourceFolders = generatedResourceFolders;
-        this.classesFolder = classesFolder;
-        this.dependencies = dependencies;
-    }
-
-    @NonNull
-    @Override
-    public File getOutputFile() {
-        return outputFile;
-    }
-
-    @Override
-    public boolean isSigned() {
-        return isSigned;
-    }
-
-    @Nullable
-    @Override
-    public String getSigningConfigName() {
-        return signingConfigName;
-    }
-
-    @NonNull
-    @Override
-    public String getPackageName() {
-        return packageName;
-    }
-
-    @NonNull
-    @Override
-    public String getSourceGenTaskName() {
-        return "TODO";
-    }
-
-    @NonNull
-    @Override
-    public String getAssembleTaskName() {
-        return assembleTaskName;
-    }
-
-    @NonNull
-    @Override
-    public List<File> getGeneratedSourceFolders() {
-        return generatedSourceFolders;
-    }
-
-    @NonNull
-    @Override
-    public List<File> getGeneratedResourceFolders() {
-        return generatedResourceFolders;
-    }
-
-    @NonNull
-    @Override
-    public File getClassesFolder() {
-        return classesFolder;
-    }
-
-    @NonNull
-    @Override
-    public Dependencies getDependencies() {
-        return dependencies;
-    }
-}
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/model/ArtifactMetaDataImpl.java b/gradle/src/main/groovy/com/android/build/gradle/internal/model/ArtifactMetaDataImpl.java
new file mode 100644
index 0000000..8f33c2d
--- /dev/null
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/model/ArtifactMetaDataImpl.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2013 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.build.gradle.internal.model;
+
+import com.android.annotations.NonNull;
+import com.android.builder.model.ArtifactMetaData;
+
+import java.io.Serializable;
+
+/**
+ * Implementation of ArtifactMetaData that is serializable
+ */
+public class ArtifactMetaDataImpl implements ArtifactMetaData, Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @NonNull
+    private final String name;
+    private final boolean isTest;
+    private final int type;
+
+    public ArtifactMetaDataImpl(@NonNull String name, boolean isTest, int type) {
+        this.name = name;
+        this.isTest = isTest;
+        this.type = type;
+    }
+
+    @NonNull
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public boolean isTest() {
+        return isTest;
+    }
+
+    @Override
+    public int getType() {
+        return type;
+    }
+}
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/model/BaseArtifactImpl.java b/gradle/src/main/groovy/com/android/build/gradle/internal/model/BaseArtifactImpl.java
new file mode 100644
index 0000000..65c0c87
--- /dev/null
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/model/BaseArtifactImpl.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2013 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.build.gradle.internal.model;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.builder.model.BaseArtifact;
+import com.android.builder.model.Dependencies;
+import com.android.builder.model.SourceProvider;
+
+import java.io.File;
+import java.io.Serializable;
+
+/**
+ * Implementation of BaseArtifact that is serializable
+ */
+class BaseArtifactImpl implements BaseArtifact, Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private final String name;
+    @NonNull
+    private final String assembleTaskName;
+    @NonNull
+    private final String javaCompileTaskName;
+    @NonNull
+    private final File classesFolder;
+    @NonNull
+    private final Dependencies dependencies;
+    @Nullable
+    private final SourceProvider variantSourceProvider;
+    @Nullable
+    private final SourceProvider multiFlavorSourceProviders;
+
+
+    BaseArtifactImpl(@NonNull String name,
+                     @NonNull String assembleTaskName,
+                     @NonNull String javaCompileTaskName,
+                     @NonNull File classesFolder,
+                     @NonNull Dependencies dependencies,
+                     @Nullable SourceProvider variantSourceProvider,
+                     @Nullable SourceProvider multiFlavorSourceProviders) {
+        this.name = name;
+        this.assembleTaskName = assembleTaskName;
+        this.javaCompileTaskName = javaCompileTaskName;
+        this.classesFolder = classesFolder;
+        this.dependencies = dependencies;
+        this.variantSourceProvider = variantSourceProvider;
+        this.multiFlavorSourceProviders = multiFlavorSourceProviders;
+    }
+
+    @NonNull
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @NonNull
+    @Override
+    public String getJavaCompileTaskName() {
+        return javaCompileTaskName;
+    }
+
+    @NonNull
+    @Override
+    public String getAssembleTaskName() {
+        return assembleTaskName;
+    }
+
+    @NonNull
+    @Override
+    public File getClassesFolder() {
+        return classesFolder;
+    }
+
+    @NonNull
+    @Override
+    public Dependencies getDependencies() {
+        return dependencies;
+    }
+
+    @Nullable
+    @Override
+    public SourceProvider getVariantSourceProvider() {
+        return variantSourceProvider;
+    }
+
+    @Nullable
+    @Override
+    public SourceProvider getMultiFlavorSourceProvider() {
+        return multiFlavorSourceProviders;
+    }
+}
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/model/BuildTypeContainerImpl.java b/gradle/src/main/groovy/com/android/build/gradle/internal/model/BuildTypeContainerImpl.java
index db8dc17..a4c302f 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/model/BuildTypeContainerImpl.java
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/model/BuildTypeContainerImpl.java
@@ -17,11 +17,14 @@
 package com.android.build.gradle.internal.model;
 
 import com.android.annotations.NonNull;
-import com.android.builder.model.BuildTypeContainer;
+import com.android.build.gradle.internal.BuildTypeData;
 import com.android.builder.model.BuildType;
+import com.android.builder.model.BuildTypeContainer;
 import com.android.builder.model.SourceProvider;
+import com.android.builder.model.SourceProviderContainer;
 
 import java.io.Serializable;
+import java.util.Collection;
 
 class BuildTypeContainerImpl implements BuildTypeContainer, Serializable {
     private static final long serialVersionUID = 1L;
@@ -30,11 +33,35 @@
     private final BuildType buildType;
     @NonNull
     private final SourceProvider sourceProvider;
+    @NonNull
+    private final Collection<SourceProviderContainer> extraSourceProviders;
 
-    BuildTypeContainerImpl(@NonNull BuildTypeImpl buildType,
-                           @NonNull SourceProviderImpl sourceProvider) {
+    /**
+     * Create a BuildTypeContainer from a BuildTypeData
+     *
+     * @param buildTypeData the build type data
+     * @param sourceProviderContainers collection of extra source providers
+     *
+     * @return a non-null BuildTypeContainer
+     */
+    @NonNull
+    static BuildTypeContainer createBTC(
+            @NonNull BuildTypeData buildTypeData,
+            @NonNull Collection<SourceProviderContainer> sourceProviderContainers) {
+
+        return new BuildTypeContainerImpl(
+                BuildTypeImpl.cloneBuildType(buildTypeData.getBuildType()),
+                SourceProviderImpl.cloneProvider(buildTypeData.getSourceSet()),
+                SourceProviderContainerImpl.cloneCollection(sourceProviderContainers));
+    }
+
+    private BuildTypeContainerImpl(
+            @NonNull BuildTypeImpl buildType,
+            @NonNull SourceProviderImpl sourceProvider,
+            @NonNull Collection<SourceProviderContainer> extraSourceProviders) {
         this.buildType = buildType;
         this.sourceProvider = sourceProvider;
+        this.extraSourceProviders = extraSourceProviders;
     }
 
     @Override
@@ -48,4 +75,10 @@
     public SourceProvider getSourceProvider() {
         return sourceProvider;
     }
+
+    @NonNull
+    @Override
+    public Collection<SourceProviderContainer> getExtraSourceProviders() {
+        return extraSourceProviders;
+    }
 }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/model/BuildTypeImpl.java b/gradle/src/main/groovy/com/android/build/gradle/internal/model/BuildTypeImpl.java
index f5fa46c..e3f5cdc 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/model/BuildTypeImpl.java
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/model/BuildTypeImpl.java
@@ -19,6 +19,8 @@
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.builder.model.BuildType;
+import com.android.builder.model.ClassField;
+import com.android.builder.model.NdkConfig;
 
 import java.io.File;
 import java.io.Serializable;
@@ -111,12 +113,25 @@
 
     @NonNull
     @Override
-    public List<String> getBuildConfig() {
+    public List<ClassField> getBuildConfigFields() {
+        return Collections.emptyList();
+    }
+
+    @NonNull
+    @Override
+    public List<File> getProguardFiles() {
+        return Collections.emptyList();
+    }
+
+    @NonNull
+    @Override
+    public List<File> getConsumerProguardFiles() {
         return Collections.emptyList();
     }
 
     @Override
-    public @NonNull List<File> getProguardFiles() {
-        return Collections.emptyList();
+    @Nullable
+    public NdkConfig getNdkConfig() {
+        return null;
     }
 }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/model/DefaultAndroidProject.java b/gradle/src/main/groovy/com/android/build/gradle/internal/model/DefaultAndroidProject.java
index 589243f..fab28d1 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/model/DefaultAndroidProject.java
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/model/DefaultAndroidProject.java
@@ -17,17 +17,20 @@
 package com.android.build.gradle.internal.model;
 
 import com.android.annotations.NonNull;
-import com.android.builder.model.AndroidProject;
-import com.android.builder.model.BuildTypeContainer;
-import com.android.builder.model.ProductFlavorContainer;
-import com.android.builder.model.Variant;
+import com.android.build.gradle.internal.CompileOptions;
 import com.android.builder.model.AaptOptions;
+import com.android.builder.model.AndroidProject;
+import com.android.builder.model.ArtifactMetaData;
+import com.android.builder.model.BuildTypeContainer;
+import com.android.builder.model.JavaCompileOptions;
+import com.android.builder.model.ProductFlavorContainer;
 import com.android.builder.model.SigningConfig;
-import com.google.common.collect.Maps;
+import com.android.builder.model.Variant;
+import com.google.common.collect.Lists;
 
+import java.io.File;
 import java.io.Serializable;
-import java.util.List;
-import java.util.Map;
+import java.util.Collection;
 
 /**
  * Implementation of the AndroidProject model object.
@@ -42,27 +45,44 @@
     @NonNull
     private final String compileTarget;
     @NonNull
-    private final List<String> bootClasspath;
+    private final Collection<String> bootClasspath;
     @NonNull
-    private final Map<String, SigningConfig> signingConfigs;
+    private final Collection<File> frameworkSource;
+    @NonNull
+    private final Collection<SigningConfig> signingConfigs;
+    @NonNull
+    private final Collection<ArtifactMetaData> extraArtifacts;
+    @NonNull
+    private final Collection<String> unresolvedDependencies;
+    @NonNull
+    private final JavaCompileOptions javaCompileOptions;
     private final boolean isLibrary;
 
-    private final Map<String, BuildTypeContainer> buildTypes = Maps.newHashMap();
-    private final Map<String, ProductFlavorContainer> productFlavors = Maps.newHashMap();
-    private final Map<String, Variant> variants = Maps.newHashMap();
+    private final Collection<BuildTypeContainer> buildTypes = Lists.newArrayList();
+    private final Collection<ProductFlavorContainer> productFlavors = Lists.newArrayList();
+    private final Collection<Variant> variants = Lists.newArrayList();
 
     private ProductFlavorContainer defaultConfig;
 
     DefaultAndroidProject(@NonNull String modelVersion,
-                          @NonNull String name, @NonNull String compileTarget,
-                          @NonNull List<String> bootClasspath,
-                          @NonNull Map<String, SigningConfig> signingConfigs,
+                          @NonNull String name,
+                          @NonNull String compileTarget,
+                          @NonNull Collection<String> bootClasspath,
+                          @NonNull Collection<File> frameworkSource,
+                          @NonNull Collection<SigningConfig> signingConfigs,
+                          @NonNull Collection<ArtifactMetaData> extraArtifacts,
+                          @NonNull Collection<String> unresolvedDependencies,
+                          @NonNull CompileOptions compileOptions,
                           boolean isLibrary) {
         this.modelVersion = modelVersion;
         this.name = name;
         this.compileTarget = compileTarget;
         this.bootClasspath = bootClasspath;
+        this.frameworkSource = frameworkSource;
         this.signingConfigs = signingConfigs;
+        this.extraArtifacts = extraArtifacts;
+        this.unresolvedDependencies = unresolvedDependencies;
+        javaCompileOptions = new DefaultJavaCompileOptions(compileOptions);
         this.isLibrary = isLibrary;
     }
 
@@ -74,26 +94,25 @@
 
     @NonNull
     DefaultAndroidProject addBuildType(@NonNull BuildTypeContainer buildTypeContainer) {
-        buildTypes.put(buildTypeContainer.getBuildType().getName(), buildTypeContainer);
+        buildTypes.add(buildTypeContainer);
         return this;
     }
 
     @NonNull
     DefaultAndroidProject addProductFlavors(
             @NonNull ProductFlavorContainer productFlavorContainer) {
-        productFlavors.put(productFlavorContainer.getProductFlavor().getName(),
-                productFlavorContainer);
+        productFlavors.add(productFlavorContainer);
         return this;
     }
 
     @NonNull
     DefaultAndroidProject addVariant(@NonNull VariantImpl variant) {
-        variants.put(variant.getName(), variant);
+        variants.add(variant);
         return this;
     }
 
-    @NonNull
     @Override
+    @NonNull
     public String getModelVersion() {
         return modelVersion;
     }
@@ -104,28 +123,34 @@
         return name;
     }
 
-    @NonNull
     @Override
+    @NonNull
     public ProductFlavorContainer getDefaultConfig() {
         return defaultConfig;
     }
 
-    @NonNull
     @Override
-    public Map<String, BuildTypeContainer> getBuildTypes() {
+    @NonNull
+    public Collection<BuildTypeContainer> getBuildTypes() {
         return buildTypes;
     }
 
-    @NonNull
     @Override
-    public Map<String, ProductFlavorContainer> getProductFlavors() {
+    @NonNull
+    public Collection<ProductFlavorContainer> getProductFlavors() {
         return productFlavors;
     }
 
+    @Override
+    @NonNull
+    public Collection<Variant> getVariants() {
+        return variants;
+    }
+
     @NonNull
     @Override
-    public Map<String, Variant> getVariants() {
-        return variants;
+    public Collection<ArtifactMetaData> getExtraArtifacts() {
+        return extraArtifacts;
     }
 
     @Override
@@ -133,27 +158,45 @@
         return isLibrary;
     }
 
-    @NonNull
     @Override
+    @NonNull
     public String getCompileTarget() {
         return compileTarget;
     }
 
-    @NonNull
     @Override
-    public List<String> getBootClasspath() {
+    @NonNull
+    public Collection<String> getBootClasspath() {
         return bootClasspath;
     }
 
-    @NonNull
     @Override
-    public Map<String,SigningConfig> getSigningConfigs() {
+    @NonNull
+    public Collection<File> getFrameworkSources() {
+        return frameworkSource;
+    }
+
+    @Override
+    @NonNull
+    public Collection<SigningConfig> getSigningConfigs() {
         return signingConfigs;
     }
 
-    @NonNull
     @Override
+    @NonNull
     public AaptOptions getAaptOptions() {
         return null;
     }
+
+    @Override
+    @NonNull
+    public Collection<String> getUnresolvedDependencies() {
+        return unresolvedDependencies;
+    }
+
+    @Override
+    @NonNull
+    public JavaCompileOptions getJavaCompileOptions() {
+        return javaCompileOptions;
+    }
 }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/model/DefaultJavaCompileOptions.java b/gradle/src/main/groovy/com/android/build/gradle/internal/model/DefaultJavaCompileOptions.java
new file mode 100644
index 0000000..4de4a89
--- /dev/null
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/model/DefaultJavaCompileOptions.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2013 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.build.gradle.internal.model;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.internal.CompileOptions;
+import com.android.builder.model.JavaCompileOptions;
+
+import java.io.Serializable;
+
+/**
+ * Implementation of {@link JavaCompileOptions}.
+ */
+class DefaultJavaCompileOptions implements JavaCompileOptions, Serializable {
+    @NonNull
+    private final String sourceCompatibility;
+    @NonNull
+    private final String targetCompatibility;
+
+    DefaultJavaCompileOptions(@NonNull CompileOptions options) {
+      sourceCompatibility = options.getSourceCompatibility().toString();
+      targetCompatibility = options.getTargetCompatibility().toString();
+    }
+
+    @NonNull
+    @Override
+    public String getSourceCompatibility() {
+        return sourceCompatibility;
+    }
+
+    @NonNull
+    @Override
+    public String getTargetCompatibility() {
+        return targetCompatibility;
+    }
+}
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/model/JavaArtifactImpl.java b/gradle/src/main/groovy/com/android/build/gradle/internal/model/JavaArtifactImpl.java
new file mode 100644
index 0000000..4c7584c
--- /dev/null
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/model/JavaArtifactImpl.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2013 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.build.gradle.internal.model;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.builder.model.Dependencies;
+import com.android.builder.model.JavaArtifact;
+import com.android.builder.model.SourceProvider;
+
+import java.io.File;
+import java.io.Serializable;
+
+/**
+ * Implementation of JavaArtifact that is serializable
+ */
+public class JavaArtifactImpl extends BaseArtifactImpl implements JavaArtifact, Serializable {
+    private static final long serialVersionUID = 1L;
+
+    public static JavaArtifactImpl clone(@NonNull JavaArtifact javaArtifact) {
+        return new JavaArtifactImpl(
+                javaArtifact.getName(),
+                javaArtifact.getAssembleTaskName(),
+                javaArtifact.getJavaCompileTaskName(),
+                javaArtifact.getClassesFolder(),
+                javaArtifact.getDependencies(), // TODO:FixME
+                SourceProviderImpl.cloneProvider(javaArtifact.getVariantSourceProvider()),
+                SourceProviderImpl.cloneProvider(javaArtifact.getMultiFlavorSourceProvider()));
+    }
+
+    public JavaArtifactImpl(@NonNull String name,
+                            @NonNull String assembleTaskName,
+                            @NonNull String javaCompileTaskName,
+                            @NonNull File classesFolder,
+                            @NonNull Dependencies dependencies,
+                            @Nullable SourceProvider variantSourceProvider,
+                            @Nullable SourceProvider multiFlavorSourceProviders) {
+        super(name, assembleTaskName, javaCompileTaskName, classesFolder, dependencies,
+                variantSourceProvider, multiFlavorSourceProviders);
+    }
+}
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/model/ModelBuilder.groovy b/gradle/src/main/groovy/com/android/build/gradle/internal/model/ModelBuilder.groovy
index 45806e4..41ec14f 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/model/ModelBuilder.groovy
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/model/ModelBuilder.groovy
@@ -26,23 +26,27 @@
 import com.android.build.gradle.internal.variant.BaseVariantData
 import com.android.build.gradle.internal.variant.LibraryVariantData
 import com.android.build.gradle.internal.variant.TestVariantData
-import com.android.builder.model.AndroidProject
-import com.android.builder.model.ArtifactInfo
-import com.android.builder.model.BuildTypeContainer
-import com.android.builder.model.ProductFlavorContainer
 import com.android.builder.DefaultProductFlavor
 import com.android.builder.SdkParser
 import com.android.builder.VariantConfiguration
+import com.android.builder.model.AndroidArtifact
+import com.android.builder.model.AndroidProject
+import com.android.builder.model.ArtifactMetaData
+import com.android.builder.model.JavaArtifact
 import com.android.builder.model.SigningConfig
 import com.android.builder.model.SourceProvider
+import com.android.builder.model.SourceProviderContainer
 import com.google.common.collect.Lists
-import com.google.common.collect.Maps
 import org.gradle.api.Project
 import org.gradle.api.plugins.UnknownPluginException
 import org.gradle.tooling.provider.model.ToolingModelBuilder
 
 import java.util.jar.Attributes
 import java.util.jar.Manifest
+
+import static com.android.builder.model.AndroidProject.ARTIFACT_INSTRUMENT_TEST
+import static com.android.builder.model.AndroidProject.ARTIFACT_MAIN
+
 /**
  * Builder for the custom Android model.
  */
@@ -78,36 +82,60 @@
 
         SdkParser sdkParser = basePlugin.getLoadedSdkParser()
         List<String> bootClasspath = basePlugin.runtimeJarList
+        List<File> frameworkSource = Collections.emptyList();
         String compileTarget = sdkParser.target.hashString()
 
+        // list of extra artifacts
+        List<ArtifactMetaData> artifactMetaDataList = Lists.newArrayList(basePlugin.extraArtifacts)
+        // plus the instrumentation test one.
+        artifactMetaDataList.add(
+                new ArtifactMetaDataImpl(
+                        ARTIFACT_INSTRUMENT_TEST,
+                        true /*isTest*/,
+                        ArtifactMetaData.TYPE_ANDROID));
+
         //noinspection GroovyVariableNotAssigned
         DefaultAndroidProject androidProject = new DefaultAndroidProject(
                 getModelVersion(),
                 project.name,
                 compileTarget,
                 bootClasspath,
+                frameworkSource,
                 cloneSigningConfigs(signingConfigs),
+                artifactMetaDataList,
+                basePlugin.unresolvedDependencies,
+                basePlugin.extension.compileOptions,
                 libPlugin != null)
-                    .setDefaultConfig(createPFC(basePlugin.defaultConfigData))
+                    .setDefaultConfig(ProductFlavorContainerImpl.createPFC(
+                        basePlugin.defaultConfigData,
+                        basePlugin.getExtraFlavorSourceProviders(basePlugin.defaultConfigData.productFlavor.name)))
 
         if (appPlugin != null) {
             for (BuildTypeData btData : appPlugin.buildTypes.values()) {
-                androidProject.addBuildType(createBTC(btData))
+                androidProject.addBuildType(BuildTypeContainerImpl.createBTC(
+                        btData,
+                        basePlugin.getExtraBuildTypeSourceProviders(btData.buildType.name)))
             }
             for (ProductFlavorData pfData : appPlugin.productFlavors.values()) {
-                androidProject.addProductFlavors(createPFC(pfData))
+                androidProject.addProductFlavors(ProductFlavorContainerImpl.createPFC(
+                        pfData,
+                        basePlugin.getExtraFlavorSourceProviders(pfData.productFlavor.name)))
             }
 
         } else if (libPlugin != null) {
-            androidProject.addBuildType(createBTC(libPlugin.debugBuildTypeData))
-                          .addBuildType(createBTC(libPlugin.releaseBuildTypeData))
+            androidProject.addBuildType(BuildTypeContainerImpl.createBTC(
+                        libPlugin.debugBuildTypeData,
+                        basePlugin.getExtraBuildTypeSourceProviders(libPlugin.debugBuildTypeData.buildType.name)))
+                 .addBuildType(BuildTypeContainerImpl.createBTC(
+                        libPlugin.releaseBuildTypeData,
+                        basePlugin.getExtraBuildTypeSourceProviders(libPlugin.releaseBuildTypeData.buildType.name)))
         }
 
         Set<Project> gradleProjects = project.getRootProject().getAllprojects();
 
         for (BaseVariantData variantData : basePlugin.variantDataList) {
             if (!(variantData instanceof TestVariantData)) {
-                androidProject.addVariant(createVariant(variantData, gradleProjects))
+                androidProject.addVariant(createVariant(variantData, basePlugin, gradleProjects))
             }
         }
 
@@ -137,6 +165,7 @@
 
     @NonNull
     private static VariantImpl createVariant(@NonNull BaseVariantData variantData,
+                                             @NonNull BasePlugin basePlugin,
                                              @NonNull Set<Project> gradleProjects) {
         TestVariantData testVariantData = null
         if (variantData instanceof ApplicationVariantData ||
@@ -144,24 +173,43 @@
             testVariantData = variantData.testVariantData
         }
 
-        ArtifactInfo mainArtifact = createArtifactInfo(variantData, gradleProjects)
-        ArtifactInfo testArtifact = testVariantData != null ?
-            createArtifactInfo(testVariantData, gradleProjects) : null
+        AndroidArtifact mainArtifact = createArtifactInfo(
+                ARTIFACT_MAIN, variantData, basePlugin, gradleProjects)
+
+        String variantName = variantData.variantConfiguration.fullName
+
+        // extra Android Artifacts
+        AndroidArtifact testArtifact = testVariantData != null ?
+                createArtifactInfo(ARTIFACT_INSTRUMENT_TEST, testVariantData, basePlugin, gradleProjects) : null
+
+        List<AndroidArtifact> extraAndroidArtifacts = Lists.newArrayList(
+                basePlugin.getExtraAndroidArtifacts(variantName))
+        if (testArtifact != null) {
+            extraAndroidArtifacts.add(testArtifact)
+        }
+
+        // extra Java Artifacts
+        List<JavaArtifact> extraJavaArtifacts = Lists.newArrayList(
+                basePlugin.getExtraJavaArtifacts(variantName))
 
         VariantImpl variant = new VariantImpl(
-                variantData.name,
-                variantData.baseName,
+                variantName,
+                variantData.variantConfiguration.baseName,
                 variantData.variantConfiguration.buildType.name,
                 getProductFlavorNames(variantData),
                 ProductFlavorImpl.cloneFlavor(variantData.variantConfiguration.mergedFlavor),
                 mainArtifact,
-                testArtifact)
+                extraAndroidArtifacts,
+                extraJavaArtifacts)
 
         return variant
     }
 
-    private static ArtifactInfo createArtifactInfo(@NonNull BaseVariantData variantData,
-                                                   @NonNull Set<Project> gradleProjects) {
+    private static AndroidArtifact createArtifactInfo(
+            @NonNull String name,
+            @NonNull BaseVariantData variantData,
+            @NonNull BasePlugin basePlugin,
+            @NonNull Set<Project> gradleProjects) {
         VariantConfiguration vC = variantData.variantConfiguration
 
         SigningConfig signingConfig = vC.signingConfig
@@ -170,17 +218,40 @@
             signingConfigName = signingConfig.name
         }
 
-        return new ArtifactInfoImpl(
+        SourceProvider variantSourceProvider = null;
+        SourceProvider multiFlavorSourceProvider = null;
+
+        if (ARTIFACT_MAIN.equals(name)) {
+            variantSourceProvider = variantData.variantConfiguration.variantSourceProvider
+            multiFlavorSourceProvider = variantData.variantConfiguration.multiFlavorSourceProvider
+        } else {
+            SourceProviderContainer container = getSourceProviderContainer(
+                    basePlugin.getExtraVariantSourceProviders(variantData.getVariantConfiguration().getFullName()),
+                    name)
+            if (container != null) {
+                variantSourceProvider = container.sourceProvider
+            }
+        }
+
+        variantSourceProvider = variantSourceProvider != null ? SourceProviderImpl.cloneProvider(variantSourceProvider) : null
+        multiFlavorSourceProvider = multiFlavorSourceProvider != null ? SourceProviderImpl.cloneProvider(multiFlavorSourceProvider) : null
+
+        return new AndroidArtifactImpl(
+                name,
                 variantData.assembleTask.name,
                 variantData.outputFile,
                 vC.isSigningReady(),
                 signingConfigName,
                 vC.packageName,
+                variantData.sourceGenTask.name,
+                variantData.javaCompileTask.name,
+                variantData.processManifestTask.manifestOutputFile,
                 getGeneratedSourceFolders(variantData),
                 getGeneratedResourceFolders(variantData),
                 variantData.javaCompileTask.destinationDir,
-                DependenciesImpl.cloneDependencies(variantData.variantDependency, gradleProjects)
-        )
+                DependenciesImpl.cloneDependencies(variantData.variantDependency, gradleProjects),
+                variantSourceProvider,
+                multiFlavorSourceProvider)
     }
 
     @NonNull
@@ -204,8 +275,15 @@
 
         folders.add(variantData.processResourcesTask.sourceOutputDir)
         folders.add(variantData.aidlCompileTask.sourceOutputDir)
-        folders.add(variantData.renderscriptCompileTask.sourceOutputDir)
         folders.add(variantData.generateBuildConfigTask.sourceOutputDir)
+        if (!variantData.variantConfiguration.mergedFlavor.renderscriptNdkMode) {
+            folders.add(variantData.renderscriptCompileTask.sourceOutputDir)
+        }
+
+        List<File> extraFolders = variantData.extraGeneratedSourceFolders
+        if (extraFolders != null) {
+            folders.addAll(extraFolders)
+        }
 
         return folders
     }
@@ -219,54 +297,29 @@
         return Collections.singletonList(variantData.renderscriptCompileTask.resOutputDir)
     }
 
-    /**
-     * Create a ProductFlavorContainer from a ProductFlavorData
-     * @param productFlavorData the product flavor data
-     * @return a non-null ProductFlavorContainer
-     */
     @NonNull
-    private static ProductFlavorContainer createPFC(@NonNull ProductFlavorData productFlavorData) {
-        return new ProductFlavorContainerImpl(
-                ProductFlavorImpl.cloneFlavor(productFlavorData.productFlavor),
-                SourceProviderImpl.cloneProvider((SourceProvider) productFlavorData.sourceSet),
-                SourceProviderImpl.cloneProvider((SourceProvider) productFlavorData.testSourceSet))
-    }
-
-    /**
-     * Create a BuildTypeContainer from a BuildTypeData
-     * @param buildTypeData the build type data
-     * @return a non-null BuildTypeContainer
-     */
-    @NonNull
-    private static BuildTypeContainer createBTC(@NonNull BuildTypeData buildTypeData) {
-        return new BuildTypeContainerImpl(
-                BuildTypeImpl.cloneBuildType(buildTypeData.buildType),
-                SourceProviderImpl.cloneProvider((SourceProvider) buildTypeData.sourceSet))
-    }
-
-    @NonNull
-    private static Map<String, SigningConfig> cloneSigningConfigs(
+    private static Collection<SigningConfig> cloneSigningConfigs(
             @NonNull Collection<SigningConfig> signingConfigs) {
-        Map<String, SigningConfig> results = Maps.newHashMapWithExpectedSize(signingConfigs.size())
+        Collection<SigningConfig> results = Lists.newArrayListWithCapacity(signingConfigs.size())
 
         for (SigningConfig signingConfig : signingConfigs) {
-            SigningConfig clonedSigningConfig = createSigningConfig(signingConfig)
-            results.put(clonedSigningConfig.name, clonedSigningConfig)
+            results.add(SigningConfigImpl.createSigningConfig(signingConfig))
         }
 
         return results
     }
 
-    @NonNull
-    private static SigningConfig createSigningConfig(@NonNull SigningConfig signingConfig) {
-        return new SigningConfigImpl(
-                signingConfig.getName(),
-                signingConfig.getStoreFile(),
-                signingConfig.getStorePassword(),
-                signingConfig.getKeyAlias(),
-                signingConfig.getKeyPassword(),
-                signingConfig.getStoreType(),
-                signingConfig.isSigningReady())
+    @Nullable
+    private static SourceProviderContainer getSourceProviderContainer(
+            @NonNull Collection<SourceProviderContainer> items,
+            @NonNull String name) {
+        for (SourceProviderContainer item : items) {
+            if (name.equals(item.getArtifactName())) {
+                return item;
+            }
+        }
+
+        return null;
     }
 
     /**
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/model/ProductFlavorContainerImpl.java b/gradle/src/main/groovy/com/android/build/gradle/internal/model/ProductFlavorContainerImpl.java
index dac38e6..92fa794 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/model/ProductFlavorContainerImpl.java
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/model/ProductFlavorContainerImpl.java
@@ -17,11 +17,16 @@
 package com.android.build.gradle.internal.model;
 
 import com.android.annotations.NonNull;
-import com.android.builder.model.ProductFlavorContainer;
+import com.android.build.gradle.internal.ProductFlavorData;
+import com.android.builder.model.AndroidProject;
 import com.android.builder.model.ProductFlavor;
+import com.android.builder.model.ProductFlavorContainer;
 import com.android.builder.model.SourceProvider;
+import com.android.builder.model.SourceProviderContainer;
 
 import java.io.Serializable;
+import java.util.Collection;
+import java.util.List;
 
 /**
  */
@@ -33,15 +38,43 @@
     @NonNull
     private final SourceProvider sourceProvider;
     @NonNull
-    private final SourceProvider testSourceProvider;
+    private final Collection<SourceProviderContainer> extraSourceProviders;
 
-    ProductFlavorContainerImpl(@NonNull ProductFlavorImpl productFlavor,
-                               @NonNull SourceProviderImpl sourceProvider,
-                               @NonNull SourceProviderImpl testSourceProvider) {
+    /**
+     * Create a ProductFlavorContainer from a ProductFlavorData
+     *
+     * @param productFlavorData the product flavor data
+     * @param sourceProviderContainers collection of extra source providers
+     *
+     * @return a non-null ProductFlavorContainer
+     */
+    @NonNull
+    static ProductFlavorContainer createPFC(
+            @NonNull ProductFlavorData productFlavorData,
+            @NonNull Collection<SourceProviderContainer> sourceProviderContainers) {
+
+        List<SourceProviderContainer> clonedContainer = SourceProviderContainerImpl.cloneCollection(sourceProviderContainers);
+
+        // instrument test Source Provider
+        SourceProviderContainer testASP = SourceProviderContainerImpl.create(
+                AndroidProject.ARTIFACT_INSTRUMENT_TEST, productFlavorData.getTestSourceSet());
+
+        clonedContainer.add(testASP);
+
+        return new ProductFlavorContainerImpl(
+                ProductFlavorImpl.cloneFlavor(productFlavorData.getProductFlavor()),
+                SourceProviderImpl.cloneProvider(productFlavorData.getSourceSet()),
+                clonedContainer);
+    }
+
+    private ProductFlavorContainerImpl(
+            @NonNull ProductFlavorImpl productFlavor,
+            @NonNull SourceProviderImpl sourceProvider,
+            @NonNull Collection<SourceProviderContainer> extraSourceProviders) {
 
         this.productFlavor = productFlavor;
         this.sourceProvider = sourceProvider;
-        this.testSourceProvider = testSourceProvider;
+        this.extraSourceProviders = extraSourceProviders;
     }
 
     @NonNull
@@ -58,7 +91,7 @@
 
     @NonNull
     @Override
-    public SourceProvider getTestSourceProvider() {
-        return testSourceProvider;
+    public Collection<SourceProviderContainer> getExtraSourceProviders() {
+        return extraSourceProviders;
     }
 }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/model/ProductFlavorImpl.java b/gradle/src/main/groovy/com/android/build/gradle/internal/model/ProductFlavorImpl.java
index db4cb6b..9f25e30 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/model/ProductFlavorImpl.java
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/model/ProductFlavorImpl.java
@@ -18,12 +18,17 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
+import com.android.builder.model.ClassField;
+import com.android.builder.model.NdkConfig;
 import com.android.builder.model.ProductFlavor;
+import com.google.common.collect.Sets;
 
 import java.io.File;
 import java.io.Serializable;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Implementation of ProductFlavor that is serializable. Objects used in the DSL cannot be
@@ -36,12 +41,16 @@
     private int mMinSdkVersion = -1;
     private int mTargetSdkVersion = -1;
     private int mRenderscriptTargetApi = -1;
+    private boolean mRenderscriptSupportMode = false;
+    private boolean mRenderscriptNdkMode = false;
     private int mVersionCode = -1;
     private String mVersionName = null;
     private String mPackageName = null;
     private String mTestPackageName = null;
     private String mTestInstrumentationRunner = null;
-
+    private Boolean mTestHandleProfiling = null;
+    private Boolean mTestFunctionalTest = null;
+    private Set<String> mResourceConfigurations = null;
 
     @NonNull
     static ProductFlavorImpl cloneFlavor(ProductFlavor productFlavor) {
@@ -51,6 +60,8 @@
         clonedFlavor.mMinSdkVersion = productFlavor.getMinSdkVersion();
         clonedFlavor.mTargetSdkVersion = productFlavor.getTargetSdkVersion();
         clonedFlavor.mRenderscriptTargetApi = productFlavor.getRenderscriptTargetApi();
+        clonedFlavor.mRenderscriptSupportMode = productFlavor.getRenderscriptSupportMode();
+        clonedFlavor.mRenderscriptNdkMode = productFlavor.getRenderscriptNdkMode();
 
         clonedFlavor.mVersionCode = productFlavor.getVersionCode();
         clonedFlavor.mVersionName = productFlavor.getVersionName();
@@ -59,6 +70,11 @@
 
         clonedFlavor.mTestPackageName = productFlavor.getTestPackageName();
         clonedFlavor.mTestInstrumentationRunner = productFlavor.getTestInstrumentationRunner();
+        clonedFlavor.mTestHandleProfiling = productFlavor.getTestHandleProfiling();
+        clonedFlavor.mTestFunctionalTest = productFlavor.getTestFunctionalTest();
+
+        clonedFlavor.mResourceConfigurations = Sets.newHashSet(
+                productFlavor.getResourceConfigurations());
 
         return clonedFlavor;
     }
@@ -104,6 +120,16 @@
         return mRenderscriptTargetApi;
     }
 
+    @Override
+    public boolean getRenderscriptSupportMode() {
+        return mRenderscriptSupportMode;
+    }
+
+    @Override
+    public boolean getRenderscriptNdkMode() {
+        return mRenderscriptNdkMode;
+    }
+
     @Nullable
     @Override
     public String getTestPackageName() {
@@ -116,24 +142,22 @@
         return mTestInstrumentationRunner;
     }
 
+    @Nullable
     @Override
-    public String toString() {
-        return "ProductFlavorImpl{" +
-                "name='" + name + '\'' +
-                ", mMinSdkVersion=" + mMinSdkVersion +
-                ", mTargetSdkVersion=" + mTargetSdkVersion +
-                ", mRenderscriptTargetApi=" + mRenderscriptTargetApi +
-                ", mVersionCode=" + mVersionCode +
-                ", mVersionName='" + mVersionName + '\'' +
-                ", mPackageName='" + mPackageName + '\'' +
-                ", mTestPackageName='" + mTestPackageName + '\'' +
-                ", mTestInstrumentationRunner='" + mTestInstrumentationRunner + '\'' +
-                '}';
+    public Boolean getTestHandleProfiling() {
+        return mTestHandleProfiling;
     }
 
+    @Nullable
+    @Override
+    public Boolean getTestFunctionalTest() {
+        return mTestFunctionalTest;
+    }
+
+
     @NonNull
     @Override
-    public List<String> getBuildConfig() {
+    public List<ClassField> getBuildConfigFields() {
         return Collections.emptyList();
     }
 
@@ -142,4 +166,42 @@
     public List<File> getProguardFiles() {
         return Collections.emptyList();
     }
+
+    @NonNull
+    @Override
+    public List<File> getConsumerProguardFiles() {
+        return Collections.emptyList();
+    }
+
+    @Override
+    @Nullable
+    public NdkConfig getNdkConfig() {
+        return null;
+    }
+
+    @NonNull
+    @Override
+    public Collection<String> getResourceConfigurations() {
+        return mResourceConfigurations;
+    }
+
+    @Override
+    public String toString() {
+        return "ProductFlavorImpl{" +
+                "name='" + name + '\'' +
+                ", mMinSdkVersion=" + mMinSdkVersion +
+                ", mTargetSdkVersion=" + mTargetSdkVersion +
+                ", mRenderscriptTargetApi=" + mRenderscriptTargetApi +
+                ", mRenderscriptSupportMode=" + mRenderscriptSupportMode +
+                ", mRenderscriptNdkMode=" + mRenderscriptNdkMode +
+                ", mVersionCode=" + mVersionCode +
+                ", mVersionName='" + mVersionName + '\'' +
+                ", mPackageName='" + mPackageName + '\'' +
+                ", mTestPackageName='" + mTestPackageName + '\'' +
+                ", mTestInstrumentationRunner='" + mTestInstrumentationRunner + '\'' +
+                ", mTestHandleProfiling='" + mTestHandleProfiling + '\'' +
+                ", mTestFunctionalTest='" + mTestFunctionalTest + '\'' +
+                ", mResourceConfigurations='" + mResourceConfigurations + '\'' +
+                '}';
+    }
 }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/model/SigningConfigImpl.java b/gradle/src/main/groovy/com/android/build/gradle/internal/model/SigningConfigImpl.java
index 06e219c..19c867c 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/model/SigningConfigImpl.java
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/model/SigningConfigImpl.java
@@ -44,7 +44,19 @@
     private final String storeType;
     private final boolean signingReady;
 
-    SigningConfigImpl(@NonNull  String name,
+    @NonNull
+    static SigningConfig createSigningConfig(@NonNull SigningConfig signingConfig) {
+        return new SigningConfigImpl(
+                signingConfig.getName(),
+                signingConfig.getStoreFile(),
+                signingConfig.getStorePassword(),
+                signingConfig.getKeyAlias(),
+                signingConfig.getKeyPassword(),
+                signingConfig.getStoreType(),
+                signingConfig.isSigningReady());
+    }
+
+    private SigningConfigImpl(@NonNull  String name,
                       @Nullable File storeFile,
                       @Nullable String storePassword,
                       @Nullable String keyAlias,
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/model/SourceProviderContainerImpl.java b/gradle/src/main/groovy/com/android/build/gradle/internal/model/SourceProviderContainerImpl.java
new file mode 100644
index 0000000..fe41180
--- /dev/null
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/model/SourceProviderContainerImpl.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2013 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.build.gradle.internal.model;
+
+import com.android.annotations.NonNull;
+import com.android.builder.model.SourceProvider;
+import com.android.builder.model.SourceProviderContainer;
+import com.google.common.collect.Lists;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Implementation of SourceProviderContainer that is serializable and is meant to be used
+ * in the model sent to the tooling API.
+ *
+ * It also provides convenient methods to create an instance, cloning the original
+ * SourceProvider.
+ *
+ * When the source Provider is cloned, its values are queried and then statically stored.
+ * Any further change through the DSL will not be impact. Therefore instances of this class
+ * should only be used when the model is built.
+ *
+ * To create more dynamic isntances of SourceProviderContainer, use
+ * {@link com.android.build.gradle.internal.variant.DefaultSourceProviderContainer}
+ */
+class SourceProviderContainerImpl implements SourceProviderContainer, Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @NonNull
+    private final String name;
+    @NonNull
+    private final SourceProvider sourceProvider;
+
+    /**
+     * Create a {@link SourceProviderContainer} that is serializable to
+     * use in the model sent through the tooling API.
+     *
+     * @param sourceProviderContainer the source provider
+     *
+     * @return a non-null SourceProviderContainer
+     */
+    @NonNull
+    static SourceProviderContainer clone(
+            @NonNull SourceProviderContainer sourceProviderContainer) {
+        return create(
+                sourceProviderContainer.getArtifactName(),
+                sourceProviderContainer.getSourceProvider());
+    }
+
+    @NonNull
+    static List<SourceProviderContainer> cloneCollection(@NonNull Collection<SourceProviderContainer> containers) {
+        List<SourceProviderContainer> clones = Lists.newArrayListWithCapacity(containers.size());
+
+        for (SourceProviderContainer container : containers) {
+            clones.add(clone(container));
+        }
+
+        return clones;
+    }
+
+    @NonNull
+    static SourceProviderContainer create(
+            @NonNull String name,
+            @NonNull SourceProvider sourceProvider) {
+        return new SourceProviderContainerImpl(name,
+                SourceProviderImpl.cloneProvider(sourceProvider));
+    }
+
+    private SourceProviderContainerImpl(@NonNull String name,
+                                        @NonNull SourceProvider sourceProvider) {
+        this.name = name;
+        this.sourceProvider = sourceProvider;
+    }
+
+    @NonNull
+    @Override
+    public String getArtifactName() {
+        return name;
+    }
+
+    @NonNull
+    @Override
+    public SourceProvider getSourceProvider() {
+        return sourceProvider;
+    }
+}
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/model/SourceProviderImpl.java b/gradle/src/main/groovy/com/android/build/gradle/internal/model/SourceProviderImpl.java
index 7d0293b..de02737 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/model/SourceProviderImpl.java
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/model/SourceProviderImpl.java
@@ -18,10 +18,11 @@
 
 import com.android.annotations.NonNull;
 import com.android.builder.model.SourceProvider;
+import com.google.common.collect.Lists;
 
 import java.io.File;
 import java.io.Serializable;
-import java.util.Set;
+import java.util.Collection;
 
 /**
  * Implementation of SourceProvider that is serializable. Objects used in the DSL cannot be
@@ -31,13 +32,13 @@
     private static final long serialVersionUID = 1L;
 
     private File manifestFile;
-    private Set<File> javaDirs;
-    private Set<File> resourcesDirs;
-    private Set<File> aidlDirs;
-    private Set<File> rsDirs;
-    private Set<File> jniDirs;
-    private Set<File> resDirs;
-    private Set<File> assetsDirs;
+    private Collection<File> javaDirs;
+    private Collection<File> resourcesDirs;
+    private Collection<File> aidlDirs;
+    private Collection<File> rsDirs;
+    private Collection<File> jniDirs;
+    private Collection<File> resDirs;
+    private Collection<File> assetsDirs;
 
     @NonNull
     static SourceProviderImpl cloneProvider(SourceProvider sourceProvider) {
@@ -55,6 +56,17 @@
         return sourceProviderClone;
     }
 
+    @NonNull
+    static Collection<SourceProvider> cloneCollection(
+            @NonNull Collection<SourceProvider> sourceProviders) {
+        Collection<SourceProvider> results = Lists.newArrayListWithCapacity(sourceProviders.size());
+        for (SourceProvider sourceProvider : sourceProviders) {
+            results.add(SourceProviderImpl.cloneProvider(sourceProvider));
+        }
+
+        return results;
+    }
+
     private SourceProviderImpl() {
     }
 
@@ -66,43 +78,43 @@
 
     @NonNull
     @Override
-    public Set<File> getJavaDirectories() {
+    public Collection<File> getJavaDirectories() {
         return javaDirs;
     }
 
     @NonNull
     @Override
-    public Set<File> getResourcesDirectories() {
+    public Collection<File> getResourcesDirectories() {
         return resourcesDirs;
     }
 
     @NonNull
     @Override
-    public Set<File> getAidlDirectories() {
+    public Collection<File> getAidlDirectories() {
         return aidlDirs;
     }
 
     @NonNull
     @Override
-    public Set<File> getRenderscriptDirectories() {
+    public Collection<File> getRenderscriptDirectories() {
         return rsDirs;
     }
 
     @NonNull
     @Override
-    public Set<File> getJniDirectories() {
+    public Collection<File> getJniDirectories() {
         return jniDirs;
     }
 
     @NonNull
     @Override
-    public Set<File> getResDirectories() {
+    public Collection<File> getResDirectories() {
         return resDirs;
     }
 
     @NonNull
     @Override
-    public Set<File> getAssetsDirectories() {
+    public Collection<File> getAssetsDirectories() {
         return assetsDirs;
     }
 
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/model/VariantImpl.java b/gradle/src/main/groovy/com/android/build/gradle/internal/model/VariantImpl.java
index f9a5c86..b8a04e6 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/model/VariantImpl.java
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/model/VariantImpl.java
@@ -17,12 +17,13 @@
 package com.android.build.gradle.internal.model;
 
 import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.builder.model.ArtifactInfo;
-import com.android.builder.model.Variant;
+import com.android.builder.model.AndroidArtifact;
+import com.android.builder.model.JavaArtifact;
 import com.android.builder.model.ProductFlavor;
+import com.android.builder.model.Variant;
 
 import java.io.Serializable;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 
@@ -43,24 +44,28 @@
     @NonNull
     private final ProductFlavor mergedFlavor;
     @NonNull
-    private final ArtifactInfo mainArtifactInfo;
-    @Nullable
-    private final ArtifactInfo testArtifactInfo;
+    private final AndroidArtifact mainArtifactInfo;
+    @NonNull
+    private final Collection<AndroidArtifact> extraAndroidArtifacts;
+    @NonNull
+    private final Collection<JavaArtifact> extraJavaArtifacts;
 
-    VariantImpl(@NonNull  String name,
-                @NonNull  String displayName,
-                @NonNull  String buildTypeName,
-                @NonNull  List<String> productFlavorNames,
-                @NonNull  ProductFlavorImpl mergedFlavor,
-                @NonNull  ArtifactInfo mainArtifactInfo,
-                @Nullable ArtifactInfo testArtifactInfo) {
+    VariantImpl(@NonNull String name,
+                @NonNull String displayName,
+                @NonNull String buildTypeName,
+                @NonNull List<String> productFlavorNames,
+                @NonNull ProductFlavorImpl mergedFlavor,
+                @NonNull AndroidArtifact mainArtifactInfo,
+                @NonNull Collection<AndroidArtifact> extraAndroidArtifacts,
+                @NonNull Collection<JavaArtifact> extraJavaArtifacts) {
         this.name = name;
         this.displayName = displayName;
         this.buildTypeName = buildTypeName;
         this.productFlavorNames = productFlavorNames;
         this.mergedFlavor = mergedFlavor;
         this.mainArtifactInfo = mainArtifactInfo;
-        this.testArtifactInfo = testArtifactInfo;
+        this.extraAndroidArtifacts = extraAndroidArtifacts;
+        this.extraJavaArtifacts = extraJavaArtifacts;
     }
 
     @Override
@@ -95,19 +100,19 @@
 
     @NonNull
     @Override
-    public ArtifactInfo getMainArtifactInfo() {
+    public AndroidArtifact getMainArtifact() {
         return mainArtifactInfo;
     }
 
-    @Nullable
+    @NonNull
     @Override
-    public ArtifactInfo getTestArtifactInfo() {
-        return testArtifactInfo;
+    public Collection<AndroidArtifact> getExtraAndroidArtifacts() {
+        return extraAndroidArtifacts;
     }
 
-    @Override
     @NonNull
-    public List<String> getResourceConfigurations() {
-        return Collections.emptyList();
+    @Override
+    public Collection<JavaArtifact> getExtraJavaArtifacts() {
+        return extraJavaArtifacts;
     }
 }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/tasks/DependencyBasedCompileTask.groovy b/gradle/src/main/groovy/com/android/build/gradle/internal/tasks/DependencyBasedCompileTask.groovy
index 4e10aa8..f264bc9 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/tasks/DependencyBasedCompileTask.groovy
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/tasks/DependencyBasedCompileTask.groovy
@@ -15,7 +15,6 @@
  */
 
 package com.android.build.gradle.internal.tasks
-
 import com.android.annotations.NonNull
 import com.android.annotations.Nullable
 import com.android.builder.compiling.DependencyFileProcessor
@@ -28,7 +27,6 @@
 import org.gradle.api.tasks.OutputDirectory
 
 import java.util.concurrent.Callable
-
 /**
  * Base task for source generators that generate and use dependency files.
  */
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/tasks/IncrementalTask.groovy b/gradle/src/main/groovy/com/android/build/gradle/internal/tasks/IncrementalTask.groovy
index aef4319..2a86114 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/tasks/IncrementalTask.groovy
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/tasks/IncrementalTask.groovy
@@ -14,16 +14,14 @@
  * limitations under the License.
  */
 package com.android.build.gradle.internal.tasks
-
-import com.android.builder.internal.incremental.ChangeManager
 import com.android.ide.common.res2.FileStatus
 import com.android.ide.common.res2.SourceSet
 import com.google.common.collect.Lists
-import org.gradle.api.file.FileCollection
+import com.google.common.collect.Maps
 import org.gradle.api.tasks.Optional
 import org.gradle.api.tasks.OutputDirectory
 import org.gradle.api.tasks.TaskAction
-import org.gradle.api.tasks.TaskInputs
+import org.gradle.api.tasks.incremental.IncrementalTaskInputs
 
 public abstract class IncrementalTask extends BaseTask {
 
@@ -31,7 +29,7 @@
     File incrementalFolder
 
     /**
-     * Whether this task can support incremental update using the {@link ChangeManager}
+     * Whether this task can support incremental update.
      *
      * @return whether this task can support incremental update.
      */
@@ -44,7 +42,7 @@
      * {@link #isIncremental()} returns false.
      *
      */
-    protected abstract void doFullTaskAction();
+    protected abstract void doFullTaskAction()
 
     /**
      * Optional incremental task action.
@@ -56,79 +54,44 @@
         // do nothing.
     }
 
-    protected Collection<File> getOutputForIncrementalBuild() {
-        return Collections.emptyList();
-    }
-
     /**
      * Actual entry point for the action.
      * Calls out to the doTaskAction as needed.
      */
     @TaskAction
-    void taskAction() {
-        try {
-            if (!isIncremental() || incrementalFolder == null) {
-                doFullTaskAction()
-                return;
-            }
-
-            // load known state.
-            ChangeManager changeManager = new ChangeManager()
-            boolean fullBuild = !changeManager.load(incrementalFolder)
-
-            // update with current files.
-            TaskInputs inputs = getInputs()
-            FileCollection inputCollection = inputs.getFiles()
-
-            for (File f : inputCollection.files) {
-                changeManager.addInput(f)
-            }
-
-            for (File f : getOutputForIncrementalBuild()) {
-                changeManager.addOutput(f);
-            }
-
-            // force full build if output changed somehow.
-            Map<File, FileStatus> changedOutputs = changeManager.getChangedOutputs()
-            Map<File, FileStatus> changedInputs = changeManager.getChangedInputs()
-            if (fullBuild) {
-                project.logger.info("No incremental data: full task run")
-                doFullTaskAction();
-            } else if (!changedOutputs.isEmpty()) {
-                project.logger.info("Changed output: full task run")
-
-                doFullTaskAction();
-            } else if (changedInputs.isEmpty() && changedOutputs.isEmpty()) {
-                // both input and output are empty, this is something we don't control
-                // through files, just do a full run
-                project.logger.info("Changed non file input/output: full task run")
-                doFullTaskAction()
-            } else {
-                doIncrementalTaskAction(changeManager.getChangedInputs())
-            }
-
-            // update the outputs post task-action, to record their state
-            // for the next run
-            changeManager.updateOutputs(getOutputForIncrementalBuild())
-
-            // write the result down to be used next time the task is run.
-            changeManager.write(incrementalFolder)
-        } catch (Exception e) {
-            // Easiest to do here, is to delete the incremental Data so that
-            // next run is full.
-            ChangeManager.delete(incrementalFolder)
-
-            throw e
+    void taskAction(IncrementalTaskInputs inputs) {
+        if (!isIncremental()) {
+            doFullTaskAction()
+            return
         }
+
+        if (!inputs.isIncremental()) {
+            project.logger.info("Unable do incremental execution: full task run")
+            doFullTaskAction()
+            return
+        }
+
+        Map<File, FileStatus> changedInputs = Maps.newHashMap()
+        inputs.outOfDate { change ->
+            //noinspection GroovyAssignabilityCheck
+            changedInputs.put(change.file, change.isAdded() ? FileStatus.NEW : FileStatus.CHANGED)
+        }
+
+        inputs.removed { change ->
+            //noinspection GroovyAssignabilityCheck
+            changedInputs.put(change.file, FileStatus.REMOVED)
+        }
+
+        doIncrementalTaskAction(changedInputs)
     }
 
     public static List<File> flattenSourceSets(List<? extends SourceSet> resourceSets) {
-        List<File> list = Lists.newArrayList();
+        List<File> list = Lists.newArrayList()
 
         for (SourceSet sourceSet : resourceSets) {
             list.addAll(sourceSet.sourceFiles)
         }
 
-        return list;
+        return list
     }
 }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/tasks/NdkTask.groovy b/gradle/src/main/groovy/com/android/build/gradle/internal/tasks/NdkTask.groovy
new file mode 100644
index 0000000..ba48552
--- /dev/null
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/tasks/NdkTask.groovy
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2013 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.build.gradle.internal.tasks
+
+import com.android.builder.model.NdkConfig
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.Optional
+
+/**
+ * Base task for tasks that require an NdkConfig
+ */
+class NdkTask extends BaseTask {
+
+    NdkConfig ndkConfig
+
+    @Input @Optional
+    String getModuleName() {
+        return getNdkConfig()?.moduleName
+    }
+
+    @Input @Optional
+    String getcFlags() {
+        return getNdkConfig()?.cFlags
+    }
+
+    @Input @Optional
+    Set<String> getLdLibs() {
+        return getNdkConfig()?.ldLibs
+    }
+
+    @Input @Optional
+    Set<String> getAbiFilters() {
+        return getNdkConfig()?.abiFilters
+    }
+
+    @Input @Optional
+    String getStl() {
+        return getNdkConfig().stl
+    }
+}
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/variant/ApkVariantData.java b/gradle/src/main/groovy/com/android/build/gradle/internal/variant/ApkVariantData.java
index 822cdfe..9c1f580 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/variant/ApkVariantData.java
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/variant/ApkVariantData.java
@@ -21,14 +21,12 @@
 import com.android.build.gradle.tasks.ZipAlign;
 import com.android.builder.VariantConfiguration;
 import org.gradle.api.DefaultTask;
-import proguard.gradle.ProGuardTask;
 
 /**
  * Base data about a variant that generates an APK file.
  */
 public abstract class ApkVariantData extends BaseVariantData {
 
-    public ProGuardTask proguardTask;
     public Dex dexTask;
     public PackageApplication packageApplicationTask;
     public ZipAlign zipAlignTask;
@@ -46,34 +44,12 @@
         if (getVariantConfiguration().hasFlavors()) {
             return String.format("%s build for flavor %s",
                     getCapitalizedBuildTypeName(),
-                    getFlavoredName(true));
+                    getCapitalizedFlavorName());
         } else {
             return String.format("%s build", getCapitalizedBuildTypeName());
         }
     }
 
-    @Override
-    @NonNull
-    public String getDirName() {
-        if (getVariantConfiguration().hasFlavors()) {
-            return String.format("%s/%s",
-                    getFlavoredName(false), getVariantConfiguration().getBuildType().getName());
-        } else {
-            return getVariantConfiguration().getBuildType().getName();
-        }
-    }
-
-    @Override
-    @NonNull
-    public String getBaseName() {
-        if (getVariantConfiguration().hasFlavors()) {
-            return String.format("%s-%s",
-                    getFlavoredName(false), getVariantConfiguration().getBuildType().getName());
-        } else {
-            return getVariantConfiguration().getBuildType().getName();
-        }
-    }
-
     public boolean isSigned() {
         return getVariantConfiguration().isSigningReady();
     }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/variant/ApplicationVariantData.java b/gradle/src/main/groovy/com/android/build/gradle/internal/variant/ApplicationVariantData.java
index 0f8d4ed..91c4d08 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/variant/ApplicationVariantData.java
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/variant/ApplicationVariantData.java
@@ -31,20 +31,6 @@
         super(config);
     }
 
-    @NonNull
-    @Override
-    protected String computeName() {
-        return getVariantConfiguration().hasFlavors() ?
-                String.format("%s%s",
-                        getFlavoredName(true), getCapitalizedBuildTypeName()) :
-                getCapitalizedBuildTypeName();
-    }
-
-    @Override
-    public boolean getRunProguard() {
-        return getVariantConfiguration().getBuildType().isRunProguard();
-    }
-
     @Override
     public void setTestVariantData(@Nullable TestVariantData testVariantData) {
         this.testVariantData = testVariantData;
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/variant/BaseVariantData.java b/gradle/src/main/groovy/com/android/build/gradle/internal/variant/BaseVariantData.java
index 135eeaf..83794e8 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/variant/BaseVariantData.java
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/variant/BaseVariantData.java
@@ -17,6 +17,7 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
+import com.android.annotations.VisibleForTesting;
 import com.android.build.gradle.internal.StringHelper;
 import com.android.build.gradle.internal.dependency.VariantDependencies;
 import com.android.build.gradle.internal.tasks.PrepareDependenciesTask;
@@ -24,29 +25,34 @@
 import com.android.build.gradle.tasks.GenerateBuildConfig;
 import com.android.build.gradle.tasks.MergeAssets;
 import com.android.build.gradle.tasks.MergeResources;
+import com.android.build.gradle.tasks.NdkCompile;
 import com.android.build.gradle.tasks.ProcessAndroidResources;
 import com.android.build.gradle.tasks.ProcessManifest;
 import com.android.build.gradle.tasks.RenderscriptCompile;
-import com.android.builder.BuilderConstants;
-import com.android.builder.DefaultProductFlavor;
 import com.android.builder.VariantConfiguration;
+import com.google.common.collect.Lists;
 import groovy.lang.Closure;
 import org.gradle.api.Task;
 import org.gradle.api.tasks.Copy;
 import org.gradle.api.tasks.compile.JavaCompile;
+import proguard.gradle.ProGuardTask;
 
 import java.io.File;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * Base data about a variant.
  */
 public abstract class BaseVariantData {
 
-    private String name;
     private final VariantConfiguration variantConfiguration;
     private VariantDependencies variantDependency;
 
+    public Task preBuildTask;
     public PrepareDependenciesTask prepareDependenciesTask;
+    public Task sourceGenTask;
 
     public ProcessManifest processManifestTask;
     public RenderscriptCompile renderscriptCompileTask;
@@ -57,21 +63,21 @@
     public GenerateBuildConfig generateBuildConfigTask;
 
     public JavaCompile javaCompileTask;
-    public Copy processJavaResources;
+    public ProGuardTask proguardTask;
+    public Copy processJavaResourcesTask;
+    public NdkCompile ndkCompileTask;
 
     private Object outputFile;
 
     public Task assembleTask;
 
+    private List<File> extraGeneratedSourceFolders;
+
     public BaseVariantData(@NonNull VariantConfiguration variantConfiguration) {
         this.variantConfiguration = variantConfiguration;
-        this.name = computeName();
     }
 
     @NonNull
-    protected abstract String computeName();
-
-    @NonNull
     public VariantConfiguration getVariantConfiguration() {
         return variantConfiguration;
     }
@@ -86,60 +92,21 @@
     }
 
     @NonNull
-    public String getName() {
-        return name;
-    }
-
-    @NonNull
     public abstract String getDescription();
 
-    @NonNull
-    public abstract String getDirName();
-
-    @NonNull
-    public String getFlavorDirName() {
-        if (variantConfiguration.hasFlavors()) {
-            return getFlavoredName(false);
-        } else {
-            return "";
-        }
-    }
-
-    @NonNull
-    public String getFlavorName() {
-        if (variantConfiguration.hasFlavors()) {
-            return getFlavoredName(true);
-        } else {
-            return StringHelper.capitalize(BuilderConstants.MAIN);
-        }
-    }
-
-    @NonNull
-    public abstract String getBaseName();
-
     @Nullable
     public String getPackageName() {
         return variantConfiguration.getPackageName();
     }
 
     @NonNull
-    protected String getFlavoredName(boolean capitalized) {
-        StringBuilder builder = new StringBuilder();
-        for (DefaultProductFlavor flavor : variantConfiguration.getFlavorConfigs()) {
-            String name = flavor.getName();
-            builder.append(capitalized ? StringHelper.capitalize(name) : name);
-        }
-
-        return builder.toString();
-    }
-
-    @NonNull
     protected String getCapitalizedBuildTypeName() {
         return StringHelper.capitalize(variantConfiguration.getBuildType().getName());
     }
 
-    public boolean getRunProguard() {
-        return false;
+    @NonNull
+    protected String getCapitalizedFlavorName() {
+        return StringHelper.capitalize(variantConfiguration.getFlavorName());
     }
 
     public void setOutputFile(Object file) {
@@ -157,4 +124,51 @@
         assert false;
         return null;
     }
+
+    @VisibleForTesting
+    @NonNull
+    String getName() {
+        return variantConfiguration.getFullName();
+    }
+
+    @Nullable
+    public List<File> getExtraGeneratedSourceFolders() {
+        return extraGeneratedSourceFolders;
+    }
+
+    public void addJavaSourceFoldersToModel(@NonNull File... generatedSourceFolders) {
+        Collections.addAll(extraGeneratedSourceFolders, generatedSourceFolders);
+    }
+
+    public void addJavaSourceFoldersToModel(@NonNull Collection<File> generatedSourceFolders) {
+        extraGeneratedSourceFolders.addAll(generatedSourceFolders);
+    }
+
+    public void registerJavaGeneratingTask(@NonNull Task task, @NonNull File... generatedSourceFolders) {
+        if (extraGeneratedSourceFolders == null) {
+            extraGeneratedSourceFolders = Lists.newArrayList();
+        }
+
+        javaCompileTask.dependsOn(task);
+
+        for (File f : generatedSourceFolders) {
+            javaCompileTask.source(f);
+        }
+
+        addJavaSourceFoldersToModel(generatedSourceFolders);
+    }
+
+    public void registerJavaGeneratingTask(@NonNull Task task, @NonNull Collection<File> generatedSourceFolders) {
+        if (extraGeneratedSourceFolders == null) {
+            extraGeneratedSourceFolders = Lists.newArrayList();
+        }
+
+        javaCompileTask.dependsOn(task);
+
+        for (File f : generatedSourceFolders) {
+            javaCompileTask.source(f);
+        }
+
+        addJavaSourceFoldersToModel(generatedSourceFolders);
+    }
 }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/variant/DefaultSourceProviderContainer.java b/gradle/src/main/groovy/com/android/build/gradle/internal/variant/DefaultSourceProviderContainer.java
new file mode 100644
index 0000000..06f2e5f
--- /dev/null
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/variant/DefaultSourceProviderContainer.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2013 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.build.gradle.internal.variant;
+
+import com.android.annotations.NonNull;
+import com.android.builder.model.SourceProvider;
+import com.android.builder.model.SourceProviderContainer;
+
+/**
+ * Default implementation of a SourceProviderContainer that wraps an existing instance of a
+ * SourceProvider.
+ */
+public class DefaultSourceProviderContainer implements SourceProviderContainer {
+
+    @NonNull
+    private final String name;
+    @NonNull
+    private final SourceProvider sourceProvider;
+
+    public DefaultSourceProviderContainer(@NonNull String name,
+                                          @NonNull SourceProvider sourceProvider) {
+        this.name = name;
+        this.sourceProvider = sourceProvider;
+    }
+
+    @NonNull
+    @Override
+    public String getArtifactName() {
+        return name;
+    }
+
+    @NonNull
+    @Override
+    public SourceProvider getSourceProvider() {
+        return sourceProvider;
+    }
+}
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantData.java b/gradle/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantData.java
index 852b8c5..8a86851 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantData.java
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantData.java
@@ -36,36 +36,17 @@
 
     @Override
     @NonNull
-    protected String computeName() {
-        return getVariantConfiguration().hasFlavors() ?
-                String.format("%s%s",
-                        getFlavoredName(true), getCapitalizedBuildTypeName()) :
-                getCapitalizedBuildTypeName();
-    }
-
-    @Override
-    @NonNull
     public String getDescription() {
         if (getVariantConfiguration().hasFlavors()) {
-            return "Test build for the ${getFlavoredName(true)}${config.buildType.name.capitalize()} build";
+            return String.format("%s build for flavor %s",
+                    getCapitalizedBuildTypeName(),
+                    getCapitalizedFlavorName());
         } else {
-            return "Test for the ${config.buildType.name.capitalize()} build";
+            return String.format("%s build", getCapitalizedBuildTypeName());
         }
     }
 
     @Override
-    @NonNull
-    public String getDirName() {
-        return getVariantConfiguration().getBuildType().getName();
-    }
-
-    @Override
-    @NonNull
-    public String getBaseName() {
-        return getVariantConfiguration().getBuildType().getName();
-    }
-
-    @Override
     public void setTestVariantData(@Nullable TestVariantData testVariantData) {
         this.testVariantData = testVariantData;
     }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/variant/TestVariantData.java b/gradle/src/main/groovy/com/android/build/gradle/internal/variant/TestVariantData.java
index ebd336c..b8d45ed 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/internal/variant/TestVariantData.java
+++ b/gradle/src/main/groovy/com/android/build/gradle/internal/variant/TestVariantData.java
@@ -43,20 +43,12 @@
         return testedVariantData;
     }
 
-    @NonNull
-    @Override
-    protected String computeName() {
-        return getVariantConfiguration().hasFlavors() ?
-                String.format("%sTest", getFlavoredName(true)) :
-                "Test";
-    }
-
     @Override
     @NonNull
     public String getDescription() {
         if (getVariantConfiguration().hasFlavors()) {
             return String.format("Test build for the %s%s build",
-                    getFlavoredName(true), getCapitalizedBuildTypeName());
+                    getCapitalizedFlavorName(), getCapitalizedBuildTypeName());
         } else {
             return String.format("Test build for the %s build",
                     getCapitalizedBuildTypeName());
@@ -64,26 +56,6 @@
     }
 
     @Override
-    @NonNull
-    public String getDirName() {
-        if (getVariantConfiguration().hasFlavors()) {
-            return String.format("%s/test", getFlavoredName(false));
-        } else {
-            return "test";
-        }
-    }
-
-    @Override
-    @NonNull
-    public String getBaseName() {
-        if (getVariantConfiguration().hasFlavors()) {
-            return String.format("%s-test", getFlavoredName(false));
-        } else {
-            return "test";
-        }
-    }
-
-    @Override
     public boolean getZipAlign() {
         return false;
     }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/tasks/AidlCompile.groovy b/gradle/src/main/groovy/com/android/build/gradle/tasks/AidlCompile.groovy
index d698bd7..6ad9277 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/tasks/AidlCompile.groovy
+++ b/gradle/src/main/groovy/com/android/build/gradle/tasks/AidlCompile.groovy
@@ -47,11 +47,6 @@
     }
 
     @Override
-    protected Collection<File> getOutputForIncrementalBuild() {
-        return Collections.singletonList(getSourceOutputDir())
-    }
-
-    @Override
     protected void compileAllFiles(DependencyFileProcessor dependencyFileProcessor) {
         getBuilder().compileAllAidlFiles(
                 getSourceDirs(),
diff --git a/gradle/src/main/groovy/com/android/build/gradle/tasks/Dex.groovy b/gradle/src/main/groovy/com/android/build/gradle/tasks/Dex.groovy
index 3db6c45..c74cdb3 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/tasks/Dex.groovy
+++ b/gradle/src/main/groovy/com/android/build/gradle/tasks/Dex.groovy
@@ -14,14 +14,11 @@
  * limitations under the License.
  */
 package com.android.build.gradle.tasks
-
 import com.android.build.gradle.internal.dsl.DexOptionsImpl
 import com.android.build.gradle.internal.tasks.IncrementalTask
 import com.android.ide.common.res2.FileStatus
-import org.gradle.api.tasks.InputFile
 import org.gradle.api.tasks.InputFiles
 import org.gradle.api.tasks.Nested
-import org.gradle.api.tasks.Optional
 import org.gradle.api.tasks.OutputFile
 
 public class Dex extends IncrementalTask {
@@ -34,13 +31,10 @@
     // ----- PRIVATE TASK API -----
 
     @InputFiles
-    Iterable<File> sourceFiles
+    Iterable<File> inputFiles
 
     @InputFiles
-    Iterable<File> libraries
-
-    @InputFile @Optional
-    File proguardedJar
+    Iterable<File> preDexedLibraries
 
     @Nested
     DexOptionsImpl dexOptions
@@ -48,10 +42,9 @@
     @Override
     protected void doFullTaskAction() {
         getBuilder().convertByteCode(
-                getSourceFiles(),
-                getLibraries(),
-                getProguardedJar(),
-                getOutputFile().absolutePath,
+                getInputFiles(),
+                getPreDexedLibraries(),
+                getOutputFile(),
                 getDexOptions(),
                 false)
     }
@@ -59,10 +52,9 @@
     @Override
     protected void doIncrementalTaskAction(Map<File, FileStatus> changedInputs) {
         getBuilder().convertByteCode(
-                getSourceFiles(),
-                getLibraries(),
-                getProguardedJar(),
-                getOutputFile().absolutePath,
+                getInputFiles(),
+                getPreDexedLibraries(),
+                getOutputFile(),
                 getDexOptions(),
                 true)
     }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/tasks/GenerateBuildConfig.groovy b/gradle/src/main/groovy/com/android/build/gradle/tasks/GenerateBuildConfig.groovy
index 35bc489..1f4d7af 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/tasks/GenerateBuildConfig.groovy
+++ b/gradle/src/main/groovy/com/android/build/gradle/tasks/GenerateBuildConfig.groovy
@@ -16,7 +16,9 @@
 package com.android.build.gradle.tasks
 
 import com.android.build.gradle.internal.tasks.IncrementalTask
+import com.android.builder.compiling.BuildConfigGenerator
 import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.Optional
 import org.gradle.api.tasks.OutputDirectory
 
 public class GenerateBuildConfig extends IncrementalTask {
@@ -29,13 +31,31 @@
     // ----- PRIVATE TASK API -----
 
     @Input
-    String packageName
+    String buildConfigPackageName
+
+    @Input
+    String appPackageName
 
     @Input
     boolean debuggable
 
     @Input
-    List<String> javaLines;
+    String flavorName
+
+    @Input
+    List<String> flavorNamesWithDimensionNames
+
+    @Input
+    String buildTypeName
+
+    @Input @Optional
+    String versionName
+
+    @Input
+    int versionCode
+
+    @Input
+    List<Object> items;
 
     @Override
     protected void doFullTaskAction() {
@@ -44,10 +64,36 @@
         File destinationDir = getSourceOutputDir()
         emptyFolder(destinationDir)
 
-        getBuilder().generateBuildConfig(
-                getPackageName(),
-                isDebuggable(),
-                getJavaLines(),
-                getSourceOutputDir().absolutePath);
+        BuildConfigGenerator generator = new BuildConfigGenerator(
+                getSourceOutputDir().absolutePath,
+                getBuildConfigPackageName());
+
+        // Hack (see IDEA-100046): We want to avoid reporting "condition is always true"
+        // from the data flow inspection, so use a non-constant value. However, that defeats
+        // the purpose of this flag (when not in debug mode, if (BuildConfig.DEBUG && ...) will
+        // be completely removed by the compiler), so as a hack we do it only for the case
+        // where debug is true, which is the most likely scenario while the user is looking
+        // at source code.
+        //map.put(PH_DEBUG, Boolean.toString(mDebug));
+        generator.addField("boolean", "DEBUG", getDebuggable() ? "Boolean.parseBoolean(\"true\")" : "false")
+            .addField("String", "PACKAGE_NAME", "\"${getAppPackageName()}\"")
+            .addField("String", "BUILD_TYPE", "\"${getBuildTypeName()}\"")
+            .addField("String", "FLAVOR", "\"${getFlavorName()}\"")
+            .addField("int", "VERSION_CODE", Integer.toString(getVersionCode()))
+            .addItems(getItems());
+
+        if (getVersionName() != null) {
+            generator.addField("String", "VERSION_NAME", "\"${getVersionName()}\"")
+        }
+
+        List<String> flavors = getFlavorNamesWithDimensionNames();
+        int count = flavors.size();
+        if (count > 1) {
+            for (int i = 0; i < count ; i+=2) {
+                generator.addField("String", "FLAVOR_${flavors.get(i+1)}", "\"${flavors.get(i)}\"")
+            }
+        }
+
+        generator.generate();
     }
 }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/tasks/Lint.groovy b/gradle/src/main/groovy/com/android/build/gradle/tasks/Lint.groovy
new file mode 100644
index 0000000..088ee72
--- /dev/null
+++ b/gradle/src/main/groovy/com/android/build/gradle/tasks/Lint.groovy
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2013 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.build.gradle.tasks
+
+import com.android.annotations.NonNull
+import com.android.annotations.Nullable
+import com.android.build.gradle.BasePlugin
+import com.android.build.gradle.internal.LintGradleClient
+import com.android.build.gradle.internal.model.ModelBuilder
+import com.android.builder.model.AndroidProject
+import com.android.builder.model.Variant
+import com.android.tools.lint.HtmlReporter
+import com.android.tools.lint.LintCliFlags
+import com.android.tools.lint.Reporter
+import com.android.tools.lint.Warning
+import com.android.tools.lint.XmlReporter
+import com.android.tools.lint.checks.BuiltinIssueRegistry
+import com.android.tools.lint.client.api.IssueRegistry
+import com.android.tools.lint.detector.api.Severity
+import com.google.common.collect.Maps
+import org.gradle.api.DefaultTask
+import org.gradle.api.GradleException
+import org.gradle.api.Project
+import org.gradle.api.tasks.TaskAction
+
+import static com.android.SdkConstants.DOT_XML
+
+public class Lint extends DefaultTask {
+    @NonNull private BasePlugin mPlugin
+    @Nullable private File mConfigFile
+    @Nullable private File mHtmlOutput
+    @Nullable private File mXmlOutput
+    @Nullable private String mVariantName
+    private boolean mQuiet = true
+
+    public void setPlugin(@NonNull BasePlugin plugin) {
+        mPlugin = plugin
+    }
+
+    public void setVariantName(@NonNull String variantName) {
+        mVariantName = variantName
+    }
+
+    public void setQuiet() {
+        mQuiet = true
+    }
+
+    public void setConfig(@NonNull File configFile) {
+        mConfigFile = configFile
+    }
+
+    public void setHtmlOutput(@NonNull File htmlOutput) {
+        mHtmlOutput = htmlOutput
+    }
+
+    public void setXmlOutput(@NonNull File xmlOutput) {
+        mXmlOutput = xmlOutput
+    }
+
+    @SuppressWarnings("GroovyUnusedDeclaration")
+    @TaskAction
+    public void lint() {
+        def modelProject = createAndroidProject(mPlugin.getProject())
+        if (mVariantName != null) {
+            lintSingleVariant(modelProject, mVariantName)
+        } else {
+            lintAllVariants(modelProject)
+        }
+    }
+
+    /**
+     * Runs lint individually on all the variants, and then compares the results
+     * across variants and reports these
+     */
+    public void lintAllVariants(@NonNull AndroidProject modelProject) {
+        Map<Variant,List<Warning>> warningMap = Maps.newHashMap()
+        for (Variant variant : modelProject.getVariants()) {
+            try {
+                List<Warning> warnings = runLint(modelProject, variant.getName())
+                warningMap.put(variant, warnings)
+            } catch (IOException e) {
+                throw new GradleException("Invalid arguments.", e)
+            }
+        }
+
+        // Compute error matrix
+        for (Map.Entry<Variant,List<Warning>> entry : warningMap.entrySet()) {
+            def variant = entry.getKey()
+            def warnings = entry.getValue()
+            println "Ran lint on variant " + variant.getName() + ": " + warnings.size() +
+                    " issues found"
+        }
+
+        List<Warning> mergedWarnings = LintGradleClient.merge(warningMap, modelProject)
+        int errorCount = 0
+        int warningCount = 0
+        for (Warning warning : mergedWarnings) {
+            if (warning.severity == Severity.ERROR || warning.severity == Severity.FATAL) {
+                errorCount++
+            } else if (warning.severity == Severity.WARNING) {
+                warningCount++
+            }
+        }
+
+        IssueRegistry registry = new BuiltinIssueRegistry()
+        LintCliFlags flags = new LintCliFlags()
+        LintGradleClient client = new LintGradleClient(registry, flags, mPlugin, modelProject,
+                null)
+        configureReporters(client, flags, null)
+        for (Reporter reporter : flags.getReporters()) {
+            reporter.write(errorCount, warningCount, mergedWarnings)
+        }
+    }
+
+    /**
+     * Runs lint on a single specified variant
+     */
+    public void lintSingleVariant(@NonNull AndroidProject modelProject, String variantName) {
+        runLint(modelProject, variantName)
+    }
+
+    /** Runs lint on the given variant and returns the set of warnings */
+    private List<Warning> runLint(
+            @NonNull AndroidProject modelProject,
+            @NonNull String variantName) {
+        IssueRegistry registry = new BuiltinIssueRegistry()
+        LintCliFlags flags = new LintCliFlags()
+        LintGradleClient client = new LintGradleClient(registry, flags, mPlugin, modelProject,
+                variantName)
+
+        // Configure Reporters
+        configureReporters(client, flags, variantName)
+
+        // Flags
+        if (mQuiet) {
+            flags.setQuiet(true)
+        }
+        if (mConfigFile != null) {
+            flags.setDefaultConfiguration(client.createConfigurationFromFile(mConfigFile))
+        }
+
+        // Finally perform lint run
+        try {
+            return client.run(registry)
+        } catch (IOException e) {
+            throw new GradleException("Invalid arguments.", e)
+        }
+    }
+
+    private void configureReporters(@NonNull LintGradleClient client, @NonNull LintCliFlags flags,
+            @Nullable String variantName) {
+        StringBuilder base = new StringBuilder()
+        base.append("lint-results/")
+        if (variantName != null) {
+            base.append(variantName)
+            base.append("/")
+        }
+        base.append("lint-results")
+        if (variantName != null) {
+            base.append("-")
+            base.append(variantName)
+        }
+        File htmlOutput = mHtmlOutput
+        File xmlOutput = mXmlOutput
+        if (htmlOutput == null) {
+            htmlOutput = project.file(base.toString() + ".html")
+            File parent = htmlOutput.parentFile
+            if (!parent.exists()) {
+                parent.mkdirs()
+            }
+        }
+
+        if (xmlOutput == null) {
+            xmlOutput = project.file(base.toString() + DOT_XML)
+            File parent = xmlOutput.parentFile
+            if (!parent.exists()) {
+                parent.mkdirs()
+            }
+        }
+
+        htmlOutput = htmlOutput.getAbsoluteFile()
+        if (htmlOutput.exists()) {
+            boolean delete = htmlOutput.delete()
+            if (!delete) {
+                throw new GradleException("Could not delete old " + htmlOutput)
+            }
+        }
+        if (htmlOutput.getParentFile() != null && !htmlOutput.getParentFile().canWrite()) {
+            throw new GradleException("Cannot write HTML output file " + htmlOutput)
+        }
+        try {
+            flags.getReporters().add(new HtmlReporter(client, htmlOutput))
+        } catch (IOException e) {
+            throw new GradleException("HTML invalid argument.", e)
+        }
+
+        xmlOutput = xmlOutput.getAbsoluteFile()
+        if (xmlOutput.exists()) {
+            boolean delete = xmlOutput.delete()
+            if (!delete) {
+                throw new GradleException("Could not delete old " + xmlOutput)
+            }
+        }
+        if (xmlOutput.getParentFile() != null && !xmlOutput.getParentFile().canWrite()) {
+            throw new GradleException("Cannot write XML output file " + xmlOutput)
+        }
+        try {
+            flags.getReporters().add(new XmlReporter(client, xmlOutput))
+        } catch (IOException e) {
+            throw new GradleException("XML invalid argument.", e)
+        }
+
+        List<Reporter> reporters = flags.getReporters()
+        if (reporters.isEmpty()) {
+            throw new GradleException("No reporter specified.")
+        }
+
+        Map<String, String> map = new HashMap<String, String>() {{
+            put("", "file://")
+        }}
+        for (Reporter reporter : reporters) {
+            reporter.setUrlMap(map)
+        }
+    }
+
+    private static AndroidProject createAndroidProject(@NonNull Project gradleProject) {
+        String modelName = AndroidProject.class.getName()
+        ModelBuilder builder = new ModelBuilder()
+        return (AndroidProject) builder.buildAll(modelName, gradleProject)
+    }
+}
diff --git a/gradle/src/main/groovy/com/android/build/gradle/tasks/MergeAssets.groovy b/gradle/src/main/groovy/com/android/build/gradle/tasks/MergeAssets.groovy
index b86fc8b..79f6190 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/tasks/MergeAssets.groovy
+++ b/gradle/src/main/groovy/com/android/build/gradle/tasks/MergeAssets.groovy
@@ -19,8 +19,8 @@
 import com.android.ide.common.res2.AssetSet
 import com.android.ide.common.res2.FileStatus
 import com.android.ide.common.res2.FileValidity
-import com.android.ide.common.res2.MergeConsumer
 import com.android.ide.common.res2.MergedAssetWriter
+import com.android.ide.common.res2.MergingException
 import org.gradle.api.tasks.InputFiles
 import org.gradle.api.tasks.OutputDirectory
 
@@ -50,11 +50,6 @@
     }
 
     @Override
-    protected Collection<File> getOutputForIncrementalBuild() {
-        return Collections.singletonList(getOutputDir())
-    }
-
-    @Override
     protected void doFullTaskAction() {
         // this is full run, clean the previous output
         File destinationDir = getOutputDir()
@@ -65,78 +60,81 @@
         // create a new merger and populate it with the sets.
         AssetMerger merger = new AssetMerger()
 
-        for (AssetSet assetSet : assetSets) {
-            // set needs to be loaded.
-            assetSet.loadFromFiles(plugin.logger)
-            merger.addDataSet(assetSet)
-        }
-
-        // get the merged set and write it down.
-        MergedAssetWriter writer = new MergedAssetWriter(destinationDir)
-
         try {
-            merger.mergeData(writer, false /*doCleanUp*/)
-        } catch (MergeConsumer.ConsumerException e) {
-            merger.cleanBlob(getIncrementalFolder())
-            throw e.getCause()
-        }
+            for (AssetSet assetSet : assetSets) {
+                // set needs to be loaded.
+                assetSet.loadFromFiles(plugin.logger)
+                merger.addDataSet(assetSet)
+            }
 
-        // No exception? Write the known state.
-        merger.writeBlobTo(getIncrementalFolder(), writer)
+            // get the merged set and write it down.
+            MergedAssetWriter writer = new MergedAssetWriter(destinationDir)
+
+            merger.mergeData(writer, false /*doCleanUp*/)
+
+            // No exception? Write the known state.
+            merger.writeBlobTo(getIncrementalFolder(), writer)
+        } catch (MergingException e) {
+            println e.getMessage()
+            merger.cleanBlob(getIncrementalFolder())
+            throw new ResourceException(e.getMessage(), e)
+        }
     }
 
     @Override
     protected void doIncrementalTaskAction(Map<File, FileStatus> changedInputs) {
         // create a merger and load the known state.
         AssetMerger merger = new AssetMerger()
-        if (!merger.loadFromBlob(getIncrementalFolder(), true /*incrementalState*/)) {
-            doFullTaskAction()
-            return
-        }
-
-        // compare the known state to the current sets to detect incompatibility.
-        // This is in case there's a change that's too hard to do incrementally. In this case
-        // we'll simply revert to full build.
-        List<AssetSet> assetSets = getInputAssetSets()
-
-        if (!merger.checkValidUpdate(assetSets)) {
-            project.logger.info("Changed Asset sets: full task run!")
-            doFullTaskAction()
-            return
-        }
-
-        // The incremental process is the following:
-        // Loop on all the changed files, find which ResourceSet it belongs to, then ask
-        // the resource set to update itself with the new file.
-        for (Map.Entry<File, FileStatus> entry : changedInputs.entrySet()) {
-            File changedFile = entry.getKey()
-
-            merger.findDataSetContaining(changedFile, fileValidity)
-            if (fileValidity.status == FileValidity.FileStatus.UNKNOWN_FILE) {
+        try {
+            if (!merger.loadFromBlob(getIncrementalFolder(), true /*incrementalState*/)) {
                 doFullTaskAction()
                 return
-            } else if (fileValidity.status == FileValidity.FileStatus.VALID_FILE) {
-                if (!fileValidity.dataSet.updateWith(
-                        fileValidity.sourceFile, changedFile, entry.getValue(), plugin.logger)) {
-                    project.logger.info(
-                            String.format("Failed to process %s event! Full task run",
-                                    entry.getValue()))
+            }
+
+            // compare the known state to the current sets to detect incompatibility.
+            // This is in case there's a change that's too hard to do incrementally. In this case
+            // we'll simply revert to full build.
+            List<AssetSet> assetSets = getInputAssetSets()
+
+            if (!merger.checkValidUpdate(assetSets)) {
+                project.logger.info("Changed Asset sets: full task run!")
+                doFullTaskAction()
+                return
+            }
+
+            // The incremental process is the following:
+            // Loop on all the changed files, find which ResourceSet it belongs to, then ask
+            // the resource set to update itself with the new file.
+            for (Map.Entry<File, FileStatus> entry : changedInputs.entrySet()) {
+                File changedFile = entry.getKey()
+
+                merger.findDataSetContaining(changedFile, fileValidity)
+                if (fileValidity.status == FileValidity.FileStatus.UNKNOWN_FILE) {
                     doFullTaskAction()
                     return
+                } else if (fileValidity.status == FileValidity.FileStatus.VALID_FILE) {
+                    if (!fileValidity.dataSet.updateWith(
+                            fileValidity.sourceFile, changedFile, entry.getValue(),
+                            plugin.logger)) {
+                        project.logger.info(
+                                String.format("Failed to process %s event! Full task run",
+                                        entry.getValue()))
+                        doFullTaskAction()
+                        return
+                    }
                 }
             }
-        }
 
-        MergedAssetWriter writer = new MergedAssetWriter(getOutputDir())
+            MergedAssetWriter writer = new MergedAssetWriter(getOutputDir())
 
-        try {
             merger.mergeData(writer, false /*doCleanUp*/)
-        } catch (MergeConsumer.ConsumerException e) {
-            merger.cleanBlob(getIncrementalFolder())
-            throw e.getCause()
-        }
 
-        // No exception? Write the known state.
-        merger.writeBlobTo(getIncrementalFolder(), writer)
+            // No exception? Write the known state.
+            merger.writeBlobTo(getIncrementalFolder(), writer)
+        } catch (MergingException e) {
+            println e.getMessage()
+            merger.cleanBlob(getIncrementalFolder())
+            throw new ResourceException(e.getMessage(), e)
+        }
     }
 }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/tasks/MergeResources.groovy b/gradle/src/main/groovy/com/android/build/gradle/tasks/MergeResources.groovy
index df1986c..b6487df 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/tasks/MergeResources.groovy
+++ b/gradle/src/main/groovy/com/android/build/gradle/tasks/MergeResources.groovy
@@ -14,11 +14,13 @@
  * limitations under the License.
  */
 package com.android.build.gradle.tasks
+
+import com.android.build.gradle.LibraryPlugin
 import com.android.build.gradle.internal.tasks.IncrementalTask
 import com.android.ide.common.res2.FileStatus
 import com.android.ide.common.res2.FileValidity
-import com.android.ide.common.res2.MergeConsumer
 import com.android.ide.common.res2.MergedResourceWriter
+import com.android.ide.common.res2.MergingException
 import com.android.ide.common.res2.ResourceMerger
 import com.android.ide.common.res2.ResourceSet
 import org.gradle.api.tasks.Input
@@ -54,11 +56,6 @@
     }
 
     @Override
-    protected Collection<File> getOutputForIncrementalBuild() {
-        return Collections.singletonList(getOutputDir())
-    }
-
-    @Override
     protected void doFullTaskAction() {
         // this is full run, clean the previous output
         File destinationDir = getOutputDir()
@@ -69,79 +66,83 @@
         // create a new merger and populate it with the sets.
         ResourceMerger merger = new ResourceMerger()
 
-        for (ResourceSet resourceSet : resourceSets) {
-            // set needs to be loaded.
-            resourceSet.loadFromFiles(plugin.logger)
-            merger.addDataSet(resourceSet)
-        }
-
-        // get the merged set and write it down.
-        MergedResourceWriter writer = new MergedResourceWriter(
-                destinationDir, getProcess9Patch() ? builder.aaptRunner : null)
-
         try {
-            merger.mergeData(writer, false /*doCleanUp*/)
-        } catch (MergeConsumer.ConsumerException e) {
-            merger.cleanBlob(getIncrementalFolder())
-            throw e.getCause()
-        }
+            for (ResourceSet resourceSet : resourceSets) {
+                // set needs to be loaded.
+                resourceSet.loadFromFiles(plugin.logger)
+                merger.addDataSet(resourceSet)
+            }
 
-        // No exception? Write the known state.
-        merger.writeBlobTo(getIncrementalFolder(), writer)
+            // get the merged set and write it down.
+            MergedResourceWriter writer = new MergedResourceWriter(
+                    destinationDir, getProcess9Patch() ? builder.aaptRunner : null)
+            writer.setInsertSourceMarkers(builder.isInsertSourceMarkers())
+
+            merger.mergeData(writer, false /*doCleanUp*/)
+
+            // No exception? Write the known state.
+            merger.writeBlobTo(getIncrementalFolder(), writer)
+        } catch (MergingException e) {
+            println e.getMessage()
+            merger.cleanBlob(getIncrementalFolder())
+            throw new ResourceException(e.getMessage(), e)
+        }
     }
 
     @Override
     protected void doIncrementalTaskAction(Map<File, FileStatus> changedInputs) {
         // create a merger and load the known state.
         ResourceMerger merger = new ResourceMerger()
-        if (!merger.loadFromBlob(getIncrementalFolder(), true /*incrementalState*/)) {
-            doFullTaskAction()
-            return
-        }
-
-        // compare the known state to the current sets to detect incompatibility.
-        // This is in case there's a change that's too hard to do incrementally. In this case
-        // we'll simply revert to full build.
-        List<ResourceSet> resourceSets = getInputResourceSets()
-
-        if (!merger.checkValidUpdate(resourceSets)) {
-            project.logger.info("Changed Resource sets: full task run!")
-            doFullTaskAction()
-            return
-        }
-
-        // The incremental process is the following:
-        // Loop on all the changed files, find which ResourceSet it belongs to, then ask
-        // the resource set to update itself with the new file.
-        for (Map.Entry<File, FileStatus> entry : changedInputs.entrySet()) {
-            File changedFile = entry.getKey()
-
-            merger.findDataSetContaining(changedFile, fileValidity)
-            if (fileValidity.status == FileValidity.FileStatus.UNKNOWN_FILE) {
+        try {
+            if (!merger.loadFromBlob(getIncrementalFolder(), true /*incrementalState*/)) {
                 doFullTaskAction()
                 return
-            } else if (fileValidity.status == FileValidity.FileStatus.VALID_FILE) {
-                if (!fileValidity.dataSet.updateWith(
-                        fileValidity.sourceFile, changedFile, entry.getValue(), plugin.logger)) {
-                    project.logger.info(
-                            String.format("Failed to process %s event! Full task run",
-                                    entry.getValue()))
+            }
+
+            // compare the known state to the current sets to detect incompatibility.
+            // This is in case there's a change that's too hard to do incrementally. In this case
+            // we'll simply revert to full build.
+            List<ResourceSet> resourceSets = getInputResourceSets()
+
+            if (!merger.checkValidUpdate(resourceSets)) {
+                project.logger.info("Changed Resource sets: full task run!")
+                doFullTaskAction()
+                return
+            }
+
+            // The incremental process is the following:
+            // Loop on all the changed files, find which ResourceSet it belongs to, then ask
+            // the resource set to update itself with the new file.
+            for (Map.Entry<File, FileStatus> entry : changedInputs.entrySet()) {
+                File changedFile = entry.getKey()
+
+                merger.findDataSetContaining(changedFile, fileValidity)
+                if (fileValidity.status == FileValidity.FileStatus.UNKNOWN_FILE) {
                     doFullTaskAction()
                     return
+                } else if (fileValidity.status == FileValidity.FileStatus.VALID_FILE) {
+                    if (!fileValidity.dataSet.updateWith(
+                            fileValidity.sourceFile, changedFile, entry.getValue(),
+                            plugin.logger)) {
+                        project.logger.info(
+                                String.format("Failed to process %s event! Full task run",
+                                        entry.getValue()))
+                        doFullTaskAction()
+                        return
+                    }
                 }
             }
-        }
 
-        MergedResourceWriter writer = new MergedResourceWriter(
-                getOutputDir(), getProcess9Patch() ? builder.aaptRunner : null)
-        try {
+            MergedResourceWriter writer = new MergedResourceWriter(
+                    getOutputDir(), getProcess9Patch() ? builder.aaptRunner : null)
+            writer.setInsertSourceMarkers(builder.isInsertSourceMarkers())
             merger.mergeData(writer, false /*doCleanUp*/)
-        } catch (MergeConsumer.ConsumerException e) {
+            // No exception? Write the known state.
+            merger.writeBlobTo(getIncrementalFolder(), writer)
+        } catch (MergingException e) {
+            println e.getMessage()
             merger.cleanBlob(getIncrementalFolder())
-            throw e.getCause()
+            throw new ResourceException(e.getMessage(), e)
         }
-
-        // No exception? Write the known state.
-        merger.writeBlobTo(getIncrementalFolder(), writer)
     }
 }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/tasks/NdkCompile.groovy b/gradle/src/main/groovy/com/android/build/gradle/tasks/NdkCompile.groovy
new file mode 100644
index 0000000..eba8d72
--- /dev/null
+++ b/gradle/src/main/groovy/com/android/build/gradle/tasks/NdkCompile.groovy
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2013 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.build.gradle.tasks
+import com.android.annotations.NonNull
+import com.android.build.gradle.internal.tasks.NdkTask
+import com.android.builder.model.NdkConfig
+import com.android.sdklib.IAndroidTarget
+import com.google.common.base.Charsets
+import com.google.common.base.Joiner
+import com.google.common.collect.Lists
+import com.google.common.io.Files
+import org.gradle.api.GradleException
+import org.gradle.api.file.FileTree
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.OutputDirectory
+import org.gradle.api.tasks.OutputFile
+import org.gradle.api.tasks.TaskAction
+import org.gradle.api.tasks.incremental.IncrementalTaskInputs
+import org.gradle.api.tasks.util.PatternSet
+/**
+ */
+class NdkCompile extends NdkTask {
+
+    List<File> sourceFolders
+
+    @OutputFile
+    File generatedMakefile
+
+    @Input
+    boolean debuggable
+
+    @OutputDirectory
+    File soFolder
+
+    @OutputDirectory
+    File objFolder
+
+    @Input
+    boolean ndkRenderScriptMode
+
+    @InputFiles
+    FileTree getSource() {
+        FileTree src = null
+        List<File> sources = getSourceFolders()
+        if (!sources.isEmpty()) {
+            src = getProject().files(new ArrayList<Object>(sources)).getAsFileTree()
+        }
+        return src == null ? getProject().files().getAsFileTree() : src
+    }
+
+    @TaskAction
+    void taskAction(IncrementalTaskInputs inputs) {
+
+        FileTree sourceFileTree = getSource()
+        Set<File> sourceFiles = sourceFileTree.matching(new PatternSet().exclude("**/*.h")).files
+        File makefile = getGeneratedMakefile()
+
+        if (sourceFiles.isEmpty()) {
+            makefile.delete()
+            emptyFolder(getSoFolder())
+            emptyFolder(getObjFolder())
+            return
+        }
+
+        File ndkDirectory = getPlugin().ndkDirectory
+        if (ndkDirectory == null || !ndkDirectory.isDirectory()) {
+            throw new GradleException("NDK not configured")
+        }
+
+        boolean generateMakefile = false
+
+        if (!inputs.isIncremental()) {
+            project.logger.info("Unable do incremental execution: full task run")
+            generateMakefile = true
+            emptyFolder(getSoFolder())
+            emptyFolder(getObjFolder())
+        } else {
+            // look for added or removed files *only*
+
+            //noinspection GroovyAssignabilityCheck
+            inputs.outOfDate { change ->
+                if (change.isAdded()) {
+                    generateMakefile = true
+                }
+            }
+
+            //noinspection GroovyAssignabilityCheck
+            inputs.removed { change ->
+                generateMakefile = true
+            }
+        }
+
+        if (generateMakefile) {
+            writeMakefile(sourceFiles, makefile)
+        }
+
+        // now build
+        runNdkBuild(ndkDirectory, makefile)
+    }
+
+    private void writeMakefile(@NonNull Set<File> sourceFiles, @NonNull File makefile) {
+        NdkConfig ndk = getNdkConfig()
+
+        StringBuilder sb = new StringBuilder()
+
+        sb.append(
+                'LOCAL_PATH := $(call my-dir)\n' +
+                'include \$(CLEAR_VARS)\n\n')
+
+        sb.append('LOCAL_MODULE := ').append(ndk.moduleName != null ? ndk.moduleName : project.name).append('\n')
+
+        if (ndk.cFlags != null) {
+            sb.append('LOCAL_CFLAGS := ').append(ndk.cFlags).append('\n')
+        }
+
+        List<String> fullLdlibs = Lists.newArrayList()
+        if (ndk.ldLibs != null) {
+            fullLdlibs.addAll(ndk.ldLibs)
+        }
+        if (getNdkRenderScriptMode()) {
+            fullLdlibs.add("dl")
+            fullLdlibs.add("log")
+            fullLdlibs.add("jnigraphics")
+            fullLdlibs.add("RScpp_static")
+            fullLdlibs.add("cutils")
+        }
+
+        if (!fullLdlibs.isEmpty()) {
+            sb.append('LOCAL_LDLIBS := \\\n')
+            for (String lib : fullLdlibs) {
+                sb.append('\t-l') .append(lib).append(' \\\n')
+            }
+            sb.append('\n')
+        }
+
+        sb.append('LOCAL_SRC_FILES := \\\n')
+        for (File sourceFile : sourceFiles) {
+            sb.append('\t').append(sourceFile.absolutePath).append(' \\\n')
+        }
+        sb.append('\n')
+
+        for (File sourceFolder : getSourceFolders()) {
+            sb.append("LOCAL_C_INCLUDES += ${sourceFolder.absolutePath}\n")
+        }
+
+        if (getNdkRenderScriptMode()) {
+            sb.append('LOCAL_LDFLAGS += -L$(call host-path,$(TARGET_C_INCLUDES)/../lib/rs)\n')
+
+            sb.append('LOCAL_C_INCLUDES += $(TARGET_C_INCLUDES)/rs/cpp\n')
+            sb.append('LOCAL_C_INCLUDES += $(TARGET_C_INCLUDES)/rs\n')
+            sb.append('LOCAL_C_INCLUDES += $(TARGET_OBJS)/$(LOCAL_MODULE)\n')
+        }
+
+        sb.append(
+                '\ninclude \$(BUILD_SHARED_LIBRARY)\n')
+
+        Files.write(sb.toString(), makefile, Charsets.UTF_8)
+    }
+
+    private void runNdkBuild(@NonNull File ndkLocation, @NonNull File makefile) {
+        NdkConfig ndk = getNdkConfig()
+
+        List<String> commands = Lists.newArrayList()
+
+        commands.add(ndkLocation.absolutePath + File.separator + "ndk-build")
+
+        commands.add("NDK_PROJECT_PATH=null")
+
+        commands.add("APP_BUILD_SCRIPT=" + makefile.absolutePath)
+
+        // target
+        IAndroidTarget target = getPlugin().loadedSdkParser.target
+        if (!target.isPlatform()) {
+            target = target.parent
+        }
+        commands.add("APP_PLATFORM=" + target.hashString())
+
+        // temp out
+        commands.add("NDK_OUT=" + getObjFolder().absolutePath)
+
+        // libs out
+        commands.add("NDK_LIBS_OUT=" + getSoFolder().absolutePath)
+
+        // debug builds
+        if (getDebuggable()) {
+            commands.add("NDK_DEBUG=1")
+        }
+
+        if (ndk.getStl() != null) {
+            commands.add("APP_STL=" + ndk.getStl())
+        }
+
+        Set<String> abiFilters = ndk.abiFilters
+        if (abiFilters != null && !abiFilters.isEmpty()) {
+            if (abiFilters.size() == 1) {
+                commands.add("APP_ABI=" + abiFilters.iterator().next())
+            } else {
+                Joiner joiner = Joiner.on(',').skipNulls()
+                commands.add("APP_ABI=" + joiner.join(abiFilters.iterator()))
+            }
+        } else {
+            commands.add("APP_ABI=all")
+        }
+
+        getBuilder().commandLineRunner.runCmdLine(commands, null)
+    }
+}
diff --git a/gradle/src/main/groovy/com/android/build/gradle/tasks/PackageApplication.groovy b/gradle/src/main/groovy/com/android/build/gradle/tasks/PackageApplication.groovy
index 549d765..de6d159 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/tasks/PackageApplication.groovy
+++ b/gradle/src/main/groovy/com/android/build/gradle/tasks/PackageApplication.groovy
@@ -19,6 +19,7 @@
 import com.android.build.gradle.internal.tasks.IncrementalTask
 import com.android.build.gradle.internal.tasks.OutputFileTask
 import com.android.builder.packaging.DuplicateFileException
+import org.gradle.api.file.FileTree
 import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.InputDirectory
 import org.gradle.api.tasks.InputFile
@@ -41,12 +42,14 @@
     @InputDirectory @Optional
     File javaResourceDir
 
-    @InputDirectory @Optional
-    File jniDir
+    Set<File> jniFolders
 
     @OutputFile
     File outputFile
 
+    @Input @Optional
+    Set<String> abiFilters
+
     // ----- PRIVATE TASK API -----
 
     @InputFiles
@@ -58,6 +61,16 @@
     @Nested @Optional
     SigningConfigDsl signingConfig
 
+    @InputFiles
+    public FileTree getNativeLibraries() {
+        FileTree src = null
+        Set<File> folders = getJniFolders()
+        if (!folders.isEmpty()) {
+            src = getProject().files(new ArrayList<Object>(folders)).getAsFileTree()
+        }
+        return src == null ? getProject().files().getAsFileTree() : src
+    }
+
     @Override
     protected void doFullTaskAction() {
         try {
@@ -66,7 +79,8 @@
                     getDexFile().absolutePath,
                     getPackagedJars(),
                     getJavaResourceDir()?.absolutePath,
-                    getJniDir()?.absolutePath,
+                    getJniFolders(),
+                    getAbiFilters(),
                     getJniDebugBuild(),
                     getSigningConfig(),
                     getOutputFile().absolutePath)
@@ -81,5 +95,4 @@
             throw new BuildException(e.getMessage(), e);
         }
     }
-
 }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/tasks/PreDex.groovy b/gradle/src/main/groovy/com/android/build/gradle/tasks/PreDex.groovy
new file mode 100644
index 0000000..6a464fe
--- /dev/null
+++ b/gradle/src/main/groovy/com/android/build/gradle/tasks/PreDex.groovy
@@ -0,0 +1,103 @@
+/*
+ * 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.build.gradle.tasks
+import com.android.SdkConstants
+import com.android.annotations.NonNull
+import com.android.build.gradle.internal.dsl.DexOptionsImpl
+import com.android.build.gradle.internal.tasks.BaseTask
+import com.android.builder.AndroidBuilder
+import com.android.builder.DexOptions
+import com.google.common.hash.HashCode
+import com.google.common.hash.HashFunction
+import com.google.common.hash.Hashing
+import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.Nested
+import org.gradle.api.tasks.OutputDirectory
+import org.gradle.api.tasks.TaskAction
+import org.gradle.api.tasks.incremental.IncrementalTaskInputs
+
+public class PreDex extends BaseTask {
+
+    // ----- PUBLIC TASK API -----
+
+    // ----- PRIVATE TASK API -----
+
+    // this is used automatically by Gradle, even though nothing
+    // in the class uses it.
+    @SuppressWarnings("GroovyUnusedDeclaration")
+    @InputFiles
+    Iterable<File> inputFiles
+
+    @OutputDirectory
+    File outputFolder
+
+    @Nested
+    DexOptionsImpl dexOptions
+
+    @TaskAction
+    void taskAction(IncrementalTaskInputs taskInputs) {
+        final File outFolder = getOutputFolder()
+        final DexOptions options = getDexOptions()
+
+        // if we are not in incremental mode, then outOfDate will contain
+        // all th files, but first we need to delete the previous output
+        if (!taskInputs.isIncremental()) {
+            emptyFolder(outFolder)
+        }
+
+        AndroidBuilder builder = getBuilder()
+
+        taskInputs.outOfDate { change ->
+
+            //noinspection GroovyAssignabilityCheck
+            File preDexedFile = getDexFileName(outFolder, change.file)
+            //noinspection GroovyAssignabilityCheck
+            builder.preDexLibrary(change.file, preDexedFile, options)
+        }
+
+        taskInputs.removed { change ->
+            //noinspection GroovyAssignabilityCheck
+            File preDexedFile = getDexFileName(outFolder, change.file)
+            preDexedFile.delete()
+        }
+    }
+
+    /**
+     * Returns a unique File for the pre-dexed library, even
+     * if there are 2 libraries with the same file names (but different
+     * paths)
+     *
+     * @param outFolder
+     * @param inputFile the library
+     * @return
+     */
+    @NonNull
+    private static File getDexFileName(@NonNull File outFolder, @NonNull File inputFile) {
+        // get the filename
+        String name = inputFile.getName();
+        // remove the extension
+        int pos = name.lastIndexOf('.');
+        if (pos != -1) {
+            name = name.substring(0, pos);
+        }
+
+        // add a hash of the original file path
+        HashFunction hashFunction = Hashing.md5();
+        HashCode hashCode = hashFunction.hashString(inputFile.getAbsolutePath());
+
+        return new File(outFolder, name + "-" + hashCode.toString() + SdkConstants.DOT_JAR);
+    }
+}
diff --git a/gradle/src/main/groovy/com/android/build/gradle/tasks/ProcessAndroidResources.groovy b/gradle/src/main/groovy/com/android/build/gradle/tasks/ProcessAndroidResources.groovy
index 201e47f..3c981ac 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/tasks/ProcessAndroidResources.groovy
+++ b/gradle/src/main/groovy/com/android/build/gradle/tasks/ProcessAndroidResources.groovy
@@ -51,6 +51,9 @@
     @OutputFile @Optional
     File proguardOutputFile
 
+    @Input
+    Collection<String> resourceConfigs
+
     // ----- PRIVATE TASK API -----
 
     @Nested
@@ -88,6 +91,7 @@
                 getProguardOutputFile()?.absolutePath,
                 getType(),
                 getDebuggable(),
-                getAaptOptions())
+                getAaptOptions(),
+                getResourceConfigs())
     }
 }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/tasks/ProcessTestManifest.groovy b/gradle/src/main/groovy/com/android/build/gradle/tasks/ProcessTestManifest.groovy
index 10fc11f..6a24155 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/tasks/ProcessTestManifest.groovy
+++ b/gradle/src/main/groovy/com/android/build/gradle/tasks/ProcessTestManifest.groovy
@@ -41,6 +41,12 @@
     @Input
     String instrumentationRunner
 
+    @Input
+    Boolean handleProfiling;
+
+    @Input
+    Boolean functionalTest;
+
     @Nested
     List<ManifestDependencyImpl> libraries
 
@@ -52,6 +58,8 @@
                 getTargetSdkVersion(),
                 getTestedPackageName(),
                 getInstrumentationRunner(),
+                getHandleProfiling(),
+                getFunctionalTest(),
                 getLibraries(),
                 getManifestOutputFile().absolutePath)
     }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/tasks/RenderscriptCompile.groovy b/gradle/src/main/groovy/com/android/build/gradle/tasks/RenderscriptCompile.groovy
index 853a0a9..a5f7986 100644
--- a/gradle/src/main/groovy/com/android/build/gradle/tasks/RenderscriptCompile.groovy
+++ b/gradle/src/main/groovy/com/android/build/gradle/tasks/RenderscriptCompile.groovy
@@ -15,7 +15,8 @@
  */
 
 package com.android.build.gradle.tasks
-import com.android.build.gradle.internal.tasks.BaseTask
+
+import com.android.build.gradle.internal.tasks.NdkTask
 import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.InputFiles
 import org.gradle.api.tasks.OutputDirectory
@@ -23,7 +24,7 @@
 /**
  * Task to compile Renderscript files. Supports incremental update.
  */
-public class RenderscriptCompile extends BaseTask {
+public class RenderscriptCompile extends NdkTask {
 
     // ----- PUBLIC TASK API -----
 
@@ -33,6 +34,13 @@
     @OutputDirectory
     File resOutputDir
 
+    @OutputDirectory
+    File objOutputDir
+
+    @OutputDirectory
+    File libOutputDir
+
+
     // ----- PRIVATE TASK API -----
 
     @InputFiles
@@ -42,14 +50,20 @@
     List<File> importDirs
 
     @Input
-    int optimLevel
-
-    @Input
     int targetApi
 
     @Input
+    boolean supportMode
+
+    @Input
+    int optimLevel
+
+    @Input
     boolean debugBuild
 
+    @Input
+    boolean ndkMode
+
     @TaskAction
     void taskAction() {
         // this is full run (always), clean the previous outputs
@@ -59,6 +73,12 @@
         File resDestDir = getResOutputDir()
         emptyFolder(resDestDir)
 
+        File objDestDir = getObjOutputDir()
+        emptyFolder(objDestDir)
+
+        File libDestDir = getLibOutputDir()
+        emptyFolder(libDestDir)
+
         // get the import folders. If the .rsh files are not directly under the import folders,
         // we need to get the leaf folders, as this is what llvm-rs-cc expects.
         List<File> importFolders = getBuilder().getLeafFolders("rsh",
@@ -69,8 +89,13 @@
                 importFolders,
                 sourceDestDir,
                 resDestDir,
+                objDestDir,
+                libDestDir,
                 getTargetApi(),
                 getDebugBuild(),
-                getOptimLevel())
+                getOptimLevel(),
+                getNdkMode(),
+                getSupportMode(),
+                getNdkConfig()?.abiFilters)
     }
 }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/tasks/ResourceException.java b/gradle/src/main/groovy/com/android/build/gradle/tasks/ResourceException.java
new file mode 100644
index 0000000..7d1b142
--- /dev/null
+++ b/gradle/src/main/groovy/com/android/build/gradle/tasks/ResourceException.java
@@ -0,0 +1,34 @@
+/*
+ * 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.build.gradle.tasks;
+
+import com.android.ide.common.res2.MergingException;
+
+/**
+ * Exception used for resource merging errors, thrown when
+ * a {@link MergingException} is thrown by the resource merging code.
+ * We can't just rethrow the {@linkplain MergingException} because
+ * gradle 1.8 seems to want a RuntimeException; without it you get
+ * the error message
+ * {@code
+ *     > Could not call IncrementalTask.taskAction() on task ':MyPrj:mergeDebugResources'
+ * }
+ */
+public class ResourceException extends RuntimeException {
+    public ResourceException(String message, Throwable throwable) {
+        super(message, throwable);
+    }
+}
diff --git a/gradle/src/test/groovy/com/android/build/gradle/AppPluginDslTest.groovy b/gradle/src/test/groovy/com/android/build/gradle/AppPluginDslTest.groovy
index 756dfc7..bef2ec7 100644
--- a/gradle/src/test/groovy/com/android/build/gradle/AppPluginDslTest.groovy
+++ b/gradle/src/test/groovy/com/android/build/gradle/AppPluginDslTest.groovy
@@ -266,19 +266,17 @@
                                            @NonNull String testedVariantName,
                                            @NonNull Set<ApplicationVariant> variants,
                                            @NonNull Set<TestVariant> testVariants) {
-        ApplicationVariant variant = findNamedItem(variants, variantName)
-        assertNotNull(variant)
+        ApplicationVariant variant = findNamedItem(variants, variantName, "variantData")
         assertNotNull(variant.testVariant)
         assertEquals(testedVariantName, variant.testVariant.name)
-        assertEquals(variant.testVariant, findNamedItem(testVariants, testedVariantName))
+        assertEquals(variant.testVariant, findNamedItemMaybe(testVariants, testedVariantName))
         checkTasks(variant)
         checkTasks(variant.testVariant)
     }
 
     private static void checkNonTestedVariant(@NonNull String variantName,
                                               @NonNull Set<ApplicationVariant> variants) {
-        ApplicationVariant variant = findNamedItem(variants, variantName)
-        assertNotNull(variant)
+        ApplicationVariant variant = findNamedItem(variants, variantName, "variantData")
         assertNull(variant.testVariant)
         checkTasks(variant)
     }
diff --git a/gradle/src/test/groovy/com/android/build/gradle/AppPluginInternalTest.groovy b/gradle/src/test/groovy/com/android/build/gradle/AppPluginInternalTest.groovy
index d840272..4578945 100644
--- a/gradle/src/test/groovy/com/android/build/gradle/AppPluginInternalTest.groovy
+++ b/gradle/src/test/groovy/com/android/build/gradle/AppPluginInternalTest.groovy
@@ -60,14 +60,9 @@
         List<BaseVariantData> variants = plugin.variantDataList
         assertEquals(3, variants.size()) // includes the test variant(s)
 
-        BaseVariantData debugVariant = findNamedItem(variants, "Debug")
-        assertNotNull(debugVariant)
-
-        BaseVariantData release = findNamedItem(variants, "Release")
-        assertNotNull(release)
-
-        BaseVariantData test = findNamedItem(variants, "Test")
-        assertNotNull(test)
+        findNamedItem(variants, "debug", "variantData")
+        findNamedItem(variants, "release", "variantData")
+        findNamedItem(variants, "debugTest", "variantData")
     }
 
     public void testDefaultConfig() {
@@ -138,17 +133,14 @@
         List<BaseVariantData> variants = plugin.variantDataList
         assertEquals(4, variants.size()) // includes the test variant(s)
 
-        BaseVariantData debugVariant = findNamedItem(variants, "Debug")
-        assertNotNull(debugVariant)
+        String[] variantNames = [
+                "debug", "release", "staging"]
 
-        BaseVariantData releaseVariant = findNamedItem(variants, "Release")
-        assertNotNull(releaseVariant)
+        for (String variantName : variantNames) {
+            findNamedItem(variants, variantName, "variantData")
+        }
 
-        BaseVariantData stagingVariant = findNamedItem(variants, "Staging")
-        assertNotNull(stagingVariant)
-
-        BaseVariantData testVariant = findNamedItem(variants, "Test")
-        assertNotNull(testVariant)
+        BaseVariantData testVariant = findNamedItem(variants, "stagingTest", "variantData")
         assertEquals("staging", testVariant.variantConfiguration.buildType.name)
     }
 
@@ -179,12 +171,13 @@
         List<BaseVariantData> variants = plugin.variantDataList
         assertEquals(6, variants.size()) // includes the test variant(s)
 
-        assertNotNull(findNamedItem(variants, "Flavor1Debug"))
-        assertNotNull(findNamedItem(variants, "Flavor1Release"))
-        assertNotNull(findNamedItem(variants, "Flavor1Test"))
-        assertNotNull(findNamedItem(variants, "Flavor2Debug"))
-        assertNotNull(findNamedItem(variants, "Flavor2Release"))
-        assertNotNull(findNamedItem(variants, "Flavor2Test"))
+        String[] variantNames = [
+                "flavor1Debug", "flavor1Release", "flavor1DebugTest",
+                "flavor2Debug", "flavor2Release", "flavor2DebugTest"]
+
+        for (String variantName : variantNames) {
+            findNamedItem(variants, variantName, "variantData")
+        }
     }
 
     public void testMultiFlavors() {
@@ -226,24 +219,29 @@
         List<BaseVariantData> variants = plugin.variantDataList
         assertEquals(18, variants.size())   // includes the test variant(s)
 
-        assertNotNull(findNamedItem(variants, "F1FaDebug"))
-        assertNotNull(findNamedItem(variants, "F1FbDebug"))
-        assertNotNull(findNamedItem(variants, "F1FcDebug"))
-        assertNotNull(findNamedItem(variants, "F2FaDebug"))
-        assertNotNull(findNamedItem(variants, "F2FbDebug"))
-        assertNotNull(findNamedItem(variants, "F2FcDebug"))
-        assertNotNull(findNamedItem(variants, "F1FaRelease"))
-        assertNotNull(findNamedItem(variants, "F1FbRelease"))
-        assertNotNull(findNamedItem(variants, "F1FcRelease"))
-        assertNotNull(findNamedItem(variants, "F2FaRelease"))
-        assertNotNull(findNamedItem(variants, "F2FbRelease"))
-        assertNotNull(findNamedItem(variants, "F2FcRelease"))
-        assertNotNull(findNamedItem(variants, "F1FaTest"))
-        assertNotNull(findNamedItem(variants, "F1FbTest"))
-        assertNotNull(findNamedItem(variants, "F1FcTest"))
-        assertNotNull(findNamedItem(variants, "F2FaTest"))
-        assertNotNull(findNamedItem(variants, "F2FbTest"))
-        assertNotNull(findNamedItem(variants, "F2FcTest"))
+        String[] variantNames = [
+                "f1FaDebug",
+                "f1FbDebug",
+                "f1FcDebug",
+                "f2FaDebug",
+                "f2FbDebug",
+                "f2FcDebug",
+                "f1FaRelease",
+                "f1FbRelease",
+                "f1FcRelease",
+                "f2FaRelease",
+                "f2FbRelease",
+                "f2FcRelease",
+                "f1FaDebugTest",
+                "f1FbDebugTest",
+                "f1FcDebugTest",
+                "f2FaDebugTest",
+                "f2FbDebugTest",
+                "f2FcDebugTest"];
+
+        for (String variantName : variantNames) {
+            findNamedItem(variants, variantName, "variantData");
+        }
     }
 
     public void testSigningConfigs() {
@@ -312,37 +310,31 @@
         BaseVariantData variant
         SigningConfig signingConfig
 
-        variant = findNamedItem(variants, "Flavor1Debug")
-        assertNotNull(variant)
+        variant = findNamedItem(variants, "flavor1Debug", "variantData")
         signingConfig = variant.variantConfiguration.signingConfig
         assertNotNull(signingConfig)
         assertEquals(KeystoreHelper.defaultDebugKeystoreLocation(), signingConfig.storeFile?.absolutePath)
 
-        variant = findNamedItem(variants, "Flavor1Staging")
-        assertNotNull(variant)
+        variant = findNamedItem(variants, "flavor1Staging", "variantData")
         signingConfig = variant.variantConfiguration.signingConfig
         assertNull(signingConfig)
 
-        variant = findNamedItem(variants, "Flavor1Release")
-        assertNotNull(variant)
+        variant = findNamedItem(variants, "flavor1Release", "variantData")
         signingConfig = variant.variantConfiguration.signingConfig
         assertNotNull(signingConfig)
         assertEquals(new File(project.projectDir, "a3"), signingConfig.storeFile)
 
-        variant = findNamedItem(variants, "Flavor2Debug")
-        assertNotNull(variant)
+        variant = findNamedItem(variants, "flavor2Debug", "variantData")
         signingConfig = variant.variantConfiguration.signingConfig
         assertNotNull(signingConfig)
         assertEquals(KeystoreHelper.defaultDebugKeystoreLocation(), signingConfig.storeFile?.absolutePath)
 
-        variant = findNamedItem(variants, "Flavor2Staging")
-        assertNotNull(variant)
+        variant = findNamedItem(variants, "flavor2Staging", "variantData")
         signingConfig = variant.variantConfiguration.signingConfig
         assertNotNull(signingConfig)
         assertEquals(new File(project.projectDir, "a1"), signingConfig.storeFile)
 
-        variant = findNamedItem(variants, "Flavor2Release")
-        assertNotNull(variant)
+        variant = findNamedItem(variants, "flavor2Release", "variantData")
         signingConfig = variant.variantConfiguration.signingConfig
         assertNotNull(signingConfig)
         assertEquals(new File(project.projectDir, "a3"), signingConfig.storeFile)
diff --git a/gradle/src/test/groovy/com/android/build/gradle/internal/test/BaseTest.groovy b/gradle/src/test/groovy/com/android/build/gradle/internal/test/BaseTest.groovy
index 5a96009..269a557 100755
--- a/gradle/src/test/groovy/com/android/build/gradle/internal/test/BaseTest.groovy
+++ b/gradle/src/test/groovy/com/android/build/gradle/internal/test/BaseTest.groovy
@@ -16,6 +16,7 @@
 
 package com.android.build.gradle.internal.test
 import com.android.annotations.NonNull
+import com.android.annotations.Nullable
 import com.android.sdklib.internal.project.ProjectProperties
 import com.android.sdklib.internal.project.ProjectPropertiesWorkingCopy
 import junit.framework.TestCase
@@ -100,10 +101,31 @@
         return null
     }
 
-    protected static File createLocalProp(File project, File sdkDir) {
+    /**
+     * Returns the SDK folder as built from the Android source tree.
+     * @return
+     */
+    protected File getNdkDir() {
+        String androidHome = System.getenv("ANDROID_NDK_HOME");
+        if (androidHome != null) {
+            File f = new File(androidHome);
+            if (f.isDirectory()) {
+                return f;
+            } else {
+                System.out.println("Failed to find NDK in ANDROID_NDK_HOME=" + androidHome)
+            }
+        }
+    }
+
+    protected static File createLocalProp(@NonNull File project,
+                                          @NonNull File sdkDir,
+                                          @Nullable File ndkDir) {
         ProjectPropertiesWorkingCopy localProp = ProjectProperties.create(
                 project.absolutePath, ProjectProperties.PropertyType.LOCAL)
         localProp.setProperty(ProjectProperties.PROPERTY_SDK, sdkDir.absolutePath)
+        if (ndkDir != null) {
+            localProp.setProperty(ProjectProperties.PROPERTY_NDK, ndkDir.absolutePath)
+        }
         localProp.save()
 
         return (File) localProp.file
@@ -112,14 +134,15 @@
     protected File runTasksOn(String name, String gradleVersion, String... tasks) {
         File project = new File(testDir, name)
 
-        runGradleTasks(sdkDir, gradleVersion, project, tasks)
+        runGradleTasks(sdkDir, ndkDir, gradleVersion, project, tasks)
 
         return project;
     }
 
-    protected static void runGradleTasks(File sdkDir, String gradleVersion,
+    protected static void runGradleTasks(File sdkDir, File ndkDir,
+                                         String gradleVersion,
                                          File project, String... tasks) {
-        File localProp = createLocalProp(project, sdkDir)
+        File localProp = createLocalProp(project, sdkDir, ndkDir)
 
         try {
 
@@ -160,8 +183,8 @@
      * @param name the name of the item to return
      * @return the found item or null
      */
-    protected static <T> T findNamedItem(@NonNull Collection<T> items,
-                                         @NonNull String name) {
+    protected static <T> T findNamedItemMaybe(@NonNull Collection<T> items,
+                                              @NonNull String name) {
         for (T item : items) {
             if (name.equals(item.name)) {
                 return item
@@ -170,4 +193,18 @@
 
         return null
     }
+
+    /**
+     * Returns the name item from the collection of items. The items *must* have a "name" property.
+     * @param items the item collection to search for a match
+     * @param name the name of the item to return
+     * @return the found item or null
+     */
+    protected static <T> T findNamedItem(@NonNull Collection<T> items,
+                                         @NonNull String name,
+                                         @NonNull String typeName) {
+        T foundItem = findNamedItemMaybe(items, name);
+        assertNotNull("$name $typeName null-check", foundItem)
+        return foundItem
+    }
 }
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index ff923bb..b5d9920 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=../../../external/gradle/gradle-1.7-bin.zip
+distributionUrl=../../../external/gradle/gradle-1.9-bin.zip
diff --git a/javadoc.gradle b/javadoc.gradle
new file mode 100644
index 0000000..720844a
--- /dev/null
+++ b/javadoc.gradle
@@ -0,0 +1,16 @@
+javadoc {
+    exclude               "**/internal/**"
+    options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PROTECTED
+
+    title                 project.ext.pomName
+}
+
+task javadocJar(type: Jar, dependsOn:javadoc) {
+    classifier         'javadoc'
+    from               javadoc.destinationDir
+}
+
+// add javadoc jar tasks as artifacts
+artifacts {
+    archives javadocJar
+}
diff --git a/publish.gradle b/publish.gradle
new file mode 100644
index 0000000..7d8a319
--- /dev/null
+++ b/publish.gradle
@@ -0,0 +1,87 @@
+apply plugin: 'signing'
+
+def getVersion() {
+    if (project.has("release")) {
+        return project.ext.baseVersion
+    }
+
+    return project.ext.baseVersion + '-SNAPSHOT'
+}
+
+version = getVersion()
+
+task publishLocal(type: Upload) {
+    configuration = configurations.archives
+    repositories {
+        mavenDeployer {
+            repository(url: uri("$rootProject.ext.androidHostOut/repo"))
+        }
+    }
+}
+
+project.ext.sonatypeUsername = hasProperty('sonatypeUsername') ? sonatypeUsername : ""
+project.ext.sonatypePassword = hasProperty('sonatypePassword') ? sonatypePassword : ""
+
+uploadArchives {
+    repositories {
+        mavenDeployer {
+            beforeDeployment { MavenDeployment deployment ->
+                if (!project.has("release")) {
+                    throw new StopExecutionException("uploadArchives must be called with the release.gradle init script")
+                }
+
+                if (project.ext.sonatypeUsername.length() == 0 || project.ext.sonatypePassword.length() == 0) {
+                    throw new StopExecutionException("uploadArchives cannot be called without sonatype username and password")
+                }
+
+                signing.signPom(deployment)
+            }
+
+            repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
+                authentication(userName: project.ext.sonatypeUsername, password: project.ext.sonatypePassword)
+            }
+
+            pom.project {
+                name project.ext.pomName
+                description project.ext.pomDesc
+                url 'http://tools.android.com'
+                inceptionYear '2007'
+
+                licenses {
+                    license {
+                        name 'The Apache Software License, Version 2.0'
+                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
+                        distribution 'repo'
+                    }
+                }
+
+                scm {
+                    url 'https://android.googlesource.com/platform/tools/build'
+                    connection 'git://android.googlesource.com/platform/tools/build.git'
+                }
+                developers {
+                    developer {
+                        name 'The Android Open Source Project'
+                    }
+                }
+            }
+        }
+    }
+}
+
+// custom tasks for creating source/javadoc jars
+task sourcesJar(type: Jar, dependsOn:classes) {
+    classifier = 'sources'
+    from sourceSets.main.allSource
+}
+
+// add source jar tasks as artifacts
+artifacts {
+    archives jar
+    archives sourcesJar
+}
+
+signing {
+    required { project.has("release") && gradle.taskGraph.hasTask("uploadArchives") }
+    sign configurations.archives
+}
diff --git a/tests/3rdPartyTests/build.gradle b/tests/3rdPartyTests/build.gradle
index 62a4405..b899f9a 100644
--- a/tests/3rdPartyTests/build.gradle
+++ b/tests/3rdPartyTests/build.gradle
@@ -3,6 +3,6 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
diff --git a/tests/3rdPartyTests/buildSrc/build.gradle b/tests/3rdPartyTests/buildSrc/build.gradle
index 2b78104..8e1c757 100644
--- a/tests/3rdPartyTests/buildSrc/build.gradle
+++ b/tests/3rdPartyTests/buildSrc/build.gradle
@@ -6,5 +6,5 @@
 }
 
 dependencies {
-    compile 'com.android.tools.build:builder-test-api:0.5.0-SNAPSHOT'
+    compile 'com.android.tools.build:builder-test-api:0.7.0-SNAPSHOT'
 }
\ No newline at end of file
diff --git a/tests/3rdPartyTests/buildSrc/src/main/java/com/android/tests/basic/buildscript/FakeDevice.java b/tests/3rdPartyTests/buildSrc/src/main/java/com/android/tests/basic/buildscript/FakeDevice.java
index 9fbe9e1..df810fe 100644
--- a/tests/3rdPartyTests/buildSrc/src/main/java/com/android/tests/basic/buildscript/FakeDevice.java
+++ b/tests/3rdPartyTests/buildSrc/src/main/java/com/android/tests/basic/buildscript/FakeDevice.java
@@ -12,6 +12,7 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
@@ -120,8 +121,8 @@
     }
 
     @NonNull
-    public String getAbi() {
-        return "fake";
+    public List<String> getAbis() {
+        return Collections.singletonList("fake");
     }
 
     public int getDensity() {
diff --git a/tests/aidl/build.gradle b/tests/aidl/build.gradle
index 2890aed..daacecc 100644
--- a/tests/aidl/build.gradle
+++ b/tests/aidl/build.gradle
@@ -3,7 +3,7 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
 apply plugin: 'android'
diff --git a/tests/api/build.gradle b/tests/api/build.gradle
index 62a4405..b899f9a 100644
--- a/tests/api/build.gradle
+++ b/tests/api/build.gradle
@@ -3,6 +3,6 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
diff --git a/tests/applibtest/build.gradle b/tests/applibtest/build.gradle
index 62a4405..b899f9a 100644
--- a/tests/applibtest/build.gradle
+++ b/tests/applibtest/build.gradle
@@ -3,6 +3,6 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
diff --git a/tests/artifactApi/build.gradle b/tests/artifactApi/build.gradle
new file mode 100644
index 0000000..be18382
--- /dev/null
+++ b/tests/artifactApi/build.gradle
@@ -0,0 +1,105 @@
+buildscript {
+    repositories {
+        maven { url '../../../../out/host/gradle/repo' }
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
+    }
+}
+
+apply plugin: 'android'
+
+import com.android.builder.model.ArtifactMetaData
+import com.android.builder.model.SourceProvider
+
+android.registerArtifactType("__test__", false, ArtifactMetaData.TYPE_JAVA)
+
+android.buildTypes.all { type ->
+    project.android.registerBuildTypeSourceProvider("__test__", type, getProvider("buildType:$type.name"))
+}
+
+android.productFlavors.all { flavor ->
+    project.android.registerProductFlavorSourceProvider("__test__", flavor, getProvider("productFlavor:$flavor.name"))
+}
+
+android.applicationVariants.all { variant ->
+    project.android.registerJavaArtifact(
+            "__test__",
+            variant,
+            "assemble:$variant.name",
+            "compile:$variant.name",
+            new File("classesFolder:$variant.name"),
+            getProvider("provider:$variant.name"))
+}
+
+android {
+    compileSdkVersion 15
+    buildToolsVersion "18.0.1"
+
+    flavorGroups  "pricing", "releaseType"
+
+    productFlavors {
+
+        beta {
+            flavorGroup "releaseType"
+        }
+
+        normal {
+            flavorGroup "releaseType"
+        }
+
+        free {
+            flavorGroup "pricing"
+        }
+
+        paid {
+            flavorGroup "pricing"
+        }
+    }
+}
+
+public class SourceProviderImpl implements SourceProvider, Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private final String name;
+
+    SourceProviderImpl(String name) {
+        this.name = name
+    }
+    
+    File getManifestFile() {
+        return new File(name)
+    }
+
+    Collection<File> getJavaDirectories() {
+        return Collections.emptyList()
+    }
+
+    Collection<File> getResourcesDirectories() {
+        return Collections.emptyList()
+    }
+
+    Collection<File> getAidlDirectories() {
+        return Collections.emptyList()
+    }
+
+    Collection<File> getRenderscriptDirectories() {
+        return Collections.emptyList()
+    }
+
+    Collection<File> getJniDirectories() {
+        return Collections.emptyList()
+    }
+
+    Collection<File> getResDirectories() {
+        return Collections.emptyList()
+    }
+
+    Collection<File> getAssetsDirectories() {
+        return Collections.emptyList()
+     }
+}
+
+SourceProvider getProvider(String name) {
+   return new SourceProviderImpl(name)
+}
\ No newline at end of file
diff --git a/tests/artifactApi/src/main/AndroidManifest.xml b/tests/artifactApi/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..1c35a5b
--- /dev/null
+++ b/tests/artifactApi/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="com.android.tests.overlay2">
+    <application android:label="@string/app_name" android:icon="@drawable/icon">
+        <activity android:name=".Main"
+                  android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/artifactApi/src/main/java/com/android/tests/overlay2/Main.java b/tests/artifactApi/src/main/java/com/android/tests/overlay2/Main.java
new file mode 100644
index 0000000..e8a0a83
--- /dev/null
+++ b/tests/artifactApi/src/main/java/com/android/tests/overlay2/Main.java
@@ -0,0 +1,15 @@
+package com.android.tests.overlay2;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class Main extends Activity
+{
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+    }
+}
diff --git a/tests/artifactApi/src/main/res/drawable/icon.png b/tests/artifactApi/src/main/res/drawable/icon.png
new file mode 100644
index 0000000..a07c69f
--- /dev/null
+++ b/tests/artifactApi/src/main/res/drawable/icon.png
Binary files differ
diff --git a/tests/artifactApi/src/main/res/drawable/no_overlay.png b/tests/artifactApi/src/main/res/drawable/no_overlay.png
new file mode 100644
index 0000000..47e1adf
--- /dev/null
+++ b/tests/artifactApi/src/main/res/drawable/no_overlay.png
Binary files differ
diff --git a/tests/artifactApi/src/main/res/layout/main.xml b/tests/artifactApi/src/main/res/layout/main.xml
new file mode 100644
index 0000000..6cd0549
--- /dev/null
+++ b/tests/artifactApi/src/main/res/layout/main.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    >
+    <ImageView
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:src="@drawable/no_overlay"
+        android:id="@+id/no_overlay" />
+</LinearLayout>
+
diff --git a/tests/artifactApi/src/main/res/values/strings.xml b/tests/artifactApi/src/main/res/values/strings.xml
new file mode 100644
index 0000000..d1420e7
--- /dev/null
+++ b/tests/artifactApi/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">_Test-Overlay2</string>
+</resources>
diff --git a/tests/assets/build.gradle b/tests/assets/build.gradle
index f210fcf..ef3d0aa 100644
--- a/tests/assets/build.gradle
+++ b/tests/assets/build.gradle
@@ -3,7 +3,7 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
 
diff --git a/tests/attrOrder/build.gradle b/tests/attrOrder/build.gradle
index 62a4405..b899f9a 100644
--- a/tests/attrOrder/build.gradle
+++ b/tests/attrOrder/build.gradle
@@ -3,6 +3,6 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
diff --git a/tests/autorepo/build.gradle b/tests/autorepo/build.gradle
index 70afd2e..d10badb 100644
--- a/tests/autorepo/build.gradle
+++ b/tests/autorepo/build.gradle
@@ -3,7 +3,7 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
 apply plugin: 'android'
diff --git a/tests/basic/build.gradle b/tests/basic/build.gradle
index 38dcc52..b1c9749 100644
--- a/tests/basic/build.gradle
+++ b/tests/basic/build.gradle
@@ -3,7 +3,7 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
 apply plugin: 'android'
@@ -40,8 +40,14 @@
         minSdkVersion 16
         targetSdkVersion 16
 
-        buildConfig "private final static boolean DEFAULT = true;", \
-                    "private final static String FOO = \"foo\";"
+        testInstrumentationRunner "android.test.InstrumentationTestRunner"
+        testHandleProfiling false
+
+        buildConfigField "boolean", "DEFAULT", "true"
+        buildConfigField "String", "FOO", "\"foo\""
+
+        resConfig "en"
+        resConfigs "nodpi", "hdpi"
     }
 
     buildTypes {
@@ -49,7 +55,7 @@
             packageNameSuffix ".debug"
             signingConfig signingConfigs.myConfig
 
-            buildConfig "private final static boolean DEBUG2 = false;"
+            buildConfigField "String", "FOO", "\"bar\""
         }
     }
 
diff --git a/tests/basic/src/instrumentTest/java/com/android/tests/basic/MainTest.java b/tests/basic/src/instrumentTest/java/com/android/tests/basic/MainTest.java
index 7cf7329..9f4b2de 100644
--- a/tests/basic/src/instrumentTest/java/com/android/tests/basic/MainTest.java
+++ b/tests/basic/src/instrumentTest/java/com/android/tests/basic/MainTest.java
@@ -34,5 +34,10 @@
     public void testPreconditions() {
         assertNotNull(mTextView);
     }
+
+    @MediumTest
+    public void testBuildConfig() {
+        assertEquals("bar", BuildConfig.FOO);
+    }
 }
 
diff --git a/tests/basicMultiFlavors/build.gradle b/tests/basicMultiFlavors/build.gradle
new file mode 100644
index 0000000..d4bce43
--- /dev/null
+++ b/tests/basicMultiFlavors/build.gradle
@@ -0,0 +1,35 @@
+buildscript {
+    repositories {
+        maven { url '../../../../out/host/gradle/repo' }
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
+    }
+}
+apply plugin: 'android'
+
+android {
+    compileSdkVersion 15
+    buildToolsVersion "18.0.1"
+
+    flavorGroups  "pricing", "releaseType"
+
+    productFlavors {
+
+        beta {
+            flavorGroup "releaseType"
+        }
+
+        normal {
+            flavorGroup "releaseType"
+        }
+
+        free {
+            flavorGroup "pricing"
+        }
+
+        paid {
+            flavorGroup "pricing"
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/basicMultiFlavors/src/f1/res/values/strings.xml b/tests/basicMultiFlavors/src/f1/res/values/strings.xml
new file mode 100644
index 0000000..7154c04
--- /dev/null
+++ b/tests/basicMultiFlavors/src/f1/res/values/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">_Test-Flavored-f1</string>
+    <string name="text">F1 text</string>
+</resources>
diff --git a/tests/basicMultiFlavors/src/f1Staging/res/values/strings.xml b/tests/basicMultiFlavors/src/f1Staging/res/values/strings.xml
new file mode 100644
index 0000000..782422e
--- /dev/null
+++ b/tests/basicMultiFlavors/src/f1Staging/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="text">F1-Staging text</string>
+</resources>
diff --git a/tests/basicMultiFlavors/src/f2/AndroidManifest.xml b/tests/basicMultiFlavors/src/f2/AndroidManifest.xml
new file mode 100644
index 0000000..ce0bb8d
--- /dev/null
+++ b/tests/basicMultiFlavors/src/f2/AndroidManifest.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="">
+    <application>
+        <activity android:name="com.android.tests.flavored.OtherActivity"
+                  android:label="@string/other_activity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/basicMultiFlavors/src/f2/java/com/android/tests/flavored/OtherActivity.java b/tests/basicMultiFlavors/src/f2/java/com/android/tests/flavored/OtherActivity.java
new file mode 100644
index 0000000..6ffac9c
--- /dev/null
+++ b/tests/basicMultiFlavors/src/f2/java/com/android/tests/flavored/OtherActivity.java
@@ -0,0 +1,15 @@
+package com.android.tests.flavored;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class OtherActivity extends Activity
+{
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main2);
+    }
+}
diff --git a/tests/basicMultiFlavors/src/f2/res/layout/main2.xml b/tests/basicMultiFlavors/src/f2/res/layout/main2.xml
new file mode 100644
index 0000000..90c3c43
--- /dev/null
+++ b/tests/basicMultiFlavors/src/f2/res/layout/main2.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    >
+<TextView
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:text="Test App - Flavored - f2"
+    android:id="@+id/text2"
+    />
+</LinearLayout>
+
diff --git a/tests/basicMultiFlavors/src/f2/res/values/strings.xml b/tests/basicMultiFlavors/src/f2/res/values/strings.xml
new file mode 100644
index 0000000..bb53fd4
--- /dev/null
+++ b/tests/basicMultiFlavors/src/f2/res/values/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">_Test-Flavored-f2</string>
+    <string name="other_activity">_Test-f2-act2</string>
+</resources>
diff --git a/tests/basicMultiFlavors/src/main/AndroidManifest.xml b/tests/basicMultiFlavors/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..0d1c338
--- /dev/null
+++ b/tests/basicMultiFlavors/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="com.android.tests.flavored">
+    <application android:label="@string/app_name" android:icon="@drawable/icon">
+        <activity android:name=".Main"
+                  android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/basicMultiFlavors/src/main/java/com/android/tests/flavored/Main.java b/tests/basicMultiFlavors/src/main/java/com/android/tests/flavored/Main.java
new file mode 100644
index 0000000..26debd3
--- /dev/null
+++ b/tests/basicMultiFlavors/src/main/java/com/android/tests/flavored/Main.java
@@ -0,0 +1,15 @@
+package com.android.tests.flavored;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class Main extends Activity
+{
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+    }
+}
diff --git a/tests/basicMultiFlavors/src/main/res/drawable/icon.png b/tests/basicMultiFlavors/src/main/res/drawable/icon.png
new file mode 100644
index 0000000..a07c69f
--- /dev/null
+++ b/tests/basicMultiFlavors/src/main/res/drawable/icon.png
Binary files differ
diff --git a/tests/basicMultiFlavors/src/main/res/layout/main.xml b/tests/basicMultiFlavors/src/main/res/layout/main.xml
new file mode 100644
index 0000000..9d4e976
--- /dev/null
+++ b/tests/basicMultiFlavors/src/main/res/layout/main.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    >
+<TextView
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:text="@string/text"
+    android:id="@+id/text"
+    />
+</LinearLayout>
+
diff --git a/tests/basicMultiFlavors/src/main/res/values/strings.xml b/tests/basicMultiFlavors/src/main/res/values/strings.xml
new file mode 100644
index 0000000..46d8260
--- /dev/null
+++ b/tests/basicMultiFlavors/src/main/res/values/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">###</string>
+    <string name="text">default text</string>
+</resources>
diff --git a/tests/build.gradle b/tests/build.gradle
new file mode 100644
index 0000000..a492fa1
--- /dev/null
+++ b/tests/build.gradle
@@ -0,0 +1,10 @@
+task("printTasks") << {
+    project.tasks.each { t ->
+        print("${t.name} -> ")
+        t.dependsOn.each { t2 ->
+            if (t2 instanceof Task)
+                print("${t2.name}, ")
+        }
+        println("")
+    }
+}
\ No newline at end of file
diff --git a/tests/dependencies/build.gradle b/tests/dependencies/build.gradle
index 0f15c02..a7810ac 100644
--- a/tests/dependencies/build.gradle
+++ b/tests/dependencies/build.gradle
@@ -3,7 +3,7 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
 apply plugin: 'android'
diff --git a/tests/dependencyChecker/build.gradle b/tests/dependencyChecker/build.gradle
index d591405..771fa54 100644
--- a/tests/dependencyChecker/build.gradle
+++ b/tests/dependencyChecker/build.gradle
@@ -3,7 +3,7 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
 apply plugin: 'android'
diff --git a/tests/flavored/build.gradle b/tests/flavored/build.gradle
index edc6fa5..d08c3fe 100644
--- a/tests/flavored/build.gradle
+++ b/tests/flavored/build.gradle
@@ -3,7 +3,7 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
 apply plugin: 'android'
@@ -15,28 +15,23 @@
     testBuildType = "staging"
 
     defaultConfig {
-        buildConfig "private final static boolean DEFAULT = true;", \
-                    "private final static String FOO = \"foo\";"
     }
 
     productFlavors {
         f1 {
             packageName = "com.android.tests.flavored.f1"
             versionName = "1.0.0-f1"
-            buildConfig "private final static String FLAVOR = \"f1\";"
         }
         f2 {
             packageName = "com.android.tests.flavored.f2"
             versionName = "1.0.0-f2"
-            buildConfig "private final static String FLAVOR = \"f2\";"
         }
     }
-    
+
     buildTypes {
         debug {
             packageNameSuffix = ".debug"
             versionNameSuffix = ".D"
-            buildConfig "private final static boolean DEBUG2 = false;"
         }
         staging {
             packageNameSuffix = ".staging"
diff --git a/tests/flavored/src/f1/res/values/strings.xml b/tests/flavored/src/f1/res/values/strings.xml
index f081f40..7154c04 100644
--- a/tests/flavored/src/f1/res/values/strings.xml
+++ b/tests/flavored/src/f1/res/values/strings.xml
@@ -1,4 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
     <string name="app_name">_Test-Flavored-f1</string>
+    <string name="text">F1 text</string>
 </resources>
diff --git a/tests/flavored/src/f1Staging/res/values/strings.xml b/tests/flavored/src/f1Staging/res/values/strings.xml
new file mode 100644
index 0000000..782422e
--- /dev/null
+++ b/tests/flavored/src/f1Staging/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="text">F1-Staging text</string>
+</resources>
diff --git a/tests/flavored/src/instrumentTest/java/com/android/tests/flavored/MainTest.java b/tests/flavored/src/instrumentTest/java/com/android/tests/flavored/MainTest.java
index f44e0f0..b22c53d 100644
--- a/tests/flavored/src/instrumentTest/java/com/android/tests/flavored/MainTest.java
+++ b/tests/flavored/src/instrumentTest/java/com/android/tests/flavored/MainTest.java
@@ -34,5 +34,14 @@
     public void testPreconditions() {
         assertNotNull(mTextView);
     }
+
+    @MediumTest
+    public void testStagingText() {
+        if ("f1".equals(BuildConfig.FLAVOR)) {
+            assertEquals("F1-Staging text", mTextView.getText());
+        } else {
+            assertEquals("default text", mTextView.getText());
+        }
+    }
 }
 
diff --git a/tests/flavored/src/main/res/layout/main.xml b/tests/flavored/src/main/res/layout/main.xml
index 058715f..9d4e976 100644
--- a/tests/flavored/src/main/res/layout/main.xml
+++ b/tests/flavored/src/main/res/layout/main.xml
@@ -7,7 +7,7 @@
 <TextView
     android:layout_width="fill_parent"
     android:layout_height="wrap_content"
-    android:text="Test App - Flavored"
+    android:text="@string/text"
     android:id="@+id/text"
     />
 </LinearLayout>
diff --git a/tests/flavored/src/main/res/values/strings.xml b/tests/flavored/src/main/res/values/strings.xml
index 24db545..46d8260 100644
--- a/tests/flavored/src/main/res/values/strings.xml
+++ b/tests/flavored/src/main/res/values/strings.xml
@@ -1,4 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
     <string name="app_name">###</string>
+    <string name="text">default text</string>
 </resources>
diff --git a/tests/flavorlib/build.gradle b/tests/flavorlib/build.gradle
index f210fcf..ef3d0aa 100644
--- a/tests/flavorlib/build.gradle
+++ b/tests/flavorlib/build.gradle
@@ -3,7 +3,7 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
 
diff --git a/tests/flavorlibWithFailedTests/build.gradle b/tests/flavorlibWithFailedTests/build.gradle
index f210fcf..ef3d0aa 100644
--- a/tests/flavorlibWithFailedTests/build.gradle
+++ b/tests/flavorlibWithFailedTests/build.gradle
@@ -3,7 +3,7 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
 
diff --git a/tests/flavors/build.gradle b/tests/flavors/build.gradle
index cd81f99..1b1c683 100644
--- a/tests/flavors/build.gradle
+++ b/tests/flavors/build.gradle
@@ -3,7 +3,7 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
 apply plugin: 'android'
@@ -17,21 +17,16 @@
     productFlavors {
         f1 {
             flavorGroup   "group1"
-
-            buildConfig   "public final static String GROUP1 = \"F1\";"
         }
         f2 {
             flavorGroup   "group1"
-            buildConfig   "public final static String GROUP1 = \"F2\";"
         }
 
         fa {
             flavorGroup   "group2"
-            buildConfig   "public final static String GROUP2 = \"FA\";"
         }
         fb {
             flavorGroup   "group2"
-            buildConfig   "public final static String GROUP2 = \"FB\";"
         }
     }
 }
diff --git a/tests/flavors/src/f1/java/com/android/tests/flavors/group1/SomeClass.java b/tests/flavors/src/f1/java/com/android/tests/flavors/group1/SomeClass.java
index a5c30b2..ffbf5ab 100644
--- a/tests/flavors/src/f1/java/com/android/tests/flavors/group1/SomeClass.java
+++ b/tests/flavors/src/f1/java/com/android/tests/flavors/group1/SomeClass.java
@@ -2,6 +2,6 @@
 
 public class SomeClass {
     public static String getString() {
-        return "F1";
+        return "f1";
     }
 }
\ No newline at end of file
diff --git a/tests/flavors/src/f1/res/values/strings.xml b/tests/flavors/src/f1/res/values/strings.xml
index 73e7584..b4db3e8 100644
--- a/tests/flavors/src/f1/res/values/strings.xml
+++ b/tests/flavors/src/f1/res/values/strings.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
 
-    <string name="group1_string">F1</string>
-    <string name="general_string">F1</string>
+    <string name="group1_string">f1</string>
+    <string name="general_string">f1</string>
 
 </resources>
\ No newline at end of file
diff --git a/tests/flavors/src/f2/java/com/android/tests/flavors/group1/SomeClass.java b/tests/flavors/src/f2/java/com/android/tests/flavors/group1/SomeClass.java
index 5974587..09327a6 100644
--- a/tests/flavors/src/f2/java/com/android/tests/flavors/group1/SomeClass.java
+++ b/tests/flavors/src/f2/java/com/android/tests/flavors/group1/SomeClass.java
@@ -2,6 +2,6 @@
 
 public class SomeClass {
     public static String getString() {
-        return "F2";
+        return "f2";
     }
 }
\ No newline at end of file
diff --git a/tests/flavors/src/f2/res/values/strings.xml b/tests/flavors/src/f2/res/values/strings.xml
index 855e7f6..7a306fc 100644
--- a/tests/flavors/src/f2/res/values/strings.xml
+++ b/tests/flavors/src/f2/res/values/strings.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
 
-    <string name="group1_string">F2</string>
-    <string name="general_string">F2</string>
+    <string name="group1_string">f2</string>
+    <string name="general_string">f2</string>
 
 </resources>
\ No newline at end of file
diff --git a/tests/flavors/src/fa/java/com/android/tests/flavors/group2/SomeClass.java b/tests/flavors/src/fa/java/com/android/tests/flavors/group2/SomeClass.java
index 40a939e..ae7677b 100644
--- a/tests/flavors/src/fa/java/com/android/tests/flavors/group2/SomeClass.java
+++ b/tests/flavors/src/fa/java/com/android/tests/flavors/group2/SomeClass.java
@@ -2,6 +2,6 @@
 
 public class SomeClass {
     public static String getString() {
-        return "FA";
+        return "fa";
     }
 }
\ No newline at end of file
diff --git a/tests/flavors/src/fa/res/values/strings.xml b/tests/flavors/src/fa/res/values/strings.xml
index 331cb90..f8cefe7 100644
--- a/tests/flavors/src/fa/res/values/strings.xml
+++ b/tests/flavors/src/fa/res/values/strings.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
 
-    <string name="group2_string">FA</string>
-    <string name="general_string">FA</string>
+    <string name="group2_string">fa</string>
+    <string name="general_string">fa</string>
 
 </resources>
\ No newline at end of file
diff --git a/tests/flavors/src/fb/java/com/android/tests/flavors/group2/SomeClass.java b/tests/flavors/src/fb/java/com/android/tests/flavors/group2/SomeClass.java
index 7441b61..5768d64 100644
--- a/tests/flavors/src/fb/java/com/android/tests/flavors/group2/SomeClass.java
+++ b/tests/flavors/src/fb/java/com/android/tests/flavors/group2/SomeClass.java
@@ -2,6 +2,6 @@
 
 public class SomeClass {
     public static String getString() {
-        return "FB";
+        return "fb";
     }
 }
\ No newline at end of file
diff --git a/tests/flavors/src/fb/res/values/strings.xml b/tests/flavors/src/fb/res/values/strings.xml
index fa0733f..8fa24dc 100644
--- a/tests/flavors/src/fb/res/values/strings.xml
+++ b/tests/flavors/src/fb/res/values/strings.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
 
-    <string name="group2_string">FB</string>
-    <string name="general_string">FB</string>
+    <string name="group2_string">fb</string>
+    <string name="general_string">fb</string>
 
 </resources>
\ No newline at end of file
diff --git a/tests/flavors/src/instrumentTestF1/java/com/android/tests/flavors/MainActivityGroup1Test.java b/tests/flavors/src/instrumentTestF1/java/com/android/tests/flavors/MainActivityGroup1Test.java
index 2c043ef..9379d96 100644
--- a/tests/flavors/src/instrumentTestF1/java/com/android/tests/flavors/MainActivityGroup1Test.java
+++ b/tests/flavors/src/instrumentTestF1/java/com/android/tests/flavors/MainActivityGroup1Test.java
@@ -80,17 +80,17 @@
 
     @MediumTest
     public void testResOverlay() {
-        assertEquals("F1", mResOverLay.getText());
-        assertEquals("F1", mResOverLay1.getText());
+        assertEquals("f1", mResOverLay.getText());
+        assertEquals("f1", mResOverLay1.getText());
     }
 
     @MediumTest
     public void testBuildConfig() {
-        assertEquals("F1", mBuildConfig1.getText());
+        assertEquals("f1", mBuildConfig1.getText());
     }
 
     @MediumTest
     public void testCodeOverlay() {
-        assertEquals("F1", mCodeOverlay1.getText());
+        assertEquals("f1", mCodeOverlay1.getText());
     }
 }
diff --git a/tests/flavors/src/instrumentTestF2/java/com/android/tests/flavors/MainActivityGroup1Test.java b/tests/flavors/src/instrumentTestF2/java/com/android/tests/flavors/MainActivityGroup1Test.java
index 41e9ecf..8ecd057 100644
--- a/tests/flavors/src/instrumentTestF2/java/com/android/tests/flavors/MainActivityGroup1Test.java
+++ b/tests/flavors/src/instrumentTestF2/java/com/android/tests/flavors/MainActivityGroup1Test.java
@@ -80,17 +80,17 @@
 
     @MediumTest
     public void testResOverlay() {
-        assertEquals("F2", mResOverLay.getText());
-        assertEquals("F2", mResOverLay1.getText());
+        assertEquals("f2", mResOverLay.getText());
+        assertEquals("f2", mResOverLay1.getText());
     }
 
     @MediumTest
     public void testBuildConfig() {
-        assertEquals("F2", mBuildConfig1.getText());
+        assertEquals("f2", mBuildConfig1.getText());
     }
 
     @MediumTest
     public void testCodeOverlay() {
-        assertEquals("F2", mCodeOverlay1.getText());
+        assertEquals("f2", mCodeOverlay1.getText());
     }
 }
diff --git a/tests/flavors/src/instrumentTestFa/java/com/android/tests/flavors/MainActivityGroup2Test.java b/tests/flavors/src/instrumentTestFa/java/com/android/tests/flavors/MainActivityGroup2Test.java
index 0d0fee2..3e51225 100644
--- a/tests/flavors/src/instrumentTestFa/java/com/android/tests/flavors/MainActivityGroup2Test.java
+++ b/tests/flavors/src/instrumentTestFa/java/com/android/tests/flavors/MainActivityGroup2Test.java
@@ -82,17 +82,17 @@
     public void testResOverlay() {
         // because this group has lower priority, we check that the resource from
         // this flavor is not used.
-        assertFalse("FA".equals(mResOverLay.getText()));
-        assertEquals("FA", mResOverLay2.getText());
+        assertFalse("fa".equals(mResOverLay.getText()));
+        assertEquals("fa", mResOverLay2.getText());
     }
 
     @MediumTest
     public void testBuildConfig() {
-        assertEquals("FA", mBuildConfig2.getText());
+        assertEquals("fa", mBuildConfig2.getText());
     }
 
     @MediumTest
     public void testCodeOverlay() {
-        assertEquals("FA", mCodeOverlay2.getText());
+        assertEquals("fa", mCodeOverlay2.getText());
     }
 }
diff --git a/tests/flavors/src/instrumentTestFb/java/com/android/tests/flavors/MainActivityGroup2Test.java b/tests/flavors/src/instrumentTestFb/java/com/android/tests/flavors/MainActivityGroup2Test.java
index 6f92adf..f11b5ce 100644
--- a/tests/flavors/src/instrumentTestFb/java/com/android/tests/flavors/MainActivityGroup2Test.java
+++ b/tests/flavors/src/instrumentTestFb/java/com/android/tests/flavors/MainActivityGroup2Test.java
@@ -82,17 +82,17 @@
     public void testResOverlay() {
         // because this group has lower priority, we check that the resource from
         // this flavor is not used.
-        assertFalse("FB".equals(mResOverLay.getText()));
-        assertEquals("FB", mResOverLay2.getText());
+        assertFalse("fb".equals(mResOverLay.getText()));
+        assertEquals("fb", mResOverLay2.getText());
     }
 
     @MediumTest
     public void testBuildConfig() {
-        assertEquals("FB", mBuildConfig2.getText());
+        assertEquals("fb", mBuildConfig2.getText());
     }
 
     @MediumTest
     public void testCodeOverlay() {
-        assertEquals("FB", mCodeOverlay2.getText());
+        assertEquals("fb", mCodeOverlay2.getText());
     }
 }
diff --git a/tests/flavors/src/main/java/com/android/tests/flavors/MainActivity.java b/tests/flavors/src/main/java/com/android/tests/flavors/MainActivity.java
index 6968643..ff2b47c 100644
--- a/tests/flavors/src/main/java/com/android/tests/flavors/MainActivity.java
+++ b/tests/flavors/src/main/java/com/android/tests/flavors/MainActivity.java
@@ -14,10 +14,10 @@
         TextView tv;
 
         tv = (TextView) findViewById(R.id.buildconfig1);
-        tv.setText(BuildConfig.GROUP1);
+        tv.setText(BuildConfig.FLAVOR_group1);
 
         tv = (TextView) findViewById(R.id.buildconfig2);
-        tv.setText(BuildConfig.GROUP2);
+        tv.setText(BuildConfig.FLAVOR_group2);
 
         tv = (TextView) findViewById(R.id.codeoverlay1);
         tv.setText(com.android.tests.flavors.group1.SomeClass.getString());
diff --git a/tests/genFolderApi/build.gradle b/tests/genFolderApi/build.gradle
new file mode 100644
index 0000000..3f35366
--- /dev/null
+++ b/tests/genFolderApi/build.gradle
@@ -0,0 +1,45 @@
+buildscript {
+    repositories {
+        maven { url '../../../../out/host/gradle/repo' }
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
+    }
+}
+apply plugin: 'android'
+
+android {
+    compileSdkVersion 15
+    buildToolsVersion "18.0.1"
+}
+
+
+public class GenerateCode extends DefaultTask {
+    @Input
+    String value
+
+    @OutputFile
+    File outputFile
+
+    @TaskAction
+    void taskAction() {
+        getOutputFile().text =
+            "package com.custom;\n" +
+            "public class Foo {\n" +
+            "    public static String getBuildDate() { return \"${getValue()}\"; }\n" +
+            "}\n";
+    }
+}
+
+
+android.applicationVariants.all { variant ->
+
+    // create a task that generates a java class
+    File sourceFolder = file("${buildDir}/customCode/${variant.dirName}")
+    def javaGenerationTask = tasks.create(name: "generatedCodeFor${variant.name.capitalize()}", type: GenerateCode) {
+        value new Date().format("yyyy-MM-dd'T'HH:mm'Z'", TimeZone.getTimeZone("UTC"))
+        outputFile file("${sourceFolder.absolutePath}/com/custom/Foo.java")
+    }
+
+    variant.registerJavaGeneratingTask(javaGenerationTask, sourceFolder)
+}
\ No newline at end of file
diff --git a/tests/genFolderApi/src/main/AndroidManifest.xml b/tests/genFolderApi/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..a34d937
--- /dev/null
+++ b/tests/genFolderApi/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="com.android.tests.basic">
+    <application android:label="@string/app_name" android:icon="@drawable/icon">
+        <activity android:name=".Main"
+                  android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/genFolderApi/src/main/java/com/android/tests/basic/Main.java b/tests/genFolderApi/src/main/java/com/android/tests/basic/Main.java
new file mode 100644
index 0000000..df05828
--- /dev/null
+++ b/tests/genFolderApi/src/main/java/com/android/tests/basic/Main.java
@@ -0,0 +1,19 @@
+package com.android.tests.basic;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+public class Main extends Activity
+{
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+
+        TextView tv = (TextView) findViewById(R.id.text);
+        tv.setText(com.custom.Foo.getBuildDate());
+    }
+}
diff --git a/tests/genFolderApi/src/main/res/drawable/icon.png b/tests/genFolderApi/src/main/res/drawable/icon.png
new file mode 100644
index 0000000..a07c69f
--- /dev/null
+++ b/tests/genFolderApi/src/main/res/drawable/icon.png
Binary files differ
diff --git a/tests/genFolderApi/src/main/res/layout/main.xml b/tests/genFolderApi/src/main/res/layout/main.xml
new file mode 100644
index 0000000..ee817cf
--- /dev/null
+++ b/tests/genFolderApi/src/main/res/layout/main.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    >
+<TextView
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:text="Test App - GenFolderApi"
+    android:id="@+id/text"
+    />
+</LinearLayout>
+
diff --git a/tests/genFolderApi/src/main/res/values/strings.xml b/tests/genFolderApi/src/main/res/values/strings.xml
new file mode 100644
index 0000000..64ee88e
--- /dev/null
+++ b/tests/genFolderApi/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">_Test-genFolderApi</string>
+</resources>
diff --git a/tests/libProguard/build.gradle b/tests/libProguard/build.gradle
new file mode 100644
index 0000000..9df86e6
--- /dev/null
+++ b/tests/libProguard/build.gradle
@@ -0,0 +1,26 @@
+buildscript {
+    repositories {
+        maven { url '../../../../out/host/gradle/repo' }
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
+    }
+}
+
+apply plugin: 'android-library'
+
+android {
+    compileSdkVersion 15
+    buildToolsVersion "18.0.1"
+
+    defaultConfig {
+        versionCode 12
+        versionName "2.0"
+        minSdkVersion 16
+        targetSdkVersion 16
+        proguardFile 'config.pro'
+    }
+    release {
+        runProguard true
+    }
+}
diff --git a/tests/libProguard/config.pro b/tests/libProguard/config.pro
new file mode 100644
index 0000000..3664f87
--- /dev/null
+++ b/tests/libProguard/config.pro
@@ -0,0 +1,3 @@
+-keep public class com.android.tests.basic.StringProvider {
+    public static java.lang.String getString(int);
+}
diff --git a/tests/libProguard/src/main/AndroidManifest.xml b/tests/libProguard/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..593a287
--- /dev/null
+++ b/tests/libProguard/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="com.android.tests.basic">
+</manifest>
diff --git a/tests/libProguard/src/main/java/com/android/tests/basic/StringProvider.java b/tests/libProguard/src/main/java/com/android/tests/basic/StringProvider.java
new file mode 100644
index 0000000..18fe927
--- /dev/null
+++ b/tests/libProguard/src/main/java/com/android/tests/basic/StringProvider.java
@@ -0,0 +1,11 @@
+package com.android.tests.basic;
+
+import java.lang.Integer;
+
+public class StringProvider {
+    private static int proguardInt = 5;
+
+    public static String getString(int foo) {
+        return Integer.toString(foo + proguardInt);
+    }
+}
diff --git a/tests/libProguardConsumerFiles/A.txt b/tests/libProguardConsumerFiles/A.txt
new file mode 100644
index 0000000..f70f10e
--- /dev/null
+++ b/tests/libProguardConsumerFiles/A.txt
@@ -0,0 +1 @@
+A
diff --git a/tests/libProguardConsumerFiles/B.txt b/tests/libProguardConsumerFiles/B.txt
new file mode 100644
index 0000000..223b783
--- /dev/null
+++ b/tests/libProguardConsumerFiles/B.txt
@@ -0,0 +1 @@
+B
diff --git a/tests/libProguardConsumerFiles/C.txt b/tests/libProguardConsumerFiles/C.txt
new file mode 100644
index 0000000..3cc58df
--- /dev/null
+++ b/tests/libProguardConsumerFiles/C.txt
@@ -0,0 +1 @@
+C
diff --git a/tests/libProguardConsumerFiles/build.gradle b/tests/libProguardConsumerFiles/build.gradle
new file mode 100644
index 0000000..55453e0
--- /dev/null
+++ b/tests/libProguardConsumerFiles/build.gradle
@@ -0,0 +1,32 @@
+buildscript {
+    repositories {
+        maven { url '../../../../out/host/gradle/repo' }
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
+    }
+}
+
+apply plugin: 'android-library'
+
+android {
+    compileSdkVersion 15
+    buildToolsVersion "18.0.1"
+
+    defaultConfig {
+        versionCode 12
+        versionName "2.0"
+        minSdkVersion 16
+        targetSdkVersion 16
+        proguardFile 'config.pro'
+        consumerProguardFiles 'A.txt'
+    }
+
+    debug {
+    }
+
+    release {
+        runProguard true
+        consumerProguardFiles 'B.txt', 'C.txt'
+    }
+}
diff --git a/tests/libProguardConsumerFiles/config.pro b/tests/libProguardConsumerFiles/config.pro
new file mode 100644
index 0000000..3664f87
--- /dev/null
+++ b/tests/libProguardConsumerFiles/config.pro
@@ -0,0 +1,3 @@
+-keep public class com.android.tests.basic.StringProvider {
+    public static java.lang.String getString(int);
+}
diff --git a/tests/libProguardConsumerFiles/src/main/AndroidManifest.xml b/tests/libProguardConsumerFiles/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..593a287
--- /dev/null
+++ b/tests/libProguardConsumerFiles/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="com.android.tests.basic">
+</manifest>
diff --git a/tests/libProguardConsumerFiles/src/main/java/com/android/tests/basic/StringProvider.java b/tests/libProguardConsumerFiles/src/main/java/com/android/tests/basic/StringProvider.java
new file mode 100644
index 0000000..18fe927
--- /dev/null
+++ b/tests/libProguardConsumerFiles/src/main/java/com/android/tests/basic/StringProvider.java
@@ -0,0 +1,11 @@
+package com.android.tests.basic;
+
+import java.lang.Integer;
+
+public class StringProvider {
+    private static int proguardInt = 5;
+
+    public static String getString(int foo) {
+        return Integer.toString(foo + proguardInt);
+    }
+}
diff --git a/tests/libProguardJarDep/app/build.gradle b/tests/libProguardJarDep/app/build.gradle
new file mode 100644
index 0000000..8166bf8
--- /dev/null
+++ b/tests/libProguardJarDep/app/build.gradle
@@ -0,0 +1,35 @@
+apply plugin: 'android'
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    compile project(':lib')
+}
+
+android {
+    compileSdkVersion 15
+    buildToolsVersion "18.0.1"
+
+    testBuildType "proguard"
+
+    defaultConfig {
+        versionCode 12
+        versionName "2.0"
+        minSdkVersion 16
+        targetSdkVersion 16
+    }
+
+    buildTypes {
+        proguard.initWith(buildTypes.debug)
+        proguard {
+            runProguard true
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'config.pro'
+        }
+    }
+
+    dexOptions {
+        incremental false
+    }
+}
diff --git a/tests/libProguardJarDep/app/config.pro b/tests/libProguardJarDep/app/config.pro
new file mode 100644
index 0000000..4321975
--- /dev/null
+++ b/tests/libProguardJarDep/app/config.pro
@@ -0,0 +1,2 @@
+-keep class com.google.**
+-dontwarn com.google.**
diff --git a/tests/libProguardJarDep/app/src/instrumentTest/java/com/android/tests/basic/MainTest.java b/tests/libProguardJarDep/app/src/instrumentTest/java/com/android/tests/basic/MainTest.java
new file mode 100644
index 0000000..6b3ff36
--- /dev/null
+++ b/tests/libProguardJarDep/app/src/instrumentTest/java/com/android/tests/basic/MainTest.java
@@ -0,0 +1,42 @@
+package com.android.tests.basic;
+
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.widget.TextView;
+
+public class MainTest extends ActivityInstrumentationTestCase2<Main> {
+
+    private TextView mTextView;
+
+    /**
+     * Creates an {@link ActivityInstrumentationTestCase2} that tests the {@link Main} activity.
+     */
+    public MainTest() {
+        super(Main.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        final Main a = getActivity();
+        // ensure a valid handle to the activity has been returned
+        assertNotNull(a);
+        mTextView = (TextView) a.findViewById(R.id.dateText);
+    }
+
+    /**
+     * The name 'test preconditions' is a convention to signal that if this
+     * test doesn't pass, the test case was not set up properly and it might
+     * explain any and all failures in other tests.  This is not guaranteed
+     * to run before other tests, as junit uses reflection to find the tests.
+     */
+    @MediumTest
+    public void testPreconditions() {
+        assertNotNull(mTextView);
+    }
+
+    public void testTextViewContent() {
+        assertEquals("FredBarney", mTextView.getText());
+    }
+}
+
diff --git a/tests/libProguardJarDep/app/src/main/AndroidManifest.xml b/tests/libProguardJarDep/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..4f8d570
--- /dev/null
+++ b/tests/libProguardJarDep/app/src/main/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="com.android.tests.basic">
+    <application android:label="@string/app_name" android:icon="@drawable/icon">
+        <activity android:name=".Main"
+                  android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+    <uses-permission android:name="com.blah" />
+
+    <permission-group android:name="foo.permission-group.COST_MONEY"
+        android:label="@string/app_name"
+        android:description="@string/app_name" />
+
+    <permission android:name="foo.permission.SEND_SMS"
+        android:permissionGroup="foo.permission-group.COST_MONEY"
+        android:label="@string/app_name"
+        android:description="@string/app_name" />
+
+    <permission android:name="foo.blah.SEND_SMS"
+        android:permissionGroup="foo.permission-group.COST_MONEY"
+        android:label="@string/app_name"
+        android:description="@string/app_name" />
+
+</manifest>
diff --git a/tests/libProguardJarDep/app/src/main/java/com/android/tests/basic/Main.java b/tests/libProguardJarDep/app/src/main/java/com/android/tests/basic/Main.java
new file mode 100644
index 0000000..f5a6953
--- /dev/null
+++ b/tests/libProguardJarDep/app/src/main/java/com/android/tests/basic/Main.java
@@ -0,0 +1,31 @@
+package com.android.tests.basic;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+import java.lang.reflect.Method;
+
+public class Main extends Activity
+{
+
+    private int foo = 1234;
+
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+
+        TextView tv = (TextView) findViewById(R.id.dateText);
+
+        try {
+            // use reflection to make sure the class wasn't obfuscated
+            Class<?> theClass = Class.forName("com.android.tests.basic.StringGetter");
+            Method method = theClass.getDeclaredMethod("getString");
+            tv.setText((String) method.invoke(null));
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/tests/libProguardJarDep/app/src/main/res/drawable/icon.png b/tests/libProguardJarDep/app/src/main/res/drawable/icon.png
new file mode 100644
index 0000000..a07c69f
--- /dev/null
+++ b/tests/libProguardJarDep/app/src/main/res/drawable/icon.png
Binary files differ
diff --git a/tests/libProguardJarDep/app/src/main/res/layout/main.xml b/tests/libProguardJarDep/app/src/main/res/layout/main.xml
new file mode 100644
index 0000000..89ab091
--- /dev/null
+++ b/tests/libProguardJarDep/app/src/main/res/layout/main.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    >
+<TextView
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:text="Test App - Basic"
+    android:id="@+id/text"
+    />
+<TextView
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:text=""
+    android:id="@+id/dateText"
+    />
+</LinearLayout>
+
diff --git a/tests/libProguardJarDep/app/src/main/res/values/strings.xml b/tests/libProguardJarDep/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..60ea2d0
--- /dev/null
+++ b/tests/libProguardJarDep/app/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">_Test-Basic</string>
+</resources>
diff --git a/tests/libProguardJarDep/build.gradle b/tests/libProguardJarDep/build.gradle
new file mode 100644
index 0000000..b899f9a
--- /dev/null
+++ b/tests/libProguardJarDep/build.gradle
@@ -0,0 +1,8 @@
+buildscript {
+    repositories {
+        maven { url '../../../../out/host/gradle/repo' }
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
+    }
+}
diff --git a/tests/libProguardJarDep/lib/build.gradle b/tests/libProguardJarDep/lib/build.gradle
new file mode 100644
index 0000000..40cb840
--- /dev/null
+++ b/tests/libProguardJarDep/lib/build.gradle
@@ -0,0 +1,30 @@
+apply plugin: 'android-library'
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    compile 'com.google.guava:guava:11.0.2'
+    compile fileTree(dir: 'libs', include: '*.jar')
+}
+
+android {
+    compileSdkVersion 15
+    buildToolsVersion "18.0.1"
+
+    defaultConfig {
+        versionCode 12
+        versionName "2.0"
+        minSdkVersion 16
+        targetSdkVersion 16
+        proguardFile 'config.pro'
+        consumerProguardFiles 'config.pro'
+    }
+    debug {
+        runProguard true
+    }
+    release {
+        runProguard true
+    }
+}
diff --git a/tests/libProguardJarDep/lib/config.pro b/tests/libProguardJarDep/lib/config.pro
new file mode 100644
index 0000000..c3fd7bd
--- /dev/null
+++ b/tests/libProguardJarDep/lib/config.pro
@@ -0,0 +1,3 @@
+-keep public class com.android.tests.basic.StringGetter {
+    public static java.lang.String getString();
+}
diff --git a/tests/libProguardJarDep/lib/libs/util-1.0.jar b/tests/libProguardJarDep/lib/libs/util-1.0.jar
new file mode 100644
index 0000000..55471dc
--- /dev/null
+++ b/tests/libProguardJarDep/lib/libs/util-1.0.jar
Binary files differ
diff --git a/tests/libProguardJarDep/lib/src/main/AndroidManifest.xml b/tests/libProguardJarDep/lib/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..950a35a
--- /dev/null
+++ b/tests/libProguardJarDep/lib/src/main/AndroidManifest.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="com.android.tests.basic">
+    <application />
+</manifest>
diff --git a/tests/libProguardJarDep/lib/src/main/java/com/android/tests/basic/StringGetter.java b/tests/libProguardJarDep/lib/src/main/java/com/android/tests/basic/StringGetter.java
new file mode 100644
index 0000000..e9e5e76
--- /dev/null
+++ b/tests/libProguardJarDep/lib/src/main/java/com/android/tests/basic/StringGetter.java
@@ -0,0 +1,23 @@
+package com.android.tests.basic;
+
+import java.lang.String;
+import java.lang.StringBuffer;
+import com.example.android.multiproject.person.People;
+import com.example.android.multiproject.person.Person;
+
+public class StringGetter{
+
+    public static String getString() {
+         return getStringInternal();
+    }
+
+    private static String getStringInternal() {
+        StringBuffer sb = new StringBuffer();
+
+        Iterable<Person> people = new People();
+        for (Person person : people) {
+            sb.append(person.getName());
+        }
+        return sb.toString();
+    }
+}
diff --git a/tests/libProguardJarDep/settings.gradle b/tests/libProguardJarDep/settings.gradle
new file mode 100644
index 0000000..eedb2a1
--- /dev/null
+++ b/tests/libProguardJarDep/settings.gradle
@@ -0,0 +1,2 @@
+include 'app'
+include 'lib'
diff --git a/tests/libProguardJarDep/util/build.gradle b/tests/libProguardJarDep/util/build.gradle
new file mode 100644
index 0000000..dff7725
--- /dev/null
+++ b/tests/libProguardJarDep/util/build.gradle
@@ -0,0 +1,8 @@
+apply plugin: 'java'
+
+dependencies {
+    compile 'com.google.guava:guava:11.0.2'
+}
+
+sourceCompatibility = "1.6"
+targetCompatibility = "1.6"
diff --git a/tests/libProguardJarDep/util/src/main/java/com/example/android/multiproject/person/People.java b/tests/libProguardJarDep/util/src/main/java/com/example/android/multiproject/person/People.java
new file mode 100644
index 0000000..8b99248
--- /dev/null
+++ b/tests/libProguardJarDep/util/src/main/java/com/example/android/multiproject/person/People.java
@@ -0,0 +1,10 @@
+package com.example.android.multiproject.person;
+
+import java.util.Iterator;
+import com.google.common.collect.Lists;
+
+public class People implements Iterable<Person> {
+    public Iterator<Person> iterator() {
+        return Lists.newArrayList(new Person("Fred"), new Person("Barney")).iterator();
+    }
+}
diff --git a/tests/libProguardJarDep/util/src/main/java/com/example/android/multiproject/person/Person.java b/tests/libProguardJarDep/util/src/main/java/com/example/android/multiproject/person/Person.java
new file mode 100644
index 0000000..2f4aa9f
--- /dev/null
+++ b/tests/libProguardJarDep/util/src/main/java/com/example/android/multiproject/person/Person.java
@@ -0,0 +1,13 @@
+package com.example.android.multiproject.person;
+
+public class Person {
+    private final String name;
+
+    public Person(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+}
diff --git a/tests/libProguardLibDep/app/build.gradle b/tests/libProguardLibDep/app/build.gradle
new file mode 100644
index 0000000..c52309a
--- /dev/null
+++ b/tests/libProguardLibDep/app/build.gradle
@@ -0,0 +1,31 @@
+apply plugin: 'android'
+
+dependencies {
+    compile project(':lib')
+}
+
+android {
+    compileSdkVersion 15
+    buildToolsVersion "18.0.1"
+
+    testBuildType "proguard"
+
+    defaultConfig {
+        versionCode 12
+        versionName "2.0"
+        minSdkVersion 16
+        targetSdkVersion 16
+    }
+
+    buildTypes {
+        proguard.initWith(buildTypes.debug)
+        proguard {
+            runProguard true
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'config.pro'
+        }
+    }
+
+    dexOptions {
+        incremental false
+    }
+}
diff --git a/tests/libProguardLibDep/app/config.pro b/tests/libProguardLibDep/app/config.pro
new file mode 100644
index 0000000..e4aadb9
--- /dev/null
+++ b/tests/libProguardLibDep/app/config.pro
@@ -0,0 +1,3 @@
+-keep public class com.android.tests.basic.Main {
+    public void getObfuscatedMethod();
+}
diff --git a/tests/libProguardLibDep/app/src/instrumentTest/java/com/android/tests/basic/MainTest.java b/tests/libProguardLibDep/app/src/instrumentTest/java/com/android/tests/basic/MainTest.java
new file mode 100644
index 0000000..f7289d1
--- /dev/null
+++ b/tests/libProguardLibDep/app/src/instrumentTest/java/com/android/tests/basic/MainTest.java
@@ -0,0 +1,54 @@
+package com.android.tests.basic;
+
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.widget.TextView;
+
+import java.lang.NoSuchMethodException;
+
+public class MainTest extends ActivityInstrumentationTestCase2<Main> {
+
+    private TextView mTextView;
+
+    /**
+     * Creates an {@link ActivityInstrumentationTestCase2} that tests the {@link Main} activity.
+     */
+    public MainTest() {
+        super(Main.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        final Main a = getActivity();
+        // ensure a valid handle to the activity has been returned
+        assertNotNull(a);
+        mTextView = (TextView) a.findViewById(R.id.dateText);
+    }
+
+    /**
+     * The name 'test preconditions' is a convention to signal that if this
+     * test doesn't pass, the test case was not set up properly and it might
+     * explain any and all failures in other tests.  This is not guaranteed
+     * to run before other tests, as junit uses reflection to find the tests.
+     */
+    @MediumTest
+    public void testPreconditions() {
+        assertNotNull(mTextView);
+    }
+
+    public void testTextViewContent() {
+        assertEquals("1234", mTextView.getText());
+    }
+
+    public void testConsumerProguardRules() {
+        try {
+            final Main a = getActivity();
+            a.getObfuscatedMethod();
+            fail("Excepted NoSuchMethodError");
+        } catch (NoSuchMethodException e) {
+            // test passed
+        }
+    }
+}
+
diff --git a/tests/libProguardLibDep/app/src/main/AndroidManifest.xml b/tests/libProguardLibDep/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..4f8d570
--- /dev/null
+++ b/tests/libProguardLibDep/app/src/main/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="com.android.tests.basic">
+    <application android:label="@string/app_name" android:icon="@drawable/icon">
+        <activity android:name=".Main"
+                  android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+    <uses-permission android:name="com.blah" />
+
+    <permission-group android:name="foo.permission-group.COST_MONEY"
+        android:label="@string/app_name"
+        android:description="@string/app_name" />
+
+    <permission android:name="foo.permission.SEND_SMS"
+        android:permissionGroup="foo.permission-group.COST_MONEY"
+        android:label="@string/app_name"
+        android:description="@string/app_name" />
+
+    <permission android:name="foo.blah.SEND_SMS"
+        android:permissionGroup="foo.permission-group.COST_MONEY"
+        android:label="@string/app_name"
+        android:description="@string/app_name" />
+
+</manifest>
diff --git a/tests/libProguardLibDep/app/src/main/java/com/android/tests/basic/Main.java b/tests/libProguardLibDep/app/src/main/java/com/android/tests/basic/Main.java
new file mode 100644
index 0000000..8faebcd
--- /dev/null
+++ b/tests/libProguardLibDep/app/src/main/java/com/android/tests/basic/Main.java
@@ -0,0 +1,46 @@
+package com.android.tests.basic;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+import java.lang.ClassNotFoundException;
+import java.lang.NoSuchMethodException;
+import java.lang.reflect.Method;
+
+public class Main extends Activity
+{
+
+    private int foo = 1234;
+
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+
+        TextView tv = (TextView) findViewById(R.id.dateText);
+
+        try {
+            // use reflection to make sure the class wasn't obfuscated
+            Class<?> theClass = Class.forName("com.android.tests.basic.StringGetter");
+            Method method = theClass.getDeclaredMethod("getString", int.class);
+            tv.setText((String) method.invoke(null, foo));
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * use reflection to get a method that should be obfuscated
+     */
+    public void getObfuscatedMethod() throws NoSuchMethodException{
+        try {
+            Class<?> theClass = Class.forName("com.android.tests.basic.StringGetter");
+            Method method = theClass.getDeclaredMethod("getStringInternal", int.class);
+        } catch (ClassNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/tests/libProguardLibDep/app/src/main/res/drawable/icon.png b/tests/libProguardLibDep/app/src/main/res/drawable/icon.png
new file mode 100644
index 0000000..a07c69f
--- /dev/null
+++ b/tests/libProguardLibDep/app/src/main/res/drawable/icon.png
Binary files differ
diff --git a/tests/libProguardLibDep/app/src/main/res/layout/main.xml b/tests/libProguardLibDep/app/src/main/res/layout/main.xml
new file mode 100644
index 0000000..89ab091
--- /dev/null
+++ b/tests/libProguardLibDep/app/src/main/res/layout/main.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    >
+<TextView
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:text="Test App - Basic"
+    android:id="@+id/text"
+    />
+<TextView
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:text=""
+    android:id="@+id/dateText"
+    />
+</LinearLayout>
+
diff --git a/tests/libProguardLibDep/app/src/main/res/values/strings.xml b/tests/libProguardLibDep/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..60ea2d0
--- /dev/null
+++ b/tests/libProguardLibDep/app/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">_Test-Basic</string>
+</resources>
diff --git a/tests/libProguardLibDep/build.gradle b/tests/libProguardLibDep/build.gradle
new file mode 100644
index 0000000..b899f9a
--- /dev/null
+++ b/tests/libProguardLibDep/build.gradle
@@ -0,0 +1,8 @@
+buildscript {
+    repositories {
+        maven { url '../../../../out/host/gradle/repo' }
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
+    }
+}
diff --git a/tests/libProguardLibDep/lib/build.gradle b/tests/libProguardLibDep/lib/build.gradle
new file mode 100644
index 0000000..ce3ccf6
--- /dev/null
+++ b/tests/libProguardLibDep/lib/build.gradle
@@ -0,0 +1,22 @@
+apply plugin: 'android-library'
+
+dependencies {
+    compile project(':lib2')
+}
+
+android {
+    compileSdkVersion 15
+    buildToolsVersion "18.0.1"
+
+    defaultConfig {
+        versionCode 12
+        versionName "2.0"
+        minSdkVersion 16
+        targetSdkVersion 16
+        proguardFile 'config.pro'
+        consumerProguardFiles 'consumerRules.pro'
+    }
+    release {
+        runProguard true
+    }
+}
diff --git a/tests/libProguardLibDep/lib/config.pro b/tests/libProguardLibDep/lib/config.pro
new file mode 100644
index 0000000..4cf7cba
--- /dev/null
+++ b/tests/libProguardLibDep/lib/config.pro
@@ -0,0 +1,3 @@
+-keep public class com.android.tests.basic.StringGetter {
+    public static java.lang.String getString(int);
+}
diff --git a/tests/libProguardLibDep/lib/consumerRules.pro b/tests/libProguardLibDep/lib/consumerRules.pro
new file mode 100644
index 0000000..d09be16
--- /dev/null
+++ b/tests/libProguardLibDep/lib/consumerRules.pro
@@ -0,0 +1,6 @@
+-keep public class com.android.tests.basic.StringGetter {
+    public static java.lang.String getString(int);
+}
+-keep public class com.android.tests.basic.StringGetter {
+    public static java.lang.String getStringInternal(int);
+}
diff --git a/tests/libProguardLibDep/lib/src/main/AndroidManifest.xml b/tests/libProguardLibDep/lib/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..950a35a
--- /dev/null
+++ b/tests/libProguardLibDep/lib/src/main/AndroidManifest.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="com.android.tests.basic">
+    <application />
+</manifest>
diff --git a/tests/libProguardLibDep/lib/src/main/java/com/android/tests/basic/StringGetter.java b/tests/libProguardLibDep/lib/src/main/java/com/android/tests/basic/StringGetter.java
new file mode 100644
index 0000000..159f359
--- /dev/null
+++ b/tests/libProguardLibDep/lib/src/main/java/com/android/tests/basic/StringGetter.java
@@ -0,0 +1,23 @@
+package com.android.tests.basic;
+
+import java.lang.RuntimeException;
+import java.lang.String;
+import java.lang.reflect.Method;
+
+public class StringGetter{
+
+    public static String getString(int foo) {
+         return getStringInternal(foo);
+    }
+
+    public static String getStringInternal(int foo) {
+        try {
+            // use reflection to make sure the class wasn't obfuscated
+            Class<?> theClass = Class.forName("com.android.tests.basic.StringProvider");
+            Method method = theClass.getDeclaredMethod("getString", int.class);
+            return (String) method.invoke(null, foo);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/tests/libProguardLibDep/lib2/build.gradle b/tests/libProguardLibDep/lib2/build.gradle
new file mode 100644
index 0000000..78da4d4
--- /dev/null
+++ b/tests/libProguardLibDep/lib2/build.gradle
@@ -0,0 +1,18 @@
+apply plugin: 'android-library'
+
+android {
+    compileSdkVersion 15
+    buildToolsVersion "18.0.1"
+
+    defaultConfig {
+        versionCode 12
+        versionName "2.0"
+        minSdkVersion 16
+        targetSdkVersion 16
+        proguardFile 'config.pro'
+	consumerProguardFiles 'config.pro'
+    }
+    release {
+	runProguard true
+    }
+}
diff --git a/tests/libProguardLibDep/lib2/config.pro b/tests/libProguardLibDep/lib2/config.pro
new file mode 100644
index 0000000..3664f87
--- /dev/null
+++ b/tests/libProguardLibDep/lib2/config.pro
@@ -0,0 +1,3 @@
+-keep public class com.android.tests.basic.StringProvider {
+    public static java.lang.String getString(int);
+}
diff --git a/tests/libProguardLibDep/lib2/src/main/AndroidManifest.xml b/tests/libProguardLibDep/lib2/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..593a287
--- /dev/null
+++ b/tests/libProguardLibDep/lib2/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="com.android.tests.basic">
+</manifest>
diff --git a/tests/libProguardLibDep/lib2/src/main/java/com/android/tests/basic/StringProvider.java b/tests/libProguardLibDep/lib2/src/main/java/com/android/tests/basic/StringProvider.java
new file mode 100644
index 0000000..6d81901
--- /dev/null
+++ b/tests/libProguardLibDep/lib2/src/main/java/com/android/tests/basic/StringProvider.java
@@ -0,0 +1,8 @@
+package com.android.tests.basic;
+
+public class StringProvider {
+
+    public static String getString(int foo) {
+        return Integer.toString(foo);
+    }
+}
diff --git a/tests/libProguardLibDep/settings.gradle b/tests/libProguardLibDep/settings.gradle
new file mode 100644
index 0000000..d8ac2dc
--- /dev/null
+++ b/tests/libProguardLibDep/settings.gradle
@@ -0,0 +1,3 @@
+include 'app'
+include 'lib'
+include 'lib2'
diff --git a/tests/libTestDep/build.gradle b/tests/libTestDep/build.gradle
index 0be14ae..e63fc62 100644
--- a/tests/libTestDep/build.gradle
+++ b/tests/libTestDep/build.gradle
@@ -3,7 +3,7 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
 
diff --git a/tests/libsTest/build.gradle b/tests/libsTest/build.gradle
index f210fcf..ef3d0aa 100644
--- a/tests/libsTest/build.gradle
+++ b/tests/libsTest/build.gradle
@@ -3,7 +3,7 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
 
diff --git a/tests/localJars/build.gradle b/tests/localJars/build.gradle
index 6c540c4..d887778 100644
--- a/tests/localJars/build.gradle
+++ b/tests/localJars/build.gradle
@@ -3,7 +3,7 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
 
diff --git a/tests/migrated/build.gradle b/tests/migrated/build.gradle
index 37efffa..e43334e 100644
--- a/tests/migrated/build.gradle
+++ b/tests/migrated/build.gradle
@@ -3,7 +3,7 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
 apply plugin: 'android'
diff --git a/tests/multiproject/baseLibrary/src/main/java/com/sample/android/multiproject/library/PersonView.java b/tests/multiproject/baseLibrary/src/main/java/com/sample/android/multiproject/library/PersonView.java
index b218532..9d3b996 100644
--- a/tests/multiproject/baseLibrary/src/main/java/com/sample/android/multiproject/library/PersonView.java
+++ b/tests/multiproject/baseLibrary/src/main/java/com/sample/android/multiproject/library/PersonView.java
@@ -1,10 +1,10 @@
-package com.example.android.multiproject.library;
+package com.sample.android.multiproject.library;
 
 import android.widget.TextView;
 import android.content.Context;
 import com.example.android.multiproject.person.Person;
 
-class PersonView extends TextView {
+public class PersonView extends TextView {
     public PersonView(Context context, Person person) {
         super(context);
         setTextSize(20);
diff --git a/tests/multiproject/build.gradle b/tests/multiproject/build.gradle
index 6c540c4..d887778 100644
--- a/tests/multiproject/build.gradle
+++ b/tests/multiproject/build.gradle
@@ -3,7 +3,7 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
 
diff --git a/tests/multiproject/library/src/main/java/com/example/android/multiproject/library/ShowPeopleActivity.java b/tests/multiproject/library/src/main/java/com/example/android/multiproject/library/ShowPeopleActivity.java
index a3f2195..b0f8b12 100644
--- a/tests/multiproject/library/src/main/java/com/example/android/multiproject/library/ShowPeopleActivity.java
+++ b/tests/multiproject/library/src/main/java/com/example/android/multiproject/library/ShowPeopleActivity.java
@@ -11,6 +11,7 @@
 
 import com.example.android.multiproject.person.Person;
 import com.example.android.multiproject.person.People;
+import com.sample.android.multiproject.library.PersonView;
 
 public class ShowPeopleActivity extends Activity {
     @Override
diff --git a/tests/multires/build.gradle b/tests/multires/build.gradle
index 290e765..5043daf 100644
--- a/tests/multires/build.gradle
+++ b/tests/multires/build.gradle
@@ -3,7 +3,7 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
 apply plugin: 'android'
diff --git a/tests/ndkJniLib/app/build.gradle b/tests/ndkJniLib/app/build.gradle
new file mode 100644
index 0000000..0c26b2a
--- /dev/null
+++ b/tests/ndkJniLib/app/build.gradle
@@ -0,0 +1,28 @@
+apply plugin: 'android'
+
+dependencies {
+    compile project(':lib')
+}
+
+android {
+    compileSdkVersion 15
+    buildToolsVersion "18.0.1"
+
+    productFlavors {
+        x86 {
+            ndk {
+                abiFilter "x86"
+            }
+        }
+        arm {
+            ndk {
+                abiFilter "armeabi-v7a"
+            }
+        }
+        mips {
+            ndk {
+                abiFilter "mips"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/ndkJniLib/app/src/instrumentTest/java/com/example/hellojni/lib/HelloJniTest.java b/tests/ndkJniLib/app/src/instrumentTest/java/com/example/hellojni/lib/HelloJniTest.java
new file mode 100644
index 0000000..feadc72
--- /dev/null
+++ b/tests/ndkJniLib/app/src/instrumentTest/java/com/example/hellojni/lib/HelloJniTest.java
@@ -0,0 +1,29 @@
+package com.example.hellojni.lib;
+
+import android.test.ActivityInstrumentationTestCase;
+
+/**
+ * This is a simple framework for a test of an Application.  See
+ * {@link android.test.ApplicationTestCase ApplicationTestCase} for more information on
+ * how to write and extend Application tests.
+ * <p/>
+ * To run this test, you can type:
+ * adb shell am instrument -w \
+ * -e class com.example.hellojni.HelloJniTest \
+ * com.example.hellojni.tests/android.test.InstrumentationTestRunner
+ */
+public class HelloJniTest extends ActivityInstrumentationTestCase<HelloJni> {
+
+    public HelloJniTest() {
+        super("com.example.hellojni", HelloJni.class);
+    }
+
+
+    public void testJniName() {
+        final HelloJni a = getActivity();
+        // ensure a valid handle to the activity has been returned
+        assertNotNull(a);
+
+        assertFalse("unknown".equals(a.jniNameFromJNI()));
+    }
+}
diff --git a/tests/ndkJniLib/app/src/main/AndroidManifest.xml b/tests/ndkJniLib/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..aae7f79
--- /dev/null
+++ b/tests/ndkJniLib/app/src/main/AndroidManifest.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="com.example.hellojni.app"
+      android:versionCode="1"
+      android:versionName="1.0">
+
+    <uses-sdk android:minSdkVersion="3" />
+
+    <application android:label="@string/app_name">
+    </application>
+</manifest> 
diff --git a/tests/ndkJniLib/app/src/main/res/values/strings.xml b/tests/ndkJniLib/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..c526073
--- /dev/null
+++ b/tests/ndkJniLib/app/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">HelloJni</string>
+</resources>
diff --git a/tests/ndkJniLib/build.gradle b/tests/ndkJniLib/build.gradle
new file mode 100644
index 0000000..b899f9a
--- /dev/null
+++ b/tests/ndkJniLib/build.gradle
@@ -0,0 +1,8 @@
+buildscript {
+    repositories {
+        maven { url '../../../../out/host/gradle/repo' }
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
+    }
+}
diff --git a/tests/ndkJniLib/lib/build.gradle b/tests/ndkJniLib/lib/build.gradle
new file mode 100644
index 0000000..5d18344
--- /dev/null
+++ b/tests/ndkJniLib/lib/build.gradle
@@ -0,0 +1,12 @@
+apply plugin: 'android-library'
+
+android {
+    compileSdkVersion 15
+    buildToolsVersion "18.0.1"
+
+    defaultConfig {
+        ndk {
+            moduleName "hello-jni"
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/ndkJniLib/lib/src/instrumentTest/java/com/example/hellojni/lib/HelloJniTest.java b/tests/ndkJniLib/lib/src/instrumentTest/java/com/example/hellojni/lib/HelloJniTest.java
new file mode 100644
index 0000000..feadc72
--- /dev/null
+++ b/tests/ndkJniLib/lib/src/instrumentTest/java/com/example/hellojni/lib/HelloJniTest.java
@@ -0,0 +1,29 @@
+package com.example.hellojni.lib;
+
+import android.test.ActivityInstrumentationTestCase;
+
+/**
+ * This is a simple framework for a test of an Application.  See
+ * {@link android.test.ApplicationTestCase ApplicationTestCase} for more information on
+ * how to write and extend Application tests.
+ * <p/>
+ * To run this test, you can type:
+ * adb shell am instrument -w \
+ * -e class com.example.hellojni.HelloJniTest \
+ * com.example.hellojni.tests/android.test.InstrumentationTestRunner
+ */
+public class HelloJniTest extends ActivityInstrumentationTestCase<HelloJni> {
+
+    public HelloJniTest() {
+        super("com.example.hellojni", HelloJni.class);
+    }
+
+
+    public void testJniName() {
+        final HelloJni a = getActivity();
+        // ensure a valid handle to the activity has been returned
+        assertNotNull(a);
+
+        assertFalse("unknown".equals(a.jniNameFromJNI()));
+    }
+}
diff --git a/tests/ndkJniLib/lib/src/main/AndroidManifest.xml b/tests/ndkJniLib/lib/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..ff4f566
--- /dev/null
+++ b/tests/ndkJniLib/lib/src/main/AndroidManifest.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="com.example.hellojni.lib">
+
+    <uses-sdk android:minSdkVersion="3" />
+    <application>
+        <activity android:name=".HelloJni"
+                  android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest> 
diff --git a/tests/ndkJniLib/lib/src/main/java/com/example/hellojni/lib/HelloJni.java b/tests/ndkJniLib/lib/src/main/java/com/example/hellojni/lib/HelloJni.java
new file mode 100644
index 0000000..c97a0eb
--- /dev/null
+++ b/tests/ndkJniLib/lib/src/main/java/com/example/hellojni/lib/HelloJni.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2009 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.example.hellojni.lib;
+
+import android.app.Activity;
+import android.widget.TextView;
+import android.os.Bundle;
+
+
+public class HelloJni extends Activity {
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        /* Create a TextView and set its content.
+         * the text is retrieved by calling a native
+         * function.
+         */
+        TextView  tv = new TextView(this);
+        tv.setText( stringFromJNI() );
+        setContentView(tv);
+    }
+
+    /* A native method that is implemented by the
+     * 'hello-jni' native library, which is packaged
+     * with this application.
+     */
+    public native String  stringFromJNI();
+
+    public native String jniNameFromJNI();
+
+    /* this is used to load the 'hello-jni' library on application
+     * startup. The library has already been unpacked into
+     * /data/data/com.example.hellojni/lib/libhello-jni.so at
+     * installation time by the package manager.
+     */
+    static {
+        System.loadLibrary("hello-jni");
+    }
+}
diff --git a/tests/ndkJniLib/lib/src/main/jni/hello-jni.c b/tests/ndkJniLib/lib/src/main/jni/hello-jni.c
new file mode 100644
index 0000000..4ee252f
--- /dev/null
+++ b/tests/ndkJniLib/lib/src/main/jni/hello-jni.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2009 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.
+ *
+ */
+#include <string.h>
+#include <jni.h>
+
+/* This is a trivial JNI example where we use a native method
+ * to return a new VM String. See the corresponding Java source
+ * file located at:
+ *
+ *   apps/samples/hello-jni/project/src/com/example/hellojni/HelloJni.java
+ */
+jstring
+Java_com_example_hellojni_lib_HelloJni_stringFromJNI(JNIEnv* env, jobject thiz)
+{
+#if defined(__arm__)
+  #if defined(__ARM_ARCH_7A__)
+    #if defined(__ARM_NEON__)
+      #define ABI "armeabi-v7a with NEON"
+    #else
+      #define ABI "armeabi-v7a"
+    #endif
+  #else
+   #define ABI "armeabi"
+  #endif
+#elif defined(__i386__)
+   #define ABI "x86"
+#elif defined(__mips__)
+   #define ABI "mips"
+#else
+   #define ABI "unknown"
+#endif
+
+    return (*env)->NewStringUTF(env, "Hello from JNI !  My ABI is " ABI ".");
+}
+
+jstring
+Java_com_example_hellojni_lib_HelloJni_jniNameFromJNI(JNIEnv* env, jobject thiz)
+{
+#if defined(__arm__)
+  #if defined(__ARM_ARCH_7A__)
+    #if defined(__ARM_NEON__)
+      #define ABI "armeabi-v7a with NEON"
+    #else
+      #define ABI "armeabi-v7a"
+    #endif
+  #else
+   #define ABI "armeabi"
+  #endif
+#elif defined(__i386__)
+   #define ABI "x86"
+#elif defined(__mips__)
+   #define ABI "mips"
+#else
+   #define ABI "unknown"
+#endif
+
+    return (*env)->NewStringUTF(env, ABI);
+}
diff --git a/tests/ndkJniLib/lib/src/main/res/values/strings.xml b/tests/ndkJniLib/lib/src/main/res/values/strings.xml
new file mode 100644
index 0000000..c526073
--- /dev/null
+++ b/tests/ndkJniLib/lib/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">HelloJni</string>
+</resources>
diff --git a/tests/ndkJniLib/settings.gradle b/tests/ndkJniLib/settings.gradle
new file mode 100644
index 0000000..7f37a58
--- /dev/null
+++ b/tests/ndkJniLib/settings.gradle
@@ -0,0 +1 @@
+include 'app', 'lib'
\ No newline at end of file
diff --git a/tests/ndkRsHelloCompute/Android.mk.old b/tests/ndkRsHelloCompute/Android.mk.old
new file mode 100644
index 0000000..5dbe19f
--- /dev/null
+++ b/tests/ndkRsHelloCompute/Android.mk.old
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2013 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+                   $(call all-renderscript-files-under, src)
+
+LOCAL_PACKAGE_NAME := HelloComputeNDK
+LOCAL_SDK_VERSION := 14
+
+LOCAL_JNI_SHARED_LIBRARIES := libhellocomputendk
+
+include $(BUILD_PACKAGE)
+include $(LOCAL_PATH)/libhellocomputendk/Android.mk
\ No newline at end of file
diff --git a/tests/ndkRsHelloCompute/build.gradle b/tests/ndkRsHelloCompute/build.gradle
new file mode 100644
index 0000000..65899ba
--- /dev/null
+++ b/tests/ndkRsHelloCompute/build.gradle
@@ -0,0 +1,43 @@
+buildscript {
+    repositories {
+        maven { url '../../../../out/host/gradle/repo' }
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
+    }
+}
+apply plugin: 'android'
+
+android {
+    compileSdkVersion 19
+    buildToolsVersion "19.0.1"
+
+    defaultConfig {
+        renderscriptNdkMode true
+        ndk {
+            moduleName "libhellocomputendk"
+            stl "stlport_shared"
+        }
+
+    }
+
+    buildTypes.debug.jniDebugBuild true
+
+    productFlavors {
+        x86 {
+            ndk {
+                abiFilter "x86"
+            }
+        }
+        arm {
+            ndk {
+                abiFilter "armeabi-v7a"
+            }
+        }
+        mips {
+            ndk {
+                abiFilter "mips"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/ndkRsHelloCompute/libhellocomputendk/Android.mk.old b/tests/ndkRsHelloCompute/libhellocomputendk/Android.mk.old
new file mode 100644
index 0000000..1c0e861
--- /dev/null
+++ b/tests/ndkRsHelloCompute/libhellocomputendk/Android.mk.old
@@ -0,0 +1,33 @@
+# Copyright (C) 2013 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.
+
+#
+# This is the shared library included by the JNI test app.
+#
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+
+LOCAL_MODULE := libhellocomputendk
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := helloComputeNDK.cpp mono.rs
+
+LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
+LOCAL_C_INCLUDES += frameworks/rs/cpp
+LOCAL_C_INCLUDES += frameworks/rs
+LOCAL_C_INCLUDES += external/stlport/stlport bionic/ bionic/libstdc++/include
+
+LOCAL_SHARED_LIBRARIES := libdl liblog libjnigraphics
+LOCAL_STATIC_LIBRARIES := libRScpp_static libstlport_static libcutils
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/ndkRsHelloCompute/src/main/AndroidManifest.xml b/tests/ndkRsHelloCompute/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..4db45e7
--- /dev/null
+++ b/tests/ndkRsHelloCompute/src/main/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.rs.hellocomputendk">
+
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-sdk android:minSdkVersion="14" />
+    <application android:label="HelloComputeNDK">
+        <activity android:name="HelloComputeNDK">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/ndkRsHelloCompute/src/main/java/com/example/android/rs/hellocomputendk/HelloComputeNDK.java b/tests/ndkRsHelloCompute/src/main/java/com/example/android/rs/hellocomputendk/HelloComputeNDK.java
new file mode 100644
index 0000000..aec6497
--- /dev/null
+++ b/tests/ndkRsHelloCompute/src/main/java/com/example/android/rs/hellocomputendk/HelloComputeNDK.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2013 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.example.android.rs.hellocomputendk;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.graphics.BitmapFactory;
+import android.graphics.Bitmap;
+import android.widget.ImageView;
+
+public class HelloComputeNDK extends Activity {
+    private Bitmap mBitmapIn;
+    private Bitmap mBitmapOut;
+
+    static {
+        System.loadLibrary("hellocomputendk");
+    }
+
+    native void nativeMono(int X, int Y, Bitmap in, Bitmap out);
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+
+        mBitmapIn = loadBitmap(R.drawable.data);
+        mBitmapOut = Bitmap.createBitmap(mBitmapIn.getWidth(), mBitmapIn.getHeight(),
+                                         mBitmapIn.getConfig());
+
+        ImageView in = (ImageView) findViewById(R.id.displayin);
+        in.setImageBitmap(mBitmapIn);
+
+        ImageView out = (ImageView) findViewById(R.id.displayout);
+        out.setImageBitmap(mBitmapOut);
+
+        nativeMono(mBitmapIn.getWidth(), mBitmapIn.getHeight(), mBitmapIn, mBitmapOut);
+
+    }
+
+    private Bitmap loadBitmap(int resource) {
+        final BitmapFactory.Options options = new BitmapFactory.Options();
+        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
+        return BitmapFactory.decodeResource(getResources(), resource, options);
+    }
+}
diff --git a/tests/ndkRsHelloCompute/src/main/jni/helloComputeNDK.cpp b/tests/ndkRsHelloCompute/src/main/jni/helloComputeNDK.cpp
new file mode 100644
index 0000000..6ed5589
--- /dev/null
+++ b/tests/ndkRsHelloCompute/src/main/jni/helloComputeNDK.cpp
@@ -0,0 +1,59 @@
+#include <jni.h>
+#include <android/log.h>
+#include <android/bitmap.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <RenderScript.h>
+
+#include "ScriptC_mono.h"
+
+#define  LOG_TAG    "HelloComputeNDK"
+#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
+#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
+
+using namespace android::RSC;
+
+extern "C" JNIEXPORT void JNICALL
+Java_com_example_android_rs_hellocomputendk_HelloComputeNDK_nativeMono(JNIEnv * env,
+                                                                       jclass,
+                                                                       jint X,
+                                                                       jint Y,
+                                                                       jobject jbitmapIn,
+                                                                       jobject jbitmapOut
+                                                                       )
+{
+
+    void* inputPtr = NULL;
+    void* outputPtr = NULL;
+
+    AndroidBitmap_lockPixels(env, jbitmapIn, &inputPtr);
+    AndroidBitmap_lockPixels(env, jbitmapOut, &outputPtr);
+
+    sp<RS> rs = new RS();
+    rs->init();
+
+    sp<const Element> e = Element::RGBA_8888(rs);
+
+    sp<const Type> t = Type::create(rs, e, X, Y, 0);
+
+    sp<Allocation> inputAlloc = Allocation::createTyped(rs, t, RS_ALLOCATION_MIPMAP_NONE,
+                                                        RS_ALLOCATION_USAGE_SHARED | RS_ALLOCATION_USAGE_SCRIPT,
+                                                        inputPtr);
+    sp<Allocation> outputAlloc = Allocation::createTyped(rs, t, RS_ALLOCATION_MIPMAP_NONE,
+                                                         RS_ALLOCATION_USAGE_SHARED | RS_ALLOCATION_USAGE_SCRIPT,
+                                                         outputPtr);
+
+
+    inputAlloc->copy2DRangeFrom(0, 0, X, Y, inputPtr);
+    ScriptC_mono* sc = new ScriptC_mono(rs);
+    sc->forEach_root(inputAlloc, outputAlloc);
+    outputAlloc->copy2DRangeTo(0, 0, X, Y, outputPtr);
+
+
+    AndroidBitmap_unlockPixels(env, jbitmapIn);
+    AndroidBitmap_unlockPixels(env, jbitmapOut);
+
+}
diff --git a/tests/ndkRsHelloCompute/src/main/res/drawable/data.jpg b/tests/ndkRsHelloCompute/src/main/res/drawable/data.jpg
new file mode 100644
index 0000000..81a87b1
--- /dev/null
+++ b/tests/ndkRsHelloCompute/src/main/res/drawable/data.jpg
Binary files differ
diff --git a/tests/ndkRsHelloCompute/src/main/res/layout/main.xml b/tests/ndkRsHelloCompute/src/main/res/layout/main.xml
new file mode 100644
index 0000000..7b2c76a
--- /dev/null
+++ b/tests/ndkRsHelloCompute/src/main/res/layout/main.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+    <ImageView
+        android:id="@+id/displayin"
+        android:layout_width="320dip"
+        android:layout_height="266dip" />
+
+    <ImageView
+        android:id="@+id/displayout"
+        android:layout_width="320dip"
+        android:layout_height="266dip" />
+
+</LinearLayout>
diff --git a/tests/ndkRsHelloCompute/src/main/rs/mono.rs b/tests/ndkRsHelloCompute/src/main/rs/mono.rs
new file mode 100644
index 0000000..efa5c72
--- /dev/null
+++ b/tests/ndkRsHelloCompute/src/main/rs/mono.rs
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.example.android.rs.hellocomputendk)
+
+const static float3 gMonoMult = {0.299f, 0.587f, 0.114f};
+
+void root(const uchar4 *v_in, uchar4 *v_out) {
+    float4 f4 = rsUnpackColor8888(*v_in);
+
+    float3 mono = dot(f4.rgb, gMonoMult);
+    *v_out = rsPackColorTo8888(mono);
+}
+
diff --git a/tests/ndkSanAngeles/README.txt b/tests/ndkSanAngeles/README.txt
new file mode 100644
index 0000000..38b8a4a
--- /dev/null
+++ b/tests/ndkSanAngeles/README.txt
@@ -0,0 +1,77 @@
+------------------------------------------------------------------------

+San Angeles Observation OpenGL ES version example

+Copyright 2004-2005 Jetro Lauha

+Web: http://iki.fi/jetro/

+See file license.txt for licensing information.

+------------------------------------------------------------------------

+

+This is an OpenGL ES port of the small self-running demonstration

+called "San Angeles Observation", which was first presented in the

+Assembly'2004 event. It won the first place in the 4 KB intro

+competition category.

+

+The demonstration features a sightseeing of a futuristic city

+having many different kind of buildings and items. Everything is

+flat shaded with three different lights.

+

+The original version was made for desktop with OpenGL. It was

+naturally heavily size optimized in order to fit it in the size

+limit. For this OpenGL ES version example much of the code is

+cleaned up and the sound is removed. Also detail level is lowered,

+although it still contains over 60000 faces.

+

+The Win32 (2000/XP) binary package of original version is

+available from this address: http://jet.ro/files/angeles.zip

+

+First version of this OpenGL ES port was submitted to the Khronos

+OpenGL ES Coding Challenge held in 2004-2005.

+

+As a code example, this source shows the following:

+  * How to create a minimal and portable ad hoc framework

+    for small testing/demonstration programs. This framework

+    compiles for both desktop and PocketPC Win32 environment,

+    and a separate source is included for Linux with X11.

+  * How to dynamically find and use the OpenGL ES DLL or

+    shared object, so that the library is not needed at

+    the compile/link stage.

+  * How to use the basic features of OpenGL ES 1.0/1.1

+    Common Lite, such as vertex arrays, color arrays and

+    lighting.

+  * How to create a self contained small demonstration

+    application with objects generated using procedural

+    algorithms.

+

+As the original version was optimized for size instead of

+performance, that holds true for this OpenGL ES version as

+well. Thus the performance could be significantly increased,

+for example by changing the code to use glDrawElements

+instead of glDrawArrays. The code uses only OpenGL ES 1.0

+Common Lite -level function calls without any extensions.

+

+The reference OpenGL ES implementations used for this application:

+  * Hybrid's OpenGL ES API Implementation (Gerbera) version 2.0.4

+    Prebuilt Win32 PC executable: SanOGLES-Gerbera.exe

+  * PowerVR MBX SDK, OpenGL ES Windows PC Emulation version 1.04.14.0170

+    Prebuilt Win32 PC executable: SanOGLES-PVRSDK.exe

+

+Note that DISABLE_IMPORTGL preprocessor macro can be used

+to specify not to use dynamic runtime binding of the library.

+You also need to define preprocessor macro PVRSDK to compile

+the source with PowerVR OpenGL ES SDK.

+

+The demo application is briefly tested with a few other OpenGL ES

+implementations as well (e.g. Vincent, GLESonGL on Linux, Dell

+Axim X50v). Most of these other implementations rendered the demo

+erroneously in some aspect. This may indicate that the demo source

+could still have some work to do with compatibility and correct

+API usage, although the non-conforming implementations are most

+probably unfinished as well.

+

+Thanks and Acknowledgements:

+

+* Toni Lönnberg (!Cube) created the music for original version, which

+  is not featured in this OpenGL ES port.

+* Sara Kapli (st Rana) for additional camera work.

+* Paul Bourke for information about the supershapes.

+

+------------------------------------------------------------------------

diff --git a/tests/ndkSanAngeles/build.gradle b/tests/ndkSanAngeles/build.gradle
new file mode 100644
index 0000000..c537e77
--- /dev/null
+++ b/tests/ndkSanAngeles/build.gradle
@@ -0,0 +1,43 @@
+buildscript {
+    repositories {
+        maven { url '../../../../out/host/gradle/repo' }
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
+    }
+}
+apply plugin: 'android'
+
+android {
+    compileSdkVersion 15
+    buildToolsVersion "18.0.1"
+
+    defaultConfig {
+        ndk {
+            moduleName "sanangeles"
+            cFlags "-DANDROID_NDK -DDISABLE_IMPORTGL"
+            ldLibs "GLESv1_CM", "dl", "log"
+            stl "stlport_static"
+        }
+    }
+
+    buildTypes.debug.jniDebugBuild true
+
+    productFlavors {
+        x86 {
+            ndk {
+                abiFilter "x86"
+            }
+        }
+        arm {
+            ndk {
+                abiFilter "armeabi-v7a"
+            }
+        }
+        mips {
+            ndk {
+                abiFilter "mips"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/ndkSanAngeles/license-BSD.txt b/tests/ndkSanAngeles/license-BSD.txt
new file mode 100644
index 0000000..8924e3c
--- /dev/null
+++ b/tests/ndkSanAngeles/license-BSD.txt
@@ -0,0 +1,34 @@
+This is the BSD-style license for the "San Angeles Observation"

+OpenGL ES version example source code

+---------------------------------------------------------------

+

+San Angeles Observation OpenGL ES version example

+Copyright (c) 2004-2005, Jetro Lauha

+All rights reserved.

+

+Redistribution and use in source and binary forms, with or without

+modification, are permitted provided that the following conditions

+are met:

+

+    * Redistributions of source code must retain the above copyright

+      notice, this list of conditions and the following disclaimer.

+    * Redistributions in binary form must reproduce the above copyright

+      notice, this list of conditions and the following disclaimer in

+      the documentation and/or other materials provided with the

+      distribution.

+    * Neither the name of the software product's copyright owner nor

+      the names of its contributors may be used to endorse or promote

+      products derived from this software without specific prior written

+      permission.

+

+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED

+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR

+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF

+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING

+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS

+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

diff --git a/tests/ndkSanAngeles/license-LGPL.txt b/tests/ndkSanAngeles/license-LGPL.txt
new file mode 100644
index 0000000..b1e3f5a
--- /dev/null
+++ b/tests/ndkSanAngeles/license-LGPL.txt
@@ -0,0 +1,504 @@
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/tests/ndkSanAngeles/license.txt b/tests/ndkSanAngeles/license.txt
new file mode 100644
index 0000000..620841e
--- /dev/null
+++ b/tests/ndkSanAngeles/license.txt
@@ -0,0 +1,19 @@
+San Angeles Observation OpenGL ES version example

+Copyright 2004-2005 Jetro Lauha

+All rights reserved.

+Web: http://iki.fi/jetro/

+

+This source is free software; you can redistribute it and/or

+modify it under the terms of EITHER:

+  (1) The GNU Lesser General Public License as published by the Free

+      Software Foundation; either version 2.1 of the License, or (at

+      your option) any later version. The text of the GNU Lesser

+      General Public License is included with this source in the

+      file LICENSE-LGPL.txt.

+  (2) The BSD-style license that is included with this source in

+      the file LICENSE-BSD.txt.

+

+This source is distributed in the hope that it will be useful,

+but WITHOUT ANY WARRANTY; without even the implied warranty of

+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files

+LICENSE-LGPL.txt and LICENSE-BSD.txt for more details.

diff --git a/tests/ndkSanAngeles/misc/app-linux.c b/tests/ndkSanAngeles/misc/app-linux.c
new file mode 100644
index 0000000..6b573f2
--- /dev/null
+++ b/tests/ndkSanAngeles/misc/app-linux.c
@@ -0,0 +1,247 @@
+/* San Angeles Observation OpenGL ES version example
+ * Copyright 2004-2005 Jetro Lauha
+ * All rights reserved.
+ * Web: http://iki.fi/jetro/
+ *
+ * This source is free software; you can redistribute it and/or
+ * modify it under the terms of EITHER:
+ *   (1) The GNU Lesser General Public License as published by the Free
+ *       Software Foundation; either version 2.1 of the License, or (at
+ *       your option) any later version. The text of the GNU Lesser
+ *       General Public License is included with this source in the
+ *       file LICENSE-LGPL.txt.
+ *   (2) The BSD-style license that is included with this source in
+ *       the file LICENSE-BSD.txt.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files
+ * LICENSE-LGPL.txt and LICENSE-BSD.txt for more details.
+ *
+ * $Id: app-linux.c,v 1.4 2005/02/08 18:42:48 tonic Exp $
+ * $Revision: 1.4 $
+ *
+ * Parts of this source file is based on test/example code from
+ * GLESonGL implementation by David Blythe. Here is copy of the
+ * license notice from that source:
+ *
+ * Copyright (C) 2003  David Blythe   All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DAVID BLYTHE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+
+#include "importgl.h"
+
+#include "app.h"
+
+
+int gAppAlive = 1;
+
+static const char sAppName[] =
+    "San Angeles Observation OpenGL ES version example (Linux)";
+static Display *sDisplay;
+static Window sWindow;
+static int sWindowWidth = WINDOW_DEFAULT_WIDTH;
+static int sWindowHeight = WINDOW_DEFAULT_HEIGHT;
+static EGLDisplay sEglDisplay = EGL_NO_DISPLAY;
+static EGLConfig sEglConfig;
+static EGLContext sEglContext = EGL_NO_CONTEXT;
+static EGLSurface sEglSurface = EGL_NO_SURFACE;
+
+
+static void checkGLErrors()
+{
+    GLenum error = glGetError();
+    if (error != GL_NO_ERROR)
+        fprintf(stderr, "GL Error: 0x%04x\n", (int)error);
+}
+
+
+static void checkEGLErrors()
+{
+    EGLint error = eglGetError();
+    // GLESonGL seems to be returning 0 when there is no errors?
+    if (error && error != EGL_SUCCESS)
+        fprintf(stderr, "EGL Error: 0x%04x\n", (int)error);
+}
+
+
+// Initializes and opens both X11 display and OpenGL ES.
+static int initGraphics()
+{
+    static const EGLint configAttribs[] =
+    {
+#if (WINDOW_BPP == 16)
+        EGL_RED_SIZE,       5,
+        EGL_GREEN_SIZE,     5,
+        EGL_BLUE_SIZE,      5,
+#elif (WINDOW_BPP == 32)
+        EGL_RED_SIZE,       8,
+        EGL_GREEN_SIZE,     8,
+        EGL_BLUE_SIZE,      8,
+#else
+#error WINDOW_BPP must be 16 or 32
+#endif
+        EGL_DEPTH_SIZE,     16,
+        EGL_ALPHA_SIZE,     EGL_DONT_CARE,
+        EGL_STENCIL_SIZE,   EGL_DONT_CARE,
+        EGL_SURFACE_TYPE,   EGL_WINDOW_BIT,
+        EGL_NONE
+    };
+    EGLBoolean success;
+    EGLint numConfigs;
+    EGLint majorVersion;
+    EGLint minorVersion;
+
+    int importGLResult;
+    importGLResult = importGLInit();
+    if (!importGLResult)
+        return 0;
+
+    sDisplay = XOpenDisplay(NULL);
+
+    sEglDisplay = eglGetDisplay(sDisplay);
+    success = eglInitialize(sEglDisplay, &majorVersion, &minorVersion);
+    if (success != EGL_FALSE)
+        success = eglGetConfigs(sEglDisplay, NULL, 0, &numConfigs);
+    if (success != EGL_FALSE)
+        success = eglChooseConfig(sEglDisplay, configAttribs,
+                                  &sEglConfig, 1, &numConfigs);
+    if (success != EGL_FALSE)
+    {
+        sEglContext = eglCreateContext(sEglDisplay, sEglConfig, NULL, NULL);
+        if (sEglContext == EGL_NO_CONTEXT)
+            success = EGL_FALSE;
+    }
+    if (success != EGL_FALSE)
+    {
+        XSetWindowAttributes swa;
+        XVisualInfo *vi, tmp;
+        XSizeHints sh;
+        int n;
+        EGLint vid;
+
+        eglGetConfigAttrib(sEglDisplay, sEglConfig,
+                           EGL_NATIVE_VISUAL_ID, &vid);
+        tmp.visualid = vid;
+        vi = XGetVisualInfo(sDisplay, VisualIDMask, &tmp, &n);
+        swa.colormap = XCreateColormap(sDisplay,
+                                       RootWindow(sDisplay, vi->screen),
+                                       vi->visual, AllocNone);
+        sh.flags = PMinSize | PMaxSize;
+        sh.min_width = sh.max_width = sWindowWidth;
+        sh.min_height = sh.max_height = sWindowHeight;
+        swa.border_pixel = 0;
+        swa.event_mask = ExposureMask | StructureNotifyMask |
+                         KeyPressMask | ButtonPressMask | ButtonReleaseMask;
+        sWindow = XCreateWindow(sDisplay, RootWindow(sDisplay, vi->screen),
+                                0, 0, sWindowWidth, sWindowHeight,
+                                0, vi->depth, InputOutput, vi->visual,
+                                CWBorderPixel | CWColormap | CWEventMask,
+                                &swa);
+        XMapWindow(sDisplay, sWindow);
+        XSetStandardProperties(sDisplay, sWindow, sAppName, sAppName,
+                               None, (void *)0, 0, &sh);
+    }
+    if (success != EGL_FALSE)
+    {
+        sEglSurface = eglCreateWindowSurface(sEglDisplay, sEglConfig,
+                                             (NativeWindowType)sWindow, NULL);
+        if (sEglSurface == EGL_NO_SURFACE)
+            success = EGL_FALSE;
+    }
+    if (success != EGL_FALSE)
+        success = eglMakeCurrent(sEglDisplay, sEglSurface,
+                                 sEglSurface, sEglContext);
+
+    if (success == EGL_FALSE)
+        checkEGLErrors();
+
+    return success != EGL_FALSE;
+}
+
+
+static void deinitGraphics()
+{
+    eglMakeCurrent(sEglDisplay, NULL, NULL, NULL);
+    eglDestroyContext(sEglDisplay, sEglContext);
+    eglDestroySurface(sEglDisplay, sEglSurface);
+    eglTerminate(sEglDisplay);
+    importGLDeinit();
+}
+
+
+int main(int argc, char *argv[])
+{
+    // not referenced:
+    argc = argc;
+    argv = argv;
+
+    if (!initGraphics())
+    {
+        fprintf(stderr, "Graphics initialization failed.\n");
+        return EXIT_FAILURE;
+    }
+
+    appInit();
+
+    while (gAppAlive)
+    {
+        struct timeval timeNow;
+
+        while (XPending(sDisplay))
+        {
+            XEvent ev;
+            XNextEvent(sDisplay, &ev);
+            switch (ev.type)
+            {
+            case KeyPress:
+                {
+                    unsigned int keycode, keysym;
+                    keycode = ((XKeyEvent *)&ev)->keycode;
+                    keysym = XKeycodeToKeysym(sDisplay, keycode, 0);
+                    if (keysym == XK_Return || keysym == XK_Escape)
+                        gAppAlive = 0;
+                }
+                break;
+            }
+        }
+
+        if (gAppAlive)
+        {
+            gettimeofday(&timeNow, NULL);
+            appRender(timeNow.tv_sec * 1000 + timeNow.tv_usec / 1000,
+                      sWindowWidth, sWindowHeight);
+            checkGLErrors();
+            eglSwapBuffers(sEglDisplay, sEglSurface);
+            checkEGLErrors();
+        }
+    }
+
+    appDeinit();
+    deinitGraphics();
+
+    return EXIT_SUCCESS;
+}
diff --git a/tests/ndkSanAngeles/misc/app-win32.c b/tests/ndkSanAngeles/misc/app-win32.c
new file mode 100644
index 0000000..b47577e
--- /dev/null
+++ b/tests/ndkSanAngeles/misc/app-win32.c
@@ -0,0 +1,294 @@
+/* San Angeles Observation OpenGL ES version example
+ * Copyright 2004-2005 Jetro Lauha
+ * All rights reserved.
+ * Web: http://iki.fi/jetro/
+ *
+ * This source is free software; you can redistribute it and/or
+ * modify it under the terms of EITHER:
+ *   (1) The GNU Lesser General Public License as published by the Free
+ *       Software Foundation; either version 2.1 of the License, or (at
+ *       your option) any later version. The text of the GNU Lesser
+ *       General Public License is included with this source in the
+ *       file LICENSE-LGPL.txt.
+ *   (2) The BSD-style license that is included with this source in
+ *       the file LICENSE-BSD.txt.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files
+ * LICENSE-LGPL.txt and LICENSE-BSD.txt for more details.
+ *
+ * $Id: app-win32.c,v 1.6 2005/02/24 20:29:00 tonic Exp $
+ * $Revision: 1.6 $
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <tchar.h>
+#ifdef UNDER_CE
+#include <aygshell.h>
+#endif
+
+#include <stdio.h>
+
+#include "importgl.h"
+
+#include "app.h"
+
+
+int gAppAlive = 1;
+
+static HINSTANCE sInstance;
+
+static const _TCHAR sAppName[] =
+    _T("San Angeles Observation OpenGL ES version example (Win32)");
+static HWND sWnd;
+static int sWindowWidth = WINDOW_DEFAULT_WIDTH;
+static int sWindowHeight = WINDOW_DEFAULT_HEIGHT;
+static EGLDisplay sEglDisplay = EGL_NO_DISPLAY;
+static EGLConfig sEglConfig;
+static EGLContext sEglContext = EGL_NO_CONTEXT;
+static EGLSurface sEglSurface = EGL_NO_SURFACE;
+
+
+static void checkGLErrors()
+{
+    GLenum error = glGetError();
+    if (error != GL_NO_ERROR)
+    {
+        _TCHAR errorString[32];
+        _stprintf(errorString, _T("0x%04x"), error);
+        MessageBox(NULL, errorString, _T("GL Error"), MB_OK);
+    }
+}
+
+
+static void checkEGLErrors()
+{
+    EGLint error = eglGetError();
+    if (error != EGL_SUCCESS)
+    {
+        _TCHAR errorString[32];
+        _stprintf(errorString, _T("0x%04x"), error);
+        MessageBox(NULL, errorString, _T("EGL Initialization Error"), MB_OK);
+    }
+}
+
+
+static BOOL initEGL(HWND wnd)
+{
+    static const EGLint configAttribs[] =
+    {
+#if (WINDOW_BPP == 16)
+        EGL_RED_SIZE,       5,
+        EGL_GREEN_SIZE,     5,
+        EGL_BLUE_SIZE,      5,
+#elif (WINDOW_BPP == 32)
+        EGL_RED_SIZE,       8,
+        EGL_GREEN_SIZE,     8,
+        EGL_BLUE_SIZE,      8,
+#else
+#error WINDOW_BPP must be 16 or 32
+#endif
+        EGL_DEPTH_SIZE,     16,
+        EGL_ALPHA_SIZE,     EGL_DONT_CARE,
+        EGL_STENCIL_SIZE,   EGL_DONT_CARE,
+        EGL_SURFACE_TYPE,   EGL_WINDOW_BIT,
+        EGL_NONE
+    };
+    EGLBoolean success;
+    EGLint numConfigs;
+    EGLint majorVersion;
+    EGLint minorVersion;
+#ifdef PVRSDK
+    HDC dc;
+#endif // PVRSDK
+
+#ifndef DISABLE_IMPORTGL
+    int importGLResult;
+    importGLResult = importGLInit();
+    if (!importGLResult)
+        return FALSE;
+#endif // !DISABLE_IMPORTGL
+
+#ifdef PVRSDK
+    dc = GetDC(sWnd);
+    sEglDisplay = eglGetDisplay(dc);
+#else
+    sEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+#endif // !PVRSDK
+    success = eglInitialize(sEglDisplay, &majorVersion, &minorVersion);
+    if (success != EGL_FALSE)
+        success = eglGetConfigs(sEglDisplay, NULL, 0, &numConfigs);
+    if (success != EGL_FALSE)
+        success = eglChooseConfig(sEglDisplay, configAttribs,
+                                  &sEglConfig, 1, &numConfigs);
+    if (success != EGL_FALSE)
+    {
+        sEglSurface = eglCreateWindowSurface(sEglDisplay, sEglConfig,
+                                             wnd, NULL);
+        if (sEglSurface == EGL_NO_SURFACE)
+            success = EGL_FALSE;
+    }
+    if (success != EGL_FALSE)
+    {
+        sEglContext = eglCreateContext(sEglDisplay, sEglConfig, NULL, NULL);
+        if (sEglContext == EGL_NO_CONTEXT)
+            success = EGL_FALSE;
+    }
+    if (success != EGL_FALSE)
+        success = eglMakeCurrent(sEglDisplay, sEglSurface,
+                                 sEglSurface, sEglContext);
+
+    if (success == EGL_FALSE)
+        checkEGLErrors();
+
+    return success;
+}
+
+
+static void deinitEGL()
+{
+    eglMakeCurrent(sEglDisplay, NULL, NULL, NULL);
+    eglDestroyContext(sEglDisplay, sEglContext);
+    eglDestroySurface(sEglDisplay, sEglSurface);
+    eglTerminate(sEglDisplay);
+#ifndef DISABLE_IMPORTGL
+    importGLDeinit();
+#endif // !DISABLE_IMPORTGL
+}
+
+
+static LRESULT CALLBACK wndProc(HWND wnd, UINT message,
+                                WPARAM wParam, LPARAM lParam)
+{
+    RECT rc;
+    int useDefWindowProc = 0;
+
+    switch (message)
+    {
+    case WM_CLOSE:
+        DestroyWindow(wnd);
+        gAppAlive = 0;
+        break;
+
+    case WM_DESTROY:
+        PostQuitMessage(0);
+        gAppAlive = 0;
+        break;
+
+    case WM_KEYDOWN:
+        if (wParam == VK_ESCAPE || wParam == VK_RETURN)
+            gAppAlive = 0;
+        useDefWindowProc = 1;
+        break;
+
+    case WM_KEYUP:
+        useDefWindowProc = 1;
+        break;
+
+    case WM_SIZE:
+        GetClientRect(sWnd, &rc);
+        sWindowWidth = rc.right;
+        sWindowHeight = rc.bottom;
+        break;
+
+    default:
+        useDefWindowProc = 1;
+    }
+
+    if (useDefWindowProc)
+        return DefWindowProc(wnd, message, wParam, lParam);
+    return 0;
+}
+
+
+int WINAPI WinMain(HINSTANCE instance, HINSTANCE prevInstance,
+                   LPTSTR cmdLine, int cmdShow)
+{
+    MSG msg;
+    WNDCLASS wc;
+    DWORD windowStyle;
+    int windowX, windowY;
+
+    // not referenced:
+    prevInstance = prevInstance;
+    cmdLine = cmdLine;
+
+
+    sInstance = instance;
+
+    // register class
+    wc.style = CS_HREDRAW | CS_VREDRAW;
+    wc.lpfnWndProc = (WNDPROC)wndProc;
+    wc.cbClsExtra = 0;
+    wc.cbWndExtra = 0;
+    wc.hInstance = sInstance;
+    wc.hIcon = NULL;
+    wc.hCursor = 0;
+    wc.hbrBackground = GetStockObject(BLACK_BRUSH);
+    wc.lpszMenuName = NULL;
+    wc.lpszClassName = sAppName;
+    if (!RegisterClass(&wc))
+        return FALSE;
+
+    // init instance
+    windowStyle = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE;
+#ifdef UNDER_CE
+    sWindowWidth = GetSystemMetrics(SM_CXSCREEN);
+    sWindowHeight = GetSystemMetrics(SM_CYSCREEN);
+    windowX = windowY = 0;
+#else
+    windowStyle |= WS_OVERLAPPEDWINDOW;
+    windowX = CW_USEDEFAULT;
+    windowY = 0;
+#endif
+    sWnd = CreateWindow(sAppName, sAppName, windowStyle,
+                        windowX, windowY,
+                        sWindowWidth, sWindowHeight,
+                        NULL, NULL, instance, NULL);
+    if (!sWnd)
+        return FALSE;
+
+    ShowWindow(sWnd, cmdShow);
+
+#ifdef UNDER_CE
+    SHFullScreen(sWnd,
+                 SHFS_HIDETASKBAR | SHFS_HIDESIPBUTTON | SHFS_HIDESTARTICON);
+    MoveWindow(sWnd, 0, 0, sWindowWidth, sWindowHeight, TRUE);
+#endif
+
+    UpdateWindow(sWnd);
+
+    if (!initEGL(sWnd))
+        return FALSE;
+
+    appInit(sWindowWidth, sWindowHeight);
+
+    while (gAppAlive)
+    {
+        while (PeekMessage(&msg, sWnd, 0, 0, PM_NOREMOVE))
+        {
+            if (GetMessage(&msg, sWnd, 0, 0))
+            {
+                TranslateMessage(&msg);
+                DispatchMessage(&msg);
+            }
+            else
+                gAppAlive = 0;
+        }
+
+        if (gAppAlive)
+        {
+            appRender(GetTickCount(), sWindowWidth, sWindowHeight);
+            checkGLErrors();
+            eglSwapBuffers(sEglDisplay, sEglSurface);
+            checkEGLErrors();
+        }
+    }
+
+    appDeinit();
+    deinitEGL();
+
+    return 0;
+}
diff --git a/tests/ndkSanAngeles/src/main/AndroidManifest.xml b/tests/ndkSanAngeles/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..5ae6a8e
--- /dev/null
+++ b/tests/ndkSanAngeles/src/main/AndroidManifest.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="com.example.SanAngeles"
+      android:versionCode="1"
+      android:versionName="1.0">
+    <application android:label="@string/app_name">
+        <activity android:name=".DemoActivity"
+                  android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+    <uses-sdk android:minSdkVersion="4" />
+</manifest> 
diff --git a/tests/ndkSanAngeles/src/main/java/com/example/SanAngeles/DemoActivity.java b/tests/ndkSanAngeles/src/main/java/com/example/SanAngeles/DemoActivity.java
new file mode 100644
index 0000000..076b8a7
--- /dev/null
+++ b/tests/ndkSanAngeles/src/main/java/com/example/SanAngeles/DemoActivity.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2009 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.
+ *
+ * This is a small port of the "San Angeles Observation" demo
+ * program for OpenGL ES 1.x. For more details, see:
+ *
+ *    http://jet.ro/visuals/san-angeles-observation/
+ *
+ * This program demonstrates how to use a GLSurfaceView from Java
+ * along with native OpenGL calls to perform frame rendering.
+ *
+ * Touching the screen will start/stop the animation.
+ *
+ * Note that the demo runs much faster on the emulator than on
+ * real devices, this is mainly due to the following facts:
+ *
+ * - the demo sends bazillions of polygons to OpenGL without
+ *   even trying to do culling. Most of them are clearly out
+ *   of view.
+ *
+ * - on a real device, the GPU bus is the real bottleneck
+ *   that prevent the demo from getting acceptable performance.
+ *
+ * - the software OpenGL engine used in the emulator uses
+ *   the system bus instead, and its code rocks :-)
+ *
+ * Fixing the program to send less polygons to the GPU is left
+ * as an exercise to the reader. As always, patches welcomed :-)
+ */
+package com.example.SanAngeles;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.app.Activity;
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.view.MotionEvent;
+
+public class DemoActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mGLView = new DemoGLSurfaceView(this);
+        setContentView(mGLView);
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        mGLView.onPause();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mGLView.onResume();
+    }
+
+    private GLSurfaceView mGLView;
+
+    static {
+        System.loadLibrary("sanangeles");
+    }
+}
+
+class DemoGLSurfaceView extends GLSurfaceView {
+    public DemoGLSurfaceView(Context context) {
+        super(context);
+        mRenderer = new DemoRenderer();
+        setRenderer(mRenderer);
+    }
+
+    public boolean onTouchEvent(final MotionEvent event) {
+        if (event.getAction() == MotionEvent.ACTION_DOWN) {
+            nativeTogglePauseResume();
+        }
+        return true;
+    }
+
+   @Override
+    public void onPause() {
+        super.onPause();
+        nativePause();
+    }
+
+   @Override
+    public void onResume() {
+        super.onResume();
+        nativeResume();
+    }
+
+
+    DemoRenderer mRenderer;
+
+    private static native void nativePause();
+    private static native void nativeResume();
+    private static native void nativeTogglePauseResume();
+}
+
+class DemoRenderer implements GLSurfaceView.Renderer {
+    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+        nativeInit();
+    }
+
+    public void onSurfaceChanged(GL10 gl, int w, int h) {
+        //gl.glViewport(0, 0, w, h);
+        nativeResize(w, h);
+    }
+
+    public void onDrawFrame(GL10 gl) {
+        nativeRender();
+    }
+
+    private static native void nativeInit();
+    private static native void nativeResize(int w, int h);
+    private static native void nativeRender();
+    private static native void nativeDone();
+}
diff --git a/tests/ndkSanAngeles/src/main/jni/app-android.c b/tests/ndkSanAngeles/src/main/jni/app-android.c
new file mode 100644
index 0000000..399d896
--- /dev/null
+++ b/tests/ndkSanAngeles/src/main/jni/app-android.c
@@ -0,0 +1,137 @@
+/* San Angeles Observation OpenGL ES version example
+ * Copyright 2009 The Android Open Source Project
+ * All rights reserved.
+ *
+ * This source is free software; you can redistribute it and/or
+ * modify it under the terms of EITHER:
+ *   (1) The GNU Lesser General Public License as published by the Free
+ *       Software Foundation; either version 2.1 of the License, or (at
+ *       your option) any later version. The text of the GNU Lesser
+ *       General Public License is included with this source in the
+ *       file LICENSE-LGPL.txt.
+ *   (2) The BSD-style license that is included with this source in
+ *       the file LICENSE-BSD.txt.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files
+ * LICENSE-LGPL.txt and LICENSE-BSD.txt for more details.
+ */
+#include <jni.h>
+#include <sys/time.h>
+#include <time.h>
+#include <android/log.h>
+#include <stdint.h>
+#include "importgl.h"
+#include "app.h"
+
+int   gAppAlive   = 1;
+
+static int  sWindowWidth  = 320;
+static int  sWindowHeight = 480;
+static int  sDemoStopped  = 0;
+static long sTimeOffset   = 0;
+static int  sTimeOffsetInit = 0;
+static long sTimeStopped  = 0;
+
+static long
+_getTime(void)
+{
+    struct timeval  now;
+
+    gettimeofday(&now, NULL);
+    return (long)(now.tv_sec*1000 + now.tv_usec/1000);
+}
+
+/* Call to initialize the graphics state */
+void
+Java_com_example_SanAngeles_DemoRenderer_nativeInit( JNIEnv*  env )
+{
+    importGLInit();
+    appInit();
+    gAppAlive  = 1;
+}
+
+void
+Java_com_example_SanAngeles_DemoRenderer_nativeResize( JNIEnv*  env, jobject  thiz, jint w, jint h )
+{
+    sWindowWidth  = w;
+    sWindowHeight = h;
+    __android_log_print(ANDROID_LOG_INFO, "SanAngeles", "resize w=%d h=%d", w, h);
+}
+
+/* Call to finalize the graphics state */
+void
+Java_com_example_SanAngeles_DemoRenderer_nativeDone( JNIEnv*  env )
+{
+    appDeinit();
+    importGLDeinit();
+}
+
+/* This is called to indicate to the render loop that it should
+ * stop as soon as possible.
+ */
+
+void _pause()
+{
+  /* we paused the animation, so store the current
+   * time in sTimeStopped for future nativeRender calls */
+    sDemoStopped = 1;
+    sTimeStopped = _getTime();
+}
+
+void _resume()
+{
+  /* we resumed the animation, so adjust the time offset
+   * to take care of the pause interval. */
+    sDemoStopped = 0;
+    sTimeOffset -= _getTime() - sTimeStopped;
+}
+
+
+void
+Java_com_example_SanAngeles_DemoGLSurfaceView_nativeTogglePauseResume( JNIEnv*  env )
+{
+    sDemoStopped = !sDemoStopped;
+    if (sDemoStopped)
+        _pause();
+    else
+        _resume();
+}
+
+void
+Java_com_example_SanAngeles_DemoGLSurfaceView_nativePause( JNIEnv*  env )
+{
+    _pause();
+}
+
+void
+Java_com_example_SanAngeles_DemoGLSurfaceView_nativeResume( JNIEnv*  env )
+{
+    _resume();
+}
+
+/* Call to render the next GL frame */
+void
+Java_com_example_SanAngeles_DemoRenderer_nativeRender( JNIEnv*  env )
+{
+    long   curTime;
+
+    /* NOTE: if sDemoStopped is TRUE, then we re-render the same frame
+     *       on each iteration.
+     */
+    if (sDemoStopped) {
+        curTime = sTimeStopped + sTimeOffset;
+    } else {
+        curTime = _getTime() + sTimeOffset;
+        if (sTimeOffsetInit == 0) {
+            sTimeOffsetInit = 1;
+            sTimeOffset     = -curTime;
+            curTime         = 0;
+        }
+    }
+
+    //__android_log_print(ANDROID_LOG_INFO, "SanAngeles", "curTime=%ld", curTime);
+
+    appRender(curTime, sWindowWidth, sWindowHeight);
+}
diff --git a/tests/ndkSanAngeles/src/main/jni/app.h b/tests/ndkSanAngeles/src/main/jni/app.h
new file mode 100644
index 0000000..70ebd35
--- /dev/null
+++ b/tests/ndkSanAngeles/src/main/jni/app.h
@@ -0,0 +1,56 @@
+/* San Angeles Observation OpenGL ES version example
+ * Copyright 2004-2005 Jetro Lauha
+ * All rights reserved.
+ * Web: http://iki.fi/jetro/
+ *
+ * This source is free software; you can redistribute it and/or
+ * modify it under the terms of EITHER:
+ *   (1) The GNU Lesser General Public License as published by the Free
+ *       Software Foundation; either version 2.1 of the License, or (at
+ *       your option) any later version. The text of the GNU Lesser
+ *       General Public License is included with this source in the
+ *       file LICENSE-LGPL.txt.
+ *   (2) The BSD-style license that is included with this source in
+ *       the file LICENSE-BSD.txt.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files
+ * LICENSE-LGPL.txt and LICENSE-BSD.txt for more details.
+ *
+ * $Id: app.h,v 1.14 2005/02/06 21:13:54 tonic Exp $
+ * $Revision: 1.14 $
+ */
+
+#ifndef APP_H_INCLUDED
+#define APP_H_INCLUDED
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define WINDOW_DEFAULT_WIDTH    640
+#define WINDOW_DEFAULT_HEIGHT   480
+
+#define WINDOW_BPP              16
+
+
+// The simple framework expects the application code to define these functions.
+extern void appInit();
+extern void appDeinit();
+extern void appRender(long tick, int width, int height);
+
+/* Value is non-zero when application is alive, and 0 when it is closing.
+ * Defined by the application framework.
+ */
+extern int gAppAlive;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif // !APP_H_INCLUDED
diff --git a/tests/ndkSanAngeles/src/main/jni/cams.h b/tests/ndkSanAngeles/src/main/jni/cams.h
new file mode 100644
index 0000000..2b1acb3
--- /dev/null
+++ b/tests/ndkSanAngeles/src/main/jni/cams.h
@@ -0,0 +1,65 @@
+/* San Angeles Observation OpenGL ES version example
+ * Copyright 2004-2005 Jetro Lauha
+ * All rights reserved.
+ * Web: http://iki.fi/jetro/
+ *
+ * This source is free software; you can redistribute it and/or
+ * modify it under the terms of EITHER:
+ *   (1) The GNU Lesser General Public License as published by the Free
+ *       Software Foundation; either version 2.1 of the License, or (at
+ *       your option) any later version. The text of the GNU Lesser
+ *       General Public License is included with this source in the
+ *       file LICENSE-LGPL.txt.
+ *   (2) The BSD-style license that is included with this source in
+ *       the file LICENSE-BSD.txt.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files
+ * LICENSE-LGPL.txt and LICENSE-BSD.txt for more details.
+ *
+ * $Id: cams.h,v 1.7 2005/01/31 22:15:15 tonic Exp $
+ * $Revision: 1.7 $
+ */
+
+#ifndef CAMS_H_INCLUDED
+#define CAMS_H_INCLUDED
+
+
+/* Length in milliseconds of one camera track base unit.
+ * The value originates from the music synchronization.
+ */
+#define CAMTRACK_LEN    5442
+
+
+// Camera track definition for one camera trucking shot.
+typedef struct
+{
+    /* Five parameters of src[5] and dest[5]:
+     * eyeX, eyeY, eyeZ, viewAngle, viewHeightOffs
+     */
+    short src[5], dest[5];
+    unsigned char dist;     // if >0, cam rotates around eye xy on dist * 0.1
+    unsigned char len;      // length multiplier
+} CAMTRACK;
+
+static CAMTRACK sCamTracks[] =
+{
+    { { 4500, 2700, 100, 70, -30 }, { 50, 50, -90, -100, 0 }, 20, 1 },
+    { { -1448, 4294, 25, 363, 0 }, { -136, 202, 125, -98, 100 }, 0, 1 },
+    { { 1437, 4930, 200, -275, -20 }, { 1684, 0, 0, 9, 0 }, 0, 1 },
+    { { 1800, 3609, 200, 0, 675 }, { 0, 0, 0, 300, 0 }, 0, 1 },
+    { { 923, 996, 50, 2336, -80 }, { 0, -20, -50, 0, 170 }, 0, 1 },
+    { { -1663, -43, 600, 2170, 0 }, { 20, 0, -600, 0, 100 }, 0, 1 },
+    { { 1049, -1420, 175, 2111, -17 }, { 0, 0, 0, -334, 0 }, 0, 2 },
+    { { 0, 0, 50, 300, 25 }, { 0, 0, 0, 300, 0 }, 70, 2 },
+    { { -473, -953, 3500, -353, -350 }, { 0, 0, -2800, 0, 0 }, 0, 2 },
+    { { 191, 1938, 35, 1139, -17 }, { 1205, -2909, 0, 0, 0 }, 0, 2 },
+    { { -1449, -2700, 150, 0, 0 }, { 0, 2000, 0, 0, 0 }, 0, 2 },
+    { { 5273, 4992, 650, 373, -50 }, { -4598, -3072, 0, 0, 0 }, 0, 2 },
+    { { 3223, -3282, 1075, -393, -25 }, { 1649, -1649, 0, 0, 0 }, 0, 2 }
+};
+#define CAMTRACK_COUNT (sizeof(camTracks) / sizeof(camTracks[0]))
+
+
+#endif // !CAMS_H_INCLUDED
diff --git a/tests/ndkSanAngeles/src/main/jni/demo.c b/tests/ndkSanAngeles/src/main/jni/demo.c
new file mode 100644
index 0000000..9cb73d1
--- /dev/null
+++ b/tests/ndkSanAngeles/src/main/jni/demo.c
@@ -0,0 +1,792 @@
+/* San Angeles Observation OpenGL ES version example
+ * Copyright 2004-2005 Jetro Lauha
+ * All rights reserved.
+ * Web: http://iki.fi/jetro/
+ *
+ * This source is free software; you can redistribute it and/or
+ * modify it under the terms of EITHER:
+ *   (1) The GNU Lesser General Public License as published by the Free
+ *       Software Foundation; either version 2.1 of the License, or (at
+ *       your option) any later version. The text of the GNU Lesser
+ *       General Public License is included with this source in the
+ *       file LICENSE-LGPL.txt.
+ *   (2) The BSD-style license that is included with this source in
+ *       the file LICENSE-BSD.txt.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files
+ * LICENSE-LGPL.txt and LICENSE-BSD.txt for more details.
+ *
+ * $Id: demo.c,v 1.10 2005/02/08 20:54:39 tonic Exp $
+ * $Revision: 1.10 $
+ */
+
+#include <stdlib.h>
+#include <math.h>
+#include <float.h>
+#include <assert.h>
+
+#include "importgl.h"
+
+#include "app.h"
+#include "shapes.h"
+#include "cams.h"
+
+
+// Total run length is 20 * camera track base unit length (see cams.h).
+#define RUN_LENGTH  (20 * CAMTRACK_LEN)
+#undef PI
+#define PI 3.1415926535897932f
+#define RANDOM_UINT_MAX 65535
+
+
+static unsigned long sRandomSeed = 0;
+
+static void seedRandom(unsigned long seed)
+{
+    sRandomSeed = seed;
+}
+
+static unsigned long randomUInt()
+{
+    sRandomSeed = sRandomSeed * 0x343fd + 0x269ec3;
+    return sRandomSeed >> 16;
+}
+
+
+// Capped conversion from float to fixed.
+static long floatToFixed(float value)
+{
+    if (value < -32768) value = -32768;
+    if (value > 32767) value = 32767;
+    return (long)(value * 65536);
+}
+
+#define FIXED(value) floatToFixed(value)
+
+
+// Definition of one GL object in this demo.
+typedef struct {
+    /* Vertex array and color array are enabled for all objects, so their
+     * pointers must always be valid and non-NULL. Normal array is not
+     * used by the ground plane, so when its pointer is NULL then normal
+     * array usage is disabled.
+     *
+     * Vertex array is supposed to use GL_FIXED datatype and stride 0
+     * (i.e. tightly packed array). Color array is supposed to have 4
+     * components per color with GL_UNSIGNED_BYTE datatype and stride 0.
+     * Normal array is supposed to use GL_FIXED datatype and stride 0.
+     */
+    GLfixed *vertexArray;
+    GLubyte *colorArray;
+    GLfixed *normalArray;
+    GLint vertexComponents;
+    GLsizei count;
+} GLOBJECT;
+
+
+static long sStartTick = 0;
+static long sTick = 0;
+
+static int sCurrentCamTrack = 0;
+static long sCurrentCamTrackStartTick = 0;
+static long sNextCamTrackStartTick = 0x7fffffff;
+
+static GLOBJECT *sSuperShapeObjects[SUPERSHAPE_COUNT] = { NULL };
+static GLOBJECT *sGroundPlane = NULL;
+
+
+typedef struct {
+    float x, y, z;
+} VECTOR3;
+
+
+static void freeGLObject(GLOBJECT *object)
+{
+    if (object == NULL)
+        return;
+    free(object->normalArray);
+    free(object->colorArray);
+    free(object->vertexArray);
+    free(object);
+}
+
+
+static GLOBJECT * newGLObject(long vertices, int vertexComponents,
+                              int useNormalArray)
+{
+    GLOBJECT *result;
+    result = (GLOBJECT *)malloc(sizeof(GLOBJECT));
+    if (result == NULL)
+        return NULL;
+    result->count = vertices;
+    result->vertexComponents = vertexComponents;
+    result->vertexArray = (GLfixed *)malloc(vertices * vertexComponents *
+                                            sizeof(GLfixed));
+    result->colorArray = (GLubyte *)malloc(vertices * 4 * sizeof(GLubyte));
+    if (useNormalArray)
+    {
+        result->normalArray = (GLfixed *)malloc(vertices * 3 *
+                                                sizeof(GLfixed));
+    }
+    else
+        result->normalArray = NULL;
+    if (result->vertexArray == NULL ||
+        result->colorArray == NULL ||
+        (useNormalArray && result->normalArray == NULL))
+    {
+        freeGLObject(result);
+        return NULL;
+    }
+    return result;
+}
+
+
+static void drawGLObject(GLOBJECT *object)
+{
+    assert(object != NULL);
+
+    glVertexPointer(object->vertexComponents, GL_FIXED,
+                    0, object->vertexArray);
+    glColorPointer(4, GL_UNSIGNED_BYTE, 0, object->colorArray);
+
+    // Already done in initialization:
+    //glEnableClientState(GL_VERTEX_ARRAY);
+    //glEnableClientState(GL_COLOR_ARRAY);
+
+    if (object->normalArray)
+    {
+        glNormalPointer(GL_FIXED, 0, object->normalArray);
+        glEnableClientState(GL_NORMAL_ARRAY);
+    }
+    else
+        glDisableClientState(GL_NORMAL_ARRAY);
+    glDrawArrays(GL_TRIANGLES, 0, object->count);
+}
+
+
+static void vector3Sub(VECTOR3 *dest, VECTOR3 *v1, VECTOR3 *v2)
+{
+    dest->x = v1->x - v2->x;
+    dest->y = v1->y - v2->y;
+    dest->z = v1->z - v2->z;
+}
+
+
+static void superShapeMap(VECTOR3 *point, float r1, float r2, float t, float p)
+{
+    // sphere-mapping of supershape parameters
+    point->x = (float)(cos(t) * cos(p) / r1 / r2);
+    point->y = (float)(sin(t) * cos(p) / r1 / r2);
+    point->z = (float)(sin(p) / r2);
+}
+
+
+static float ssFunc(const float t, const float *p)
+{
+    return (float)(pow(pow(fabs(cos(p[0] * t / 4)) / p[1], p[4]) +
+                       pow(fabs(sin(p[0] * t / 4)) / p[2], p[5]), 1 / p[3]));
+}
+
+
+// Creates and returns a supershape object.
+// Based on Paul Bourke's POV-Ray implementation.
+// http://astronomy.swin.edu.au/~pbourke/povray/supershape/
+static GLOBJECT * createSuperShape(const float *params)
+{
+    const int resol1 = (int)params[SUPERSHAPE_PARAMS - 3];
+    const int resol2 = (int)params[SUPERSHAPE_PARAMS - 2];
+    // latitude 0 to pi/2 for no mirrored bottom
+    // (latitudeBegin==0 for -pi/2 to pi/2 originally)
+    const int latitudeBegin = resol2 / 4;
+    const int latitudeEnd = resol2 / 2;    // non-inclusive
+    const int longitudeCount = resol1;
+    const int latitudeCount = latitudeEnd - latitudeBegin;
+    const long triangleCount = longitudeCount * latitudeCount * 2;
+    const long vertices = triangleCount * 3;
+    GLOBJECT *result;
+    float baseColor[3];
+    int a, longitude, latitude;
+    long currentVertex, currentQuad;
+
+    result = newGLObject(vertices, 3, 1);
+    if (result == NULL)
+        return NULL;
+
+    for (a = 0; a < 3; ++a)
+        baseColor[a] = ((randomUInt() % 155) + 100) / 255.f;
+
+    currentQuad = 0;
+    currentVertex = 0;
+
+    // longitude -pi to pi
+    for (longitude = 0; longitude < longitudeCount; ++longitude)
+    {
+
+        // latitude 0 to pi/2
+        for (latitude = latitudeBegin; latitude < latitudeEnd; ++latitude)
+        {
+            float t1 = -PI + longitude * 2 * PI / resol1;
+            float t2 = -PI + (longitude + 1) * 2 * PI / resol1;
+            float p1 = -PI / 2 + latitude * 2 * PI / resol2;
+            float p2 = -PI / 2 + (latitude + 1) * 2 * PI / resol2;
+            float r0, r1, r2, r3;
+
+            r0 = ssFunc(t1, params);
+            r1 = ssFunc(p1, &params[6]);
+            r2 = ssFunc(t2, params);
+            r3 = ssFunc(p2, &params[6]);
+
+            if (r0 != 0 && r1 != 0 && r2 != 0 && r3 != 0)
+            {
+                VECTOR3 pa, pb, pc, pd;
+                VECTOR3 v1, v2, n;
+                float ca;
+                int i;
+                //float lenSq, invLenSq;
+
+                superShapeMap(&pa, r0, r1, t1, p1);
+                superShapeMap(&pb, r2, r1, t2, p1);
+                superShapeMap(&pc, r2, r3, t2, p2);
+                superShapeMap(&pd, r0, r3, t1, p2);
+
+                // kludge to set lower edge of the object to fixed level
+                if (latitude == latitudeBegin + 1)
+                    pa.z = pb.z = 0;
+
+                vector3Sub(&v1, &pb, &pa);
+                vector3Sub(&v2, &pd, &pa);
+
+                // Calculate normal with cross product.
+                /*   i    j    k      i    j
+                 * v1.x v1.y v1.z | v1.x v1.y
+                 * v2.x v2.y v2.z | v2.x v2.y
+                 */
+
+                n.x = v1.y * v2.z - v1.z * v2.y;
+                n.y = v1.z * v2.x - v1.x * v2.z;
+                n.z = v1.x * v2.y - v1.y * v2.x;
+
+                /* Pre-normalization of the normals is disabled here because
+                 * they will be normalized anyway later due to automatic
+                 * normalization (GL_NORMALIZE). It is enabled because the
+                 * objects are scaled with glScale.
+                 */
+                /*
+                lenSq = n.x * n.x + n.y * n.y + n.z * n.z;
+                invLenSq = (float)(1 / sqrt(lenSq));
+                n.x *= invLenSq;
+                n.y *= invLenSq;
+                n.z *= invLenSq;
+                */
+
+                ca = pa.z + 0.5f;
+
+                for (i = currentVertex * 3;
+                     i < (currentVertex + 6) * 3;
+                     i += 3)
+                {
+                    result->normalArray[i] = FIXED(n.x);
+                    result->normalArray[i + 1] = FIXED(n.y);
+                    result->normalArray[i + 2] = FIXED(n.z);
+                }
+                for (i = currentVertex * 4;
+                     i < (currentVertex + 6) * 4;
+                     i += 4)
+                {
+                    int a, color[3];
+                    for (a = 0; a < 3; ++a)
+                    {
+                        color[a] = (int)(ca * baseColor[a] * 255);
+                        if (color[a] > 255) color[a] = 255;
+                    }
+                    result->colorArray[i] = (GLubyte)color[0];
+                    result->colorArray[i + 1] = (GLubyte)color[1];
+                    result->colorArray[i + 2] = (GLubyte)color[2];
+                    result->colorArray[i + 3] = 0;
+                }
+                result->vertexArray[currentVertex * 3] = FIXED(pa.x);
+                result->vertexArray[currentVertex * 3 + 1] = FIXED(pa.y);
+                result->vertexArray[currentVertex * 3 + 2] = FIXED(pa.z);
+                ++currentVertex;
+                result->vertexArray[currentVertex * 3] = FIXED(pb.x);
+                result->vertexArray[currentVertex * 3 + 1] = FIXED(pb.y);
+                result->vertexArray[currentVertex * 3 + 2] = FIXED(pb.z);
+                ++currentVertex;
+                result->vertexArray[currentVertex * 3] = FIXED(pd.x);
+                result->vertexArray[currentVertex * 3 + 1] = FIXED(pd.y);
+                result->vertexArray[currentVertex * 3 + 2] = FIXED(pd.z);
+                ++currentVertex;
+                result->vertexArray[currentVertex * 3] = FIXED(pb.x);
+                result->vertexArray[currentVertex * 3 + 1] = FIXED(pb.y);
+                result->vertexArray[currentVertex * 3 + 2] = FIXED(pb.z);
+                ++currentVertex;
+                result->vertexArray[currentVertex * 3] = FIXED(pc.x);
+                result->vertexArray[currentVertex * 3 + 1] = FIXED(pc.y);
+                result->vertexArray[currentVertex * 3 + 2] = FIXED(pc.z);
+                ++currentVertex;
+                result->vertexArray[currentVertex * 3] = FIXED(pd.x);
+                result->vertexArray[currentVertex * 3 + 1] = FIXED(pd.y);
+                result->vertexArray[currentVertex * 3 + 2] = FIXED(pd.z);
+                ++currentVertex;
+            } // r0 && r1 && r2 && r3
+            ++currentQuad;
+        } // latitude
+    } // longitude
+
+    // Set number of vertices in object to the actual amount created.
+    result->count = currentVertex;
+
+    return result;
+}
+
+
+static GLOBJECT * createGroundPlane()
+{
+    const int scale = 4;
+    const int yBegin = -15, yEnd = 15;    // ends are non-inclusive
+    const int xBegin = -15, xEnd = 15;
+    const long triangleCount = (yEnd - yBegin) * (xEnd - xBegin) * 2;
+    const long vertices = triangleCount * 3;
+    GLOBJECT *result;
+    int x, y;
+    long currentVertex, currentQuad;
+
+    result = newGLObject(vertices, 2, 0);
+    if (result == NULL)
+        return NULL;
+
+    currentQuad = 0;
+    currentVertex = 0;
+
+    for (y = yBegin; y < yEnd; ++y)
+    {
+        for (x = xBegin; x < xEnd; ++x)
+        {
+            GLubyte color;
+            int i, a;
+            color = (GLubyte)((randomUInt() & 0x5f) + 81);  // 101 1111
+            for (i = currentVertex * 4; i < (currentVertex + 6) * 4; i += 4)
+            {
+                result->colorArray[i] = color;
+                result->colorArray[i + 1] = color;
+                result->colorArray[i + 2] = color;
+                result->colorArray[i + 3] = 0;
+            }
+
+            // Axis bits for quad triangles:
+            // x: 011100 (0x1c), y: 110001 (0x31)  (clockwise)
+            // x: 001110 (0x0e), y: 100011 (0x23)  (counter-clockwise)
+            for (a = 0; a < 6; ++a)
+            {
+                const int xm = x + ((0x1c >> a) & 1);
+                const int ym = y + ((0x31 >> a) & 1);
+                const float m = (float)(cos(xm * 2) * sin(ym * 4) * 0.75f);
+                result->vertexArray[currentVertex * 2] =
+                    FIXED(xm * scale + m);
+                result->vertexArray[currentVertex * 2 + 1] =
+                    FIXED(ym * scale + m);
+                ++currentVertex;
+            }
+            ++currentQuad;
+        }
+    }
+    return result;
+}
+
+
+static void drawGroundPlane()
+{
+    glDisable(GL_CULL_FACE);
+    glDisable(GL_DEPTH_TEST);
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_ZERO, GL_SRC_COLOR);
+    glDisable(GL_LIGHTING);
+
+    drawGLObject(sGroundPlane);
+
+    glEnable(GL_LIGHTING);
+    glDisable(GL_BLEND);
+    glEnable(GL_DEPTH_TEST);
+}
+
+
+static void drawFadeQuad()
+{
+    static const GLfixed quadVertices[] = {
+        -0x10000, -0x10000,
+         0x10000, -0x10000,
+        -0x10000,  0x10000,
+         0x10000, -0x10000,
+         0x10000,  0x10000,
+        -0x10000,  0x10000
+    };
+
+    const int beginFade = sTick - sCurrentCamTrackStartTick;
+    const int endFade = sNextCamTrackStartTick - sTick;
+    const int minFade = beginFade < endFade ? beginFade : endFade;
+
+    if (minFade < 1024)
+    {
+        const GLfixed fadeColor = minFade << 6;
+        glColor4x(fadeColor, fadeColor, fadeColor, 0);
+
+        glDisable(GL_DEPTH_TEST);
+        glEnable(GL_BLEND);
+        glBlendFunc(GL_ZERO, GL_SRC_COLOR);
+        glDisable(GL_LIGHTING);
+
+        glMatrixMode(GL_MODELVIEW);
+        glLoadIdentity();
+
+        glMatrixMode(GL_PROJECTION);
+        glLoadIdentity();
+
+        glDisableClientState(GL_COLOR_ARRAY);
+        glDisableClientState(GL_NORMAL_ARRAY);
+        glVertexPointer(2, GL_FIXED, 0, quadVertices);
+        glDrawArrays(GL_TRIANGLES, 0, 6);
+
+        glEnableClientState(GL_COLOR_ARRAY);
+
+        glMatrixMode(GL_MODELVIEW);
+
+        glEnable(GL_LIGHTING);
+        glDisable(GL_BLEND);
+        glEnable(GL_DEPTH_TEST);
+    }
+}
+
+
+// Called from the app framework.
+void appInit()
+{
+    int a;
+
+    glEnable(GL_NORMALIZE);
+    glEnable(GL_DEPTH_TEST);
+    glDisable(GL_CULL_FACE);
+    glShadeModel(GL_FLAT);
+
+    glEnable(GL_LIGHTING);
+    glEnable(GL_LIGHT0);
+    glEnable(GL_LIGHT1);
+    glEnable(GL_LIGHT2);
+
+    glEnableClientState(GL_VERTEX_ARRAY);
+    glEnableClientState(GL_COLOR_ARRAY);
+
+    seedRandom(15);
+
+    for (a = 0; a < SUPERSHAPE_COUNT; ++a)
+    {
+        sSuperShapeObjects[a] = createSuperShape(sSuperShapeParams[a]);
+        assert(sSuperShapeObjects[a] != NULL);
+    }
+    sGroundPlane = createGroundPlane();
+    assert(sGroundPlane != NULL);
+}
+
+
+// Called from the app framework.
+void appDeinit()
+{
+    int a;
+    for (a = 0; a < SUPERSHAPE_COUNT; ++a)
+        freeGLObject(sSuperShapeObjects[a]);
+    freeGLObject(sGroundPlane);
+}
+
+
+static void gluPerspective(GLfloat fovy, GLfloat aspect,
+                           GLfloat zNear, GLfloat zFar)
+{
+    GLfloat xmin, xmax, ymin, ymax;
+
+    ymax = zNear * (GLfloat)tan(fovy * PI / 360);
+    ymin = -ymax;
+    xmin = ymin * aspect;
+    xmax = ymax * aspect;
+
+    glFrustumx((GLfixed)(xmin * 65536), (GLfixed)(xmax * 65536),
+               (GLfixed)(ymin * 65536), (GLfixed)(ymax * 65536),
+               (GLfixed)(zNear * 65536), (GLfixed)(zFar * 65536));
+}
+
+
+static void prepareFrame(int width, int height)
+{
+    glViewport(0, 0, width, height);
+
+    glClearColorx((GLfixed)(0.1f * 65536),
+                  (GLfixed)(0.2f * 65536),
+                  (GLfixed)(0.3f * 65536), 0x10000);
+    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+    gluPerspective(45, (float)width / height, 0.5f, 150);
+
+    glMatrixMode(GL_MODELVIEW);
+
+    glLoadIdentity();
+}
+
+
+static void configureLightAndMaterial()
+{
+    static GLfixed light0Position[] = { -0x40000, 0x10000, 0x10000, 0 };
+    static GLfixed light0Diffuse[] = { 0x10000, 0x6666, 0, 0x10000 };
+    static GLfixed light1Position[] = { 0x10000, -0x20000, -0x10000, 0 };
+    static GLfixed light1Diffuse[] = { 0x11eb, 0x23d7, 0x5999, 0x10000 };
+    static GLfixed light2Position[] = { -0x10000, 0, -0x40000, 0 };
+    static GLfixed light2Diffuse[] = { 0x11eb, 0x2b85, 0x23d7, 0x10000 };
+    static GLfixed materialSpecular[] = { 0x10000, 0x10000, 0x10000, 0x10000 };
+
+    glLightxv(GL_LIGHT0, GL_POSITION, light0Position);
+    glLightxv(GL_LIGHT0, GL_DIFFUSE, light0Diffuse);
+    glLightxv(GL_LIGHT1, GL_POSITION, light1Position);
+    glLightxv(GL_LIGHT1, GL_DIFFUSE, light1Diffuse);
+    glLightxv(GL_LIGHT2, GL_POSITION, light2Position);
+    glLightxv(GL_LIGHT2, GL_DIFFUSE, light2Diffuse);
+    glMaterialxv(GL_FRONT_AND_BACK, GL_SPECULAR, materialSpecular);
+
+    glMaterialx(GL_FRONT_AND_BACK, GL_SHININESS, 60 << 16);
+    glEnable(GL_COLOR_MATERIAL);
+}
+
+
+static void drawModels(float zScale)
+{
+    const int translationScale = 9;
+    int x, y;
+
+    seedRandom(9);
+
+    glScalex(1 << 16, 1 << 16, (GLfixed)(zScale * 65536));
+
+    for (y = -5; y <= 5; ++y)
+    {
+        for (x = -5; x <= 5; ++x)
+        {
+            float buildingScale;
+            GLfixed fixedScale;
+
+            int curShape = randomUInt() % SUPERSHAPE_COUNT;
+            buildingScale = sSuperShapeParams[curShape][SUPERSHAPE_PARAMS - 1];
+            fixedScale = (GLfixed)(buildingScale * 65536);
+
+            glPushMatrix();
+            glTranslatex((x * translationScale) * 65536,
+                         (y * translationScale) * 65536,
+                         0);
+            glRotatex((GLfixed)((randomUInt() % 360) << 16), 0, 0, 1 << 16);
+            glScalex(fixedScale, fixedScale, fixedScale);
+
+            drawGLObject(sSuperShapeObjects[curShape]);
+            glPopMatrix();
+        }
+    }
+
+    for (x = -2; x <= 2; ++x)
+    {
+        const int shipScale100 = translationScale * 500;
+        const int offs100 = x * shipScale100 + (sTick % shipScale100);
+        float offs = offs100 * 0.01f;
+        GLfixed fixedOffs = (GLfixed)(offs * 65536);
+        glPushMatrix();
+        glTranslatex(fixedOffs, -4 * 65536, 2 << 16);
+        drawGLObject(sSuperShapeObjects[SUPERSHAPE_COUNT - 1]);
+        glPopMatrix();
+        glPushMatrix();
+        glTranslatex(-4 * 65536, fixedOffs, 4 << 16);
+        glRotatex(90 << 16, 0, 0, 1 << 16);
+        drawGLObject(sSuperShapeObjects[SUPERSHAPE_COUNT - 1]);
+        glPopMatrix();
+    }
+}
+
+
+/* Following gluLookAt implementation is adapted from the
+ * Mesa 3D Graphics library. http://www.mesa3d.org
+ */
+static void gluLookAt(GLfloat eyex, GLfloat eyey, GLfloat eyez,
+	              GLfloat centerx, GLfloat centery, GLfloat centerz,
+	              GLfloat upx, GLfloat upy, GLfloat upz)
+{
+    GLfloat m[16];
+    GLfloat x[3], y[3], z[3];
+    GLfloat mag;
+
+    /* Make rotation matrix */
+
+    /* Z vector */
+    z[0] = eyex - centerx;
+    z[1] = eyey - centery;
+    z[2] = eyez - centerz;
+    mag = (float)sqrt(z[0] * z[0] + z[1] * z[1] + z[2] * z[2]);
+    if (mag) {			/* mpichler, 19950515 */
+        z[0] /= mag;
+        z[1] /= mag;
+        z[2] /= mag;
+    }
+
+    /* Y vector */
+    y[0] = upx;
+    y[1] = upy;
+    y[2] = upz;
+
+    /* X vector = Y cross Z */
+    x[0] = y[1] * z[2] - y[2] * z[1];
+    x[1] = -y[0] * z[2] + y[2] * z[0];
+    x[2] = y[0] * z[1] - y[1] * z[0];
+
+    /* Recompute Y = Z cross X */
+    y[0] = z[1] * x[2] - z[2] * x[1];
+    y[1] = -z[0] * x[2] + z[2] * x[0];
+    y[2] = z[0] * x[1] - z[1] * x[0];
+
+    /* mpichler, 19950515 */
+    /* cross product gives area of parallelogram, which is < 1.0 for
+     * non-perpendicular unit-length vectors; so normalize x, y here
+     */
+
+    mag = (float)sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]);
+    if (mag) {
+        x[0] /= mag;
+        x[1] /= mag;
+        x[2] /= mag;
+    }
+
+    mag = (float)sqrt(y[0] * y[0] + y[1] * y[1] + y[2] * y[2]);
+    if (mag) {
+        y[0] /= mag;
+        y[1] /= mag;
+        y[2] /= mag;
+    }
+
+#define M(row,col)  m[col*4+row]
+    M(0, 0) = x[0];
+    M(0, 1) = x[1];
+    M(0, 2) = x[2];
+    M(0, 3) = 0.0;
+    M(1, 0) = y[0];
+    M(1, 1) = y[1];
+    M(1, 2) = y[2];
+    M(1, 3) = 0.0;
+    M(2, 0) = z[0];
+    M(2, 1) = z[1];
+    M(2, 2) = z[2];
+    M(2, 3) = 0.0;
+    M(3, 0) = 0.0;
+    M(3, 1) = 0.0;
+    M(3, 2) = 0.0;
+    M(3, 3) = 1.0;
+#undef M
+    {
+        int a;
+        GLfixed fixedM[16];
+        for (a = 0; a < 16; ++a)
+            fixedM[a] = (GLfixed)(m[a] * 65536);
+        glMultMatrixx(fixedM);
+    }
+
+    /* Translate Eye to Origin */
+    glTranslatex((GLfixed)(-eyex * 65536),
+                 (GLfixed)(-eyey * 65536),
+                 (GLfixed)(-eyez * 65536));
+}
+
+
+static void camTrack()
+{
+    float lerp[5];
+    float eX, eY, eZ, cX, cY, cZ;
+    float trackPos;
+    CAMTRACK *cam;
+    long currentCamTick;
+    int a;
+
+    if (sNextCamTrackStartTick <= sTick)
+    {
+        ++sCurrentCamTrack;
+        sCurrentCamTrackStartTick = sNextCamTrackStartTick;
+    }
+    sNextCamTrackStartTick = sCurrentCamTrackStartTick +
+                             sCamTracks[sCurrentCamTrack].len * CAMTRACK_LEN;
+
+    cam = &sCamTracks[sCurrentCamTrack];
+    currentCamTick = sTick - sCurrentCamTrackStartTick;
+    trackPos = (float)currentCamTick / (CAMTRACK_LEN * cam->len);
+
+    for (a = 0; a < 5; ++a)
+        lerp[a] = (cam->src[a] + cam->dest[a] * trackPos) * 0.01f;
+
+    if (cam->dist)
+    {
+        float dist = cam->dist * 0.1f;
+        cX = lerp[0];
+        cY = lerp[1];
+        cZ = lerp[2];
+        eX = cX - (float)cos(lerp[3]) * dist;
+        eY = cY - (float)sin(lerp[3]) * dist;
+        eZ = cZ - lerp[4];
+    }
+    else
+    {
+        eX = lerp[0];
+        eY = lerp[1];
+        eZ = lerp[2];
+        cX = eX + (float)cos(lerp[3]);
+        cY = eY + (float)sin(lerp[3]);
+        cZ = eZ + lerp[4];
+    }
+    gluLookAt(eX, eY, eZ, cX, cY, cZ, 0, 0, 1);
+}
+
+
+// Called from the app framework.
+/* The tick is current time in milliseconds, width and height
+ * are the image dimensions to be rendered.
+ */
+void appRender(long tick, int width, int height)
+{
+    if (sStartTick == 0)
+        sStartTick = tick;
+    if (!gAppAlive)
+        return;
+
+    // Actual tick value is "blurred" a little bit.
+    sTick = (sTick + tick - sStartTick) >> 1;
+
+    // Terminate application after running through the demonstration once.
+    if (sTick >= RUN_LENGTH)
+    {
+        gAppAlive = 0;
+        return;
+    }
+
+    // Prepare OpenGL ES for rendering of the frame.
+    prepareFrame(width, height);
+
+    // Update the camera position and set the lookat.
+    camTrack();
+
+    // Configure environment.
+    configureLightAndMaterial();
+
+    // Draw the reflection by drawing models with negated Z-axis.
+    glPushMatrix();
+    drawModels(-1);
+    glPopMatrix();
+
+    // Blend the ground plane to the window.
+    drawGroundPlane();
+
+    // Draw all the models normally.
+    drawModels(1);
+
+    // Draw fade quad over whole window (when changing cameras).
+    drawFadeQuad();
+}
diff --git a/tests/ndkSanAngeles/src/main/jni/importgl.c b/tests/ndkSanAngeles/src/main/jni/importgl.c
new file mode 100644
index 0000000..f501636
--- /dev/null
+++ b/tests/ndkSanAngeles/src/main/jni/importgl.c
@@ -0,0 +1,168 @@
+/* San Angeles Observation OpenGL ES version example
+ * Copyright 2004-2005 Jetro Lauha
+ * All rights reserved.
+ * Web: http://iki.fi/jetro/
+ *
+ * This source is free software; you can redistribute it and/or
+ * modify it under the terms of EITHER:
+ *   (1) The GNU Lesser General Public License as published by the Free
+ *       Software Foundation; either version 2.1 of the License, or (at
+ *       your option) any later version. The text of the GNU Lesser
+ *       General Public License is included with this source in the
+ *       file LICENSE-LGPL.txt.
+ *   (2) The BSD-style license that is included with this source in
+ *       the file LICENSE-BSD.txt.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files
+ * LICENSE-LGPL.txt and LICENSE-BSD.txt for more details.
+ *
+ * $Id: importgl.c,v 1.4 2005/02/08 18:42:55 tonic Exp $
+ * $Revision: 1.4 $
+ */
+
+#undef WIN32
+#undef LINUX
+#ifdef _MSC_VER
+// Desktop or mobile Win32 environment:
+#define WIN32
+#else
+// Linux environment:
+#define LINUX
+#endif
+
+#ifndef DISABLE_IMPORTGL
+
+#if defined(WIN32)
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <tchar.h>
+static HMODULE sGLESDLL = NULL;
+#endif // WIN32
+
+#ifdef LINUX
+#include <stdlib.h>
+#include <dlfcn.h>
+static void *sGLESSO = NULL;
+#endif // LINUX
+
+#endif /* DISABLE_IMPORTGL */
+
+#define IMPORTGL_NO_FNPTR_DEFS
+#define IMPORTGL_API
+#define IMPORTGL_FNPTRINIT = NULL
+#include "importgl.h"
+
+
+/* Imports function pointers to selected function calls in OpenGL ES Common
+ * or Common Lite profile DLL or shared object. The function pointers are
+ * stored as global symbols with equivalent function name but prefixed with
+ * "funcPtr_". Standard gl/egl calls are redirected to the function pointers
+ * with preprocessor macros (see importgl.h).
+ */
+int importGLInit()
+{
+    int result = 1;
+
+#ifndef DISABLE_IMPORTGL
+
+#undef IMPORT_FUNC
+
+#ifdef WIN32
+    sGLESDLL = LoadLibrary(_T("libGLES_CM.dll"));
+    if (sGLESDLL == NULL)
+        sGLESDLL = LoadLibrary(_T("libGLES_CL.dll"));
+    if (sGLESDLL == NULL)
+        return 0;   // Cannot find OpenGL ES Common or Common Lite DLL.
+
+    /* The following fetches address to each egl & gl function call
+     * and stores it to the related function pointer. Casting through
+     * void * results in warnings with VC warning level 4, which
+     * could be fixed by casting to the true type for each fetch.
+     */
+#define IMPORT_FUNC(funcName) do { \
+        void *procAddress = (void *)GetProcAddress(sGLESDLL, _T(#funcName)); \
+        if (procAddress == NULL) result = 0; \
+        *((void **)&FNPTR(funcName)) = procAddress; } while (0)
+#endif // WIN32
+
+#ifdef LINUX
+#ifdef ANDROID_NDK
+    sGLESSO = dlopen("libGLESv1_CM.so", RTLD_NOW);
+#else /* !ANDROID_NDK */
+    sGLESSO = dlopen("libGLES_CM.so", RTLD_NOW);
+    if (sGLESSO == NULL)
+        sGLESSO = dlopen("libGLES_CL.so", RTLD_NOW);
+#endif /* !ANDROID_NDK */
+    if (sGLESSO == NULL)
+        return 0;   // Cannot find OpenGL ES Common or Common Lite SO.
+
+#define IMPORT_FUNC(funcName) do { \
+        void *procAddress = (void *)dlsym(sGLESSO, #funcName); \
+        if (procAddress == NULL) result = 0; \
+        *((void **)&FNPTR(funcName)) = procAddress; } while (0)
+#endif // LINUX
+
+#ifndef ANDROID_NDK
+    IMPORT_FUNC(eglChooseConfig);
+    IMPORT_FUNC(eglCreateContext);
+    IMPORT_FUNC(eglCreateWindowSurface);
+    IMPORT_FUNC(eglDestroyContext);
+    IMPORT_FUNC(eglDestroySurface);
+    IMPORT_FUNC(eglGetConfigAttrib);
+    IMPORT_FUNC(eglGetConfigs);
+    IMPORT_FUNC(eglGetDisplay);
+    IMPORT_FUNC(eglGetError);
+    IMPORT_FUNC(eglInitialize);
+    IMPORT_FUNC(eglMakeCurrent);
+    IMPORT_FUNC(eglSwapBuffers);
+    IMPORT_FUNC(eglTerminate);
+#endif /* !ANDROID_NDK */
+
+    IMPORT_FUNC(glBlendFunc);
+    IMPORT_FUNC(glClear);
+    IMPORT_FUNC(glClearColorx);
+    IMPORT_FUNC(glColor4x);
+    IMPORT_FUNC(glColorPointer);
+    IMPORT_FUNC(glDisable);
+    IMPORT_FUNC(glDisableClientState);
+    IMPORT_FUNC(glDrawArrays);
+    IMPORT_FUNC(glEnable);
+    IMPORT_FUNC(glEnableClientState);
+    IMPORT_FUNC(glFrustumx);
+    IMPORT_FUNC(glGetError);
+    IMPORT_FUNC(glLightxv);
+    IMPORT_FUNC(glLoadIdentity);
+    IMPORT_FUNC(glMaterialx);
+    IMPORT_FUNC(glMaterialxv);
+    IMPORT_FUNC(glMatrixMode);
+    IMPORT_FUNC(glMultMatrixx);
+    IMPORT_FUNC(glNormalPointer);
+    IMPORT_FUNC(glPopMatrix);
+    IMPORT_FUNC(glPushMatrix);
+    IMPORT_FUNC(glRotatex);
+    IMPORT_FUNC(glScalex);
+    IMPORT_FUNC(glShadeModel);
+    IMPORT_FUNC(glTranslatex);
+    IMPORT_FUNC(glVertexPointer);
+    IMPORT_FUNC(glViewport);
+
+#endif /* DISABLE_IMPORTGL */
+
+    return result;
+}
+
+
+void importGLDeinit()
+{
+#ifndef DISABLE_IMPORTGL
+#ifdef WIN32
+    FreeLibrary(sGLESDLL);
+#endif
+
+#ifdef LINUX
+    dlclose(sGLESSO);
+#endif
+#endif /* DISABLE_IMPORTGL */
+}
diff --git a/tests/ndkSanAngeles/src/main/jni/importgl.h b/tests/ndkSanAngeles/src/main/jni/importgl.h
new file mode 100644
index 0000000..a19a3a7
--- /dev/null
+++ b/tests/ndkSanAngeles/src/main/jni/importgl.h
@@ -0,0 +1,171 @@
+/* San Angeles Observation OpenGL ES version example
+ * Copyright 2004-2005 Jetro Lauha
+ * All rights reserved.
+ * Web: http://iki.fi/jetro/
+ *
+ * This source is free software; you can redistribute it and/or
+ * modify it under the terms of EITHER:
+ *   (1) The GNU Lesser General Public License as published by the Free
+ *       Software Foundation; either version 2.1 of the License, or (at
+ *       your option) any later version. The text of the GNU Lesser
+ *       General Public License is included with this source in the
+ *       file LICENSE-LGPL.txt.
+ *   (2) The BSD-style license that is included with this source in
+ *       the file LICENSE-BSD.txt.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files
+ * LICENSE-LGPL.txt and LICENSE-BSD.txt for more details.
+ *
+ * $Id: importgl.h,v 1.4 2005/02/24 20:29:33 tonic Exp $
+ * $Revision: 1.4 $
+ */
+
+#ifndef IMPORTGL_H_INCLUDED
+#define IMPORTGL_H_INCLUDED
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#include <GLES/gl.h>
+#ifndef ANDROID_NDK
+#include <GLES/egl.h>
+#endif /* !ANDROID_NDK */
+
+/* Dynamically fetches pointers to the egl & gl functions.
+ * Should be called once on application initialization.
+ * Returns non-zero on success and 0 on failure.
+ */
+extern int importGLInit();
+
+/* Frees the handle to egl & gl functions library.
+ */
+extern void importGLDeinit();
+
+/* Use DISABLE_IMPORTGL if you want to link the OpenGL ES at
+ * compile/link time and not import it dynamically runtime.
+ */
+#ifndef DISABLE_IMPORTGL
+
+
+#ifndef IMPORTGL_API
+#define IMPORTGL_API extern
+#endif
+#ifndef IMPORTGL_FNPTRINIT
+#define IMPORTGL_FNPTRINIT
+#endif
+
+#define FNDEF(retType, funcName, args) IMPORTGL_API retType (*funcPtr_##funcName) args IMPORTGL_FNPTRINIT
+
+#ifndef ANDROID_NDK
+FNDEF(EGLBoolean, eglChooseConfig, (EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config));
+FNDEF(EGLContext, eglCreateContext, (EGLDisplay dpy, EGLConfig config, EGLContext share_list, const EGLint *attrib_list));
+FNDEF(EGLSurface, eglCreateWindowSurface, (EGLDisplay dpy, EGLConfig config, NativeWindowType window, const EGLint *attrib_list));
+FNDEF(EGLBoolean, eglDestroyContext, (EGLDisplay dpy, EGLContext ctx));
+FNDEF(EGLBoolean, eglDestroySurface, (EGLDisplay dpy, EGLSurface surface));
+FNDEF(EGLBoolean, eglGetConfigAttrib, (EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value));
+FNDEF(EGLBoolean, eglGetConfigs, (EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config));
+FNDEF(EGLDisplay, eglGetDisplay, (NativeDisplayType display));
+FNDEF(EGLint, eglGetError, (void));
+FNDEF(EGLBoolean, eglInitialize, (EGLDisplay dpy, EGLint *major, EGLint *minor));
+FNDEF(EGLBoolean, eglMakeCurrent, (EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx));
+FNDEF(EGLBoolean, eglSwapBuffers, (EGLDisplay dpy, EGLSurface draw));
+FNDEF(EGLBoolean, eglTerminate, (EGLDisplay dpy));
+#endif /* !ANDROID_NDK */
+
+FNDEF(void, glBlendFunc, (GLenum sfactor, GLenum dfactor));
+FNDEF(void, glClear, (GLbitfield mask));
+FNDEF(void, glClearColorx, (GLclampx red, GLclampx green, GLclampx blue, GLclampx alpha));
+FNDEF(void, glColor4x, (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha));
+FNDEF(void, glColorPointer, (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer));
+FNDEF(void, glDisable, (GLenum cap));
+FNDEF(void, glDisableClientState, (GLenum array));
+FNDEF(void, glDrawArrays, (GLenum mode, GLint first, GLsizei count));
+FNDEF(void, glEnable, (GLenum cap));
+FNDEF(void, glEnableClientState, (GLenum array));
+FNDEF(void, glFrustumx, (GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar));
+FNDEF(GLenum, glGetError, (void));
+FNDEF(void, glLightxv, (GLenum light, GLenum pname, const GLfixed *params));
+FNDEF(void, glLoadIdentity, (void));
+FNDEF(void, glMaterialx, (GLenum face, GLenum pname, GLfixed param));
+FNDEF(void, glMaterialxv, (GLenum face, GLenum pname, const GLfixed *params));
+FNDEF(void, glMatrixMode, (GLenum mode));
+FNDEF(void, glMultMatrixx, (const GLfixed *m));
+FNDEF(void, glNormalPointer, (GLenum type, GLsizei stride, const GLvoid *pointer));
+FNDEF(void, glPopMatrix, (void));
+FNDEF(void, glPushMatrix, (void));
+FNDEF(void, glRotatex, (GLfixed angle, GLfixed x, GLfixed y, GLfixed z));
+FNDEF(void, glScalex, (GLfixed x, GLfixed y, GLfixed z));
+FNDEF(void, glShadeModel, (GLenum mode));
+FNDEF(void, glTranslatex, (GLfixed x, GLfixed y, GLfixed z));
+FNDEF(void, glVertexPointer, (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer));
+FNDEF(void, glViewport, (GLint x, GLint y, GLsizei width, GLsizei height));
+
+
+#undef FN
+#define FNPTR(name) funcPtr_##name
+
+#ifndef IMPORTGL_NO_FNPTR_DEFS
+
+// Redirect egl* and gl* function calls to funcPtr_egl* and funcPtr_gl*.
+
+#ifndef ANDROID_NDK
+#define eglChooseConfig         FNPTR(eglChooseConfig)
+#define eglCreateContext        FNPTR(eglCreateContext)
+#define eglCreateWindowSurface  FNPTR(eglCreateWindowSurface)
+#define eglDestroyContext       FNPTR(eglDestroyContext)
+#define eglDestroySurface       FNPTR(eglDestroySurface)
+#define eglGetConfigAttrib      FNPTR(eglGetConfigAttrib)
+#define eglGetConfigs           FNPTR(eglGetConfigs)
+#define eglGetDisplay           FNPTR(eglGetDisplay)
+#define eglGetError             FNPTR(eglGetError)
+#define eglInitialize           FNPTR(eglInitialize)
+#define eglMakeCurrent          FNPTR(eglMakeCurrent)
+#define eglSwapBuffers          FNPTR(eglSwapBuffers)
+#define eglTerminate            FNPTR(eglTerminate)
+#endif /* !ANDROID_NDK */
+
+#define glBlendFunc             FNPTR(glBlendFunc)
+#define glClear                 FNPTR(glClear)
+#define glClearColorx           FNPTR(glClearColorx)
+#define glColor4x               FNPTR(glColor4x)
+#define glColorPointer          FNPTR(glColorPointer)
+#define glDisable               FNPTR(glDisable)
+#define glDisableClientState    FNPTR(glDisableClientState)
+#define glDrawArrays            FNPTR(glDrawArrays)
+#define glEnable                FNPTR(glEnable)
+#define glEnableClientState     FNPTR(glEnableClientState)
+#define glFrustumx              FNPTR(glFrustumx)
+#define glGetError              FNPTR(glGetError)
+#define glLightxv               FNPTR(glLightxv)
+#define glLoadIdentity          FNPTR(glLoadIdentity)
+#define glMaterialx             FNPTR(glMaterialx)
+#define glMaterialxv            FNPTR(glMaterialxv)
+#define glMatrixMode            FNPTR(glMatrixMode)
+#define glMultMatrixx           FNPTR(glMultMatrixx)
+#define glNormalPointer         FNPTR(glNormalPointer)
+#define glPopMatrix             FNPTR(glPopMatrix)
+#define glPushMatrix            FNPTR(glPushMatrix)
+#define glRotatex               FNPTR(glRotatex)
+#define glScalex                FNPTR(glScalex)
+#define glShadeModel            FNPTR(glShadeModel)
+#define glTranslatex            FNPTR(glTranslatex)
+#define glVertexPointer         FNPTR(glVertexPointer)
+#define glViewport              FNPTR(glViewport)
+
+#endif // !IMPORTGL_NO_FNPTR_DEFS
+
+
+#endif // !DISABLE_IMPORTGL
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif // !IMPORTGL_H_INCLUDED
diff --git a/tests/ndkSanAngeles/src/main/jni/shapes.h b/tests/ndkSanAngeles/src/main/jni/shapes.h
new file mode 100644
index 0000000..25ffae8
--- /dev/null
+++ b/tests/ndkSanAngeles/src/main/jni/shapes.h
@@ -0,0 +1,59 @@
+/* San Angeles Observation OpenGL ES version example
+ * Copyright 2004-2005 Jetro Lauha
+ * All rights reserved.
+ * Web: http://iki.fi/jetro/
+ *
+ * This source is free software; you can redistribute it and/or
+ * modify it under the terms of EITHER:
+ *   (1) The GNU Lesser General Public License as published by the Free
+ *       Software Foundation; either version 2.1 of the License, or (at
+ *       your option) any later version. The text of the GNU Lesser
+ *       General Public License is included with this source in the
+ *       file LICENSE-LGPL.txt.
+ *   (2) The BSD-style license that is included with this source in
+ *       the file LICENSE-BSD.txt.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files
+ * LICENSE-LGPL.txt and LICENSE-BSD.txt for more details.
+ *
+ * $Id: shapes.h,v 1.6 2005/01/31 22:15:30 tonic Exp $
+ * $Revision: 1.6 $
+ */
+
+#ifndef SHAPES_H_INCLUDED
+#define SHAPES_H_INCLUDED
+
+
+#define SUPERSHAPE_PARAMS 15
+
+static const float sSuperShapeParams[][SUPERSHAPE_PARAMS] =
+{
+    // m  a     b     n1      n2     n3     m     a     b     n1     n2      n3   res1 res2 scale  (org.res1,res2)
+    { 10, 1,    2,    90,      1,   -45,    8,    1,    1,    -1,     1,  -0.4f,   20,  30, 2 }, // 40, 60
+    { 10, 1,    2,    90,      1,   -45,    4,    1,    1,    10,     1,  -0.4f,   20,  20, 4 }, // 40, 40
+    { 10, 1,    2,    60,      1,   -10,    4,    1,    1,    -1,    -2,  -0.4f,   41,  41, 1 }, // 82, 82
+    {  6, 1,    1,    60,      1,   -70,    8,    1,    1,  0.4f,     3,  0.25f,   20,  20, 1 }, // 40, 40
+    {  4, 1,    1,    30,      1,    20,   12,    1,    1,  0.4f,     3,  0.25f,   10,  30, 1 }, // 20, 60
+    {  8, 1,    1,    30,      1,    -4,    8,    2,    1,    -1,     5,   0.5f,   25,  26, 1 }, // 60, 60
+    { 13, 1,    1,    30,      1,    -4,   13,    1,    1,     1,     5,      1,   30,  30, 6 }, // 60, 60
+    { 10, 1, 1.1f, -0.5f,   0.1f,    70,   60,    1,    1,   -90,     0, -0.25f,   20,  60, 8 }, // 60, 180
+    {  7, 1,    1,    20,  -0.3f, -3.5f,    6,    1,    1,    -1,  4.5f,   0.5f,   10,  20, 4 }, // 60, 80
+    {  4, 1,    1,    10,     10,    10,    4,    1,    1,    10,    10,     10,   10,  20, 1 }, // 20, 40
+    {  4, 1,    1,     1,      1,     1,    4,    1,    1,     1,     1,      1,   10,  10, 2 }, // 10, 10
+    {  1, 1,    1,    38, -0.25f,    19,    4,    1,    1,    10,    10,     10,   10,  15, 2 }, // 20, 40
+    {  2, 1,    1,  0.7f,   0.3f,  0.2f,    3,    1,    1,   100,   100,    100,   10,  25, 2 }, // 20, 50
+    {  6, 1,    1,     1,      1,     1,    3,    1,    1,     1,     1,      1,   30,  30, 2 }, // 60, 60
+    {  3, 1,    1,     1,      1,     1,    6,    1,    1,     2,     1,      1,   10,  20, 2 }, // 20, 40
+    {  6, 1,    1,     6,   5.5f,   100,    6,    1,    1,    25,    10,     10,   30,  20, 2 }, // 60, 40
+    {  3, 1,    1,  0.5f,   1.7f,  1.7f,    2,    1,    1,    10,    10,     10,   20,  20, 2 }, // 40, 40
+    {  5, 1,    1,  0.1f,   1.7f,  1.7f,    1,    1,    1,  0.3f,  0.5f,   0.5f,   20,  20, 4 }, // 40, 40
+    {  2, 1,    1,     6,   5.5f,   100,    6,    1,    1,     4,    10,     10,   10,  22, 1 }, // 40, 40
+    {  6, 1,    1,    -1,     70,  0.1f,    9,    1, 0.5f,   -98, 0.05f,    -45,   20,  30, 4 }, // 60, 91
+    {  6, 1,    1,    -1,     90, -0.1f,    7,    1,    1,    90,  1.3f,     34,   13,  16, 1 }, // 32, 60
+};
+#define SUPERSHAPE_COUNT (sizeof(sSuperShapeParams) / sizeof(sSuperShapeParams[0]))
+
+
+#endif // !SHAPES_H_INCLUDED
diff --git a/tests/ndkSanAngeles/src/main/res/layout/main.xml b/tests/ndkSanAngeles/src/main/res/layout/main.xml
new file mode 100644
index 0000000..3e76662
--- /dev/null
+++ b/tests/ndkSanAngeles/src/main/res/layout/main.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    >
+<TextView  
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content" 
+    android:text="Hello World, DemoActivity"
+    />
+</LinearLayout>
+
diff --git a/tests/ndkSanAngeles/src/main/res/values/strings.xml b/tests/ndkSanAngeles/src/main/res/values/strings.xml
new file mode 100644
index 0000000..574c333
--- /dev/null
+++ b/tests/ndkSanAngeles/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">SanAngeles</string>
+</resources>
diff --git a/tests/overlay1/build.gradle b/tests/overlay1/build.gradle
index c88c4c0..5d126e1 100644
--- a/tests/overlay1/build.gradle
+++ b/tests/overlay1/build.gradle
@@ -3,7 +3,7 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
 
diff --git a/tests/overlay2/build.gradle b/tests/overlay2/build.gradle
index b3d7fcd..845610c 100644
--- a/tests/overlay2/build.gradle
+++ b/tests/overlay2/build.gradle
@@ -3,7 +3,7 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
 
diff --git a/tests/overlay2/src/debug/res/drawable/variant_type_flavor_overlay.png b/tests/overlay2/src/debug/res/drawable/variant_type_flavor_overlay.png
new file mode 100644
index 0000000..0e89381
--- /dev/null
+++ b/tests/overlay2/src/debug/res/drawable/variant_type_flavor_overlay.png
Binary files differ
diff --git a/tests/overlay2/src/instrumentTest/java/com/android/tests/overlay2/MainTest.java b/tests/overlay2/src/instrumentTest/java/com/android/tests/overlay2/MainTest.java
index 263cb76..364640d 100644
--- a/tests/overlay2/src/instrumentTest/java/com/android/tests/overlay2/MainTest.java
+++ b/tests/overlay2/src/instrumentTest/java/com/android/tests/overlay2/MainTest.java
@@ -15,6 +15,7 @@
     private ImageView mTypeOverlayIV;
     private ImageView mFlavorOverlayIV;
     private ImageView mTypeFlavorOverlayIV;
+    private ImageView mVariantTypeFlavorOverlayIV;
 
     /**
      * Creates an {@link ActivityInstrumentationTestCase2} that tests the {@link Main} activity.
@@ -33,6 +34,7 @@
         mTypeOverlayIV = (ImageView) a.findViewById(R.id.type_overlay);
         mFlavorOverlayIV = (ImageView) a.findViewById(R.id.flavor_overlay);
         mTypeFlavorOverlayIV = (ImageView) a.findViewById(R.id.type_flavor_overlay);
+        mVariantTypeFlavorOverlayIV = (ImageView) a.findViewById(R.id.variant_type_flavor_overlay);
     }
 
     /**
@@ -47,6 +49,7 @@
         assertNotNull(mTypeOverlayIV);
         assertNotNull(mFlavorOverlayIV);
         assertNotNull(mTypeFlavorOverlayIV);
+        assertNotNull(mVariantTypeFlavorOverlayIV);
     }
 
     public void testNoOverlay() {
@@ -64,6 +67,10 @@
     public void testTypeFlavorOverlay() {
         pixelLooker(mTypeFlavorOverlayIV, GREEN);
     }
+
+    public void testVariantTypeFlavorOverlay() {
+        pixelLooker(mVariantTypeFlavorOverlayIV, GREEN);
+    }
     
     private void pixelLooker(ImageView iv, int expectedColor) {
         BitmapDrawable d = (BitmapDrawable) iv.getDrawable();
diff --git a/tests/overlay2/src/main/res/drawable/variant_type_flavor_overlay.png b/tests/overlay2/src/main/res/drawable/variant_type_flavor_overlay.png
new file mode 100644
index 0000000..b55e544
--- /dev/null
+++ b/tests/overlay2/src/main/res/drawable/variant_type_flavor_overlay.png
Binary files differ
diff --git a/tests/overlay2/src/main/res/layout/main.xml b/tests/overlay2/src/main/res/layout/main.xml
index 433f390..408c1f4 100644
--- a/tests/overlay2/src/main/res/layout/main.xml
+++ b/tests/overlay2/src/main/res/layout/main.xml
@@ -24,5 +24,10 @@
         android:layout_height="wrap_content"
         android:src="@drawable/type_flavor_overlay"
         android:id="@+id/type_flavor_overlay" />
+    <ImageView
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:src="@drawable/variant_type_flavor_overlay"
+        android:id="@+id/variant_type_flavor_overlay" />
 </LinearLayout>
 
diff --git a/tests/overlay2/src/one/res/drawable/type_flavor_overlay.png b/tests/overlay2/src/one/res/drawable/type_flavor_overlay.png
index b55e544..0e89381 100644
--- a/tests/overlay2/src/one/res/drawable/type_flavor_overlay.png
+++ b/tests/overlay2/src/one/res/drawable/type_flavor_overlay.png
Binary files differ
diff --git a/tests/overlay2/src/one/res/drawable/variant_type_flavor_overlay.png b/tests/overlay2/src/one/res/drawable/variant_type_flavor_overlay.png
new file mode 100644
index 0000000..0e89381
--- /dev/null
+++ b/tests/overlay2/src/one/res/drawable/variant_type_flavor_overlay.png
Binary files differ
diff --git a/tests/overlay2/src/oneDebug/res/drawable/variant_type_flavor_overlay.png b/tests/overlay2/src/oneDebug/res/drawable/variant_type_flavor_overlay.png
new file mode 100644
index 0000000..47e1adf
--- /dev/null
+++ b/tests/overlay2/src/oneDebug/res/drawable/variant_type_flavor_overlay.png
Binary files differ
diff --git a/tests/overlay3/build.gradle b/tests/overlay3/build.gradle
new file mode 100644
index 0000000..1043f5e
--- /dev/null
+++ b/tests/overlay3/build.gradle
@@ -0,0 +1,45 @@
+buildscript {
+    repositories {
+        maven { url '../../../../out/host/gradle/repo' }
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
+    }
+}
+
+apply plugin: 'android'
+
+android {
+    compileSdkVersion 15
+    buildToolsVersion "18.0.1"
+
+    flavorGroups  "pricing", "releaseType"
+
+    sourceSets {
+       beta.setRoot('movedSrc/beta')
+       free.setRoot('movedSrc/free')
+       debug.setRoot('movedSrc/debug')
+       freeBeta.setRoot('movedSrc/freeBeta')
+       freeBetaDebug.setRoot('movedSrc/freeBetaDebug')
+       freeNormal.setRoot('movedSrc/freeNormal')
+    }
+
+    productFlavors {
+
+        beta {
+            flavorGroup "releaseType"
+        }
+
+        normal {
+            flavorGroup "releaseType"
+        }
+
+        free {
+            flavorGroup "pricing"
+        }
+
+        paid {
+            flavorGroup "pricing"
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/overlay3/movedSrc/beta/res/drawable/beta_overlay.png b/tests/overlay3/movedSrc/beta/res/drawable/beta_overlay.png
new file mode 100644
index 0000000..47e1adf
--- /dev/null
+++ b/tests/overlay3/movedSrc/beta/res/drawable/beta_overlay.png
Binary files differ
diff --git a/tests/overlay3/movedSrc/beta/res/drawable/debug_overlay.png b/tests/overlay3/movedSrc/beta/res/drawable/debug_overlay.png
new file mode 100644
index 0000000..b55e544
--- /dev/null
+++ b/tests/overlay3/movedSrc/beta/res/drawable/debug_overlay.png
Binary files differ
diff --git a/tests/overlay3/movedSrc/beta/res/drawable/free_beta_debug_overlay.png b/tests/overlay3/movedSrc/beta/res/drawable/free_beta_debug_overlay.png
new file mode 100644
index 0000000..b55e544
--- /dev/null
+++ b/tests/overlay3/movedSrc/beta/res/drawable/free_beta_debug_overlay.png
Binary files differ
diff --git a/tests/overlay3/movedSrc/beta/res/drawable/free_beta_overlay.png b/tests/overlay3/movedSrc/beta/res/drawable/free_beta_overlay.png
new file mode 100644
index 0000000..b55e544
--- /dev/null
+++ b/tests/overlay3/movedSrc/beta/res/drawable/free_beta_overlay.png
Binary files differ
diff --git a/tests/overlay3/movedSrc/beta/res/drawable/free_overlay.png b/tests/overlay3/movedSrc/beta/res/drawable/free_overlay.png
new file mode 100644
index 0000000..b55e544
--- /dev/null
+++ b/tests/overlay3/movedSrc/beta/res/drawable/free_overlay.png
Binary files differ
diff --git a/tests/overlay3/movedSrc/debug/res/drawable/debug_overlay.png b/tests/overlay3/movedSrc/debug/res/drawable/debug_overlay.png
new file mode 100644
index 0000000..47e1adf
--- /dev/null
+++ b/tests/overlay3/movedSrc/debug/res/drawable/debug_overlay.png
Binary files differ
diff --git a/tests/overlay3/movedSrc/debug/res/drawable/free_beta_debug_overlay.png b/tests/overlay3/movedSrc/debug/res/drawable/free_beta_debug_overlay.png
new file mode 100644
index 0000000..b55e544
--- /dev/null
+++ b/tests/overlay3/movedSrc/debug/res/drawable/free_beta_debug_overlay.png
Binary files differ
diff --git a/tests/overlay3/movedSrc/free/res/drawable/debug_overlay.png b/tests/overlay3/movedSrc/free/res/drawable/debug_overlay.png
new file mode 100644
index 0000000..b55e544
--- /dev/null
+++ b/tests/overlay3/movedSrc/free/res/drawable/debug_overlay.png
Binary files differ
diff --git a/tests/overlay3/movedSrc/free/res/drawable/free_beta_debug_overlay.png b/tests/overlay3/movedSrc/free/res/drawable/free_beta_debug_overlay.png
new file mode 100644
index 0000000..b55e544
--- /dev/null
+++ b/tests/overlay3/movedSrc/free/res/drawable/free_beta_debug_overlay.png
Binary files differ
diff --git a/tests/overlay3/movedSrc/free/res/drawable/free_beta_overlay.png b/tests/overlay3/movedSrc/free/res/drawable/free_beta_overlay.png
new file mode 100644
index 0000000..b55e544
--- /dev/null
+++ b/tests/overlay3/movedSrc/free/res/drawable/free_beta_overlay.png
Binary files differ
diff --git a/tests/overlay3/movedSrc/free/res/drawable/free_overlay.png b/tests/overlay3/movedSrc/free/res/drawable/free_overlay.png
new file mode 100644
index 0000000..47e1adf
--- /dev/null
+++ b/tests/overlay3/movedSrc/free/res/drawable/free_overlay.png
Binary files differ
diff --git a/tests/overlay3/movedSrc/freeBeta/res/drawable/debug_overlay.png b/tests/overlay3/movedSrc/freeBeta/res/drawable/debug_overlay.png
new file mode 100644
index 0000000..b55e544
--- /dev/null
+++ b/tests/overlay3/movedSrc/freeBeta/res/drawable/debug_overlay.png
Binary files differ
diff --git a/tests/overlay3/movedSrc/freeBeta/res/drawable/free_beta_debug_overlay.png b/tests/overlay3/movedSrc/freeBeta/res/drawable/free_beta_debug_overlay.png
new file mode 100644
index 0000000..b55e544
--- /dev/null
+++ b/tests/overlay3/movedSrc/freeBeta/res/drawable/free_beta_debug_overlay.png
Binary files differ
diff --git a/tests/overlay3/movedSrc/freeBeta/res/drawable/free_beta_overlay.png b/tests/overlay3/movedSrc/freeBeta/res/drawable/free_beta_overlay.png
new file mode 100644
index 0000000..47e1adf
--- /dev/null
+++ b/tests/overlay3/movedSrc/freeBeta/res/drawable/free_beta_overlay.png
Binary files differ
diff --git a/tests/overlay3/movedSrc/freeBetaDebug/res/drawable/free_beta_debug_overlay.png b/tests/overlay3/movedSrc/freeBetaDebug/res/drawable/free_beta_debug_overlay.png
new file mode 100644
index 0000000..47e1adf
--- /dev/null
+++ b/tests/overlay3/movedSrc/freeBetaDebug/res/drawable/free_beta_debug_overlay.png
Binary files differ
diff --git a/tests/overlay3/movedSrc/freeNormal/res/drawable/debug_overlay.png b/tests/overlay3/movedSrc/freeNormal/res/drawable/debug_overlay.png
new file mode 100644
index 0000000..b55e544
--- /dev/null
+++ b/tests/overlay3/movedSrc/freeNormal/res/drawable/debug_overlay.png
Binary files differ
diff --git a/tests/overlay3/movedSrc/freeNormal/res/drawable/free_normal_overlay.png b/tests/overlay3/movedSrc/freeNormal/res/drawable/free_normal_overlay.png
new file mode 100644
index 0000000..47e1adf
--- /dev/null
+++ b/tests/overlay3/movedSrc/freeNormal/res/drawable/free_normal_overlay.png
Binary files differ
diff --git a/tests/overlay3/src/instrumentTest/java/com/android/tests/overlay2/MainTest.java b/tests/overlay3/src/instrumentTest/java/com/android/tests/overlay2/MainTest.java
new file mode 100644
index 0000000..a4d6da1
--- /dev/null
+++ b/tests/overlay3/src/instrumentTest/java/com/android/tests/overlay2/MainTest.java
@@ -0,0 +1,98 @@
+package com.android.tests.overlay2;
+
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.widget.ImageView;
+
+
+public class MainTest extends ActivityInstrumentationTestCase2<Main> {
+    
+    private final static int RED = 0xFFFF0000;
+    private final static int GREEN = 0xFF00FF00;
+
+    private ImageView mNoOverlayIV;
+    private ImageView mDebugOverlayIV;
+    private ImageView mBetaOverlayIV;
+    private ImageView mFreeNormalOverlayIV;
+    private ImageView mFreeBetaDebugOverlayIV;
+
+    /**
+     * Creates an {@link ActivityInstrumentationTestCase2} that tests the {@link Main} activity.
+     */
+    public MainTest() {
+        super(Main.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        final Main a = getActivity();
+        // ensure a valid handle to the activity has been returned
+        assertNotNull(a);
+        mNoOverlayIV = (ImageView) a.findViewById(R.id.no_overlay);
+        mDebugOverlayIV = (ImageView) a.findViewById(R.id.debug_overlay);
+        mBetaOverlayIV = (ImageView) a.findViewById(R.id.beta_overlay);
+        mFreeNormalOverlayIV = (ImageView) a.findViewById(R.id.free_normal_overlay);
+        mFreeBetaDebugOverlayIV = (ImageView) a.findViewById(R.id.free_beta_debug_overlay);
+    }
+
+    /**
+     * The name 'test preconditions' is a convention to signal that if this
+     * test doesn't pass, the test case was not set up properly and it might
+     * explain any and all failures in other tests.  This is not guaranteed
+     * to run before other tests, as junit uses reflection to find the tests.
+     */
+    @MediumTest
+    public void testPreconditions() {
+        assertNotNull(mNoOverlayIV);
+        assertNotNull(mDebugOverlayIV);
+        assertNotNull(mFreeBetaDebugOverlayIV);
+        assertNotNull(mFreeNormalOverlayIV);
+        assertNotNull(mFreeBetaDebugOverlayIV);
+    }
+
+    public void testNoOverlay() {
+        pixelLooker(mNoOverlayIV, GREEN);
+    }
+
+    public void testDebugOverlay() {
+        if ("debug".equals(BuildConfig.BUILD_TYPE)) {
+            pixelLooker(mDebugOverlayIV, GREEN);
+        } else {
+            pixelLooker(mDebugOverlayIV, RED);
+        }
+    }
+
+    public void testBetaOverlay() {
+        if ("beta".equals(BuildConfig.FLAVOR2)) {
+            pixelLooker(mBetaOverlayIV, GREEN);
+        } else {
+            pixelLooker(mBetaOverlayIV, RED);
+        }
+    }
+
+    public void testFreeNormalOverlay() {
+        if ("freeNormal".equals(BuildConfig.FLAVOR)) {
+            pixelLooker(mFreeNormalOverlayIV, GREEN);
+        } else {
+            pixelLooker(mFreeNormalOverlayIV, RED);
+        }
+    }
+
+    public void testFreeBetaDebugOverlay() {
+        if ("freeBeta".equals(BuildConfig.FLAVOR) && "debug".equals(BuildConfig.BUILD_TYPE)) {
+            pixelLooker(mFreeBetaDebugOverlayIV, GREEN);
+        } else {
+            pixelLooker(mFreeBetaDebugOverlayIV, RED);
+        }
+    }
+    
+    private void pixelLooker(ImageView iv, int expectedColor) {
+        BitmapDrawable d = (BitmapDrawable) iv.getDrawable();
+        Bitmap bitmap = d.getBitmap();
+        assertEquals(expectedColor, bitmap.getPixel(0, 0));
+    }
+}
+
diff --git a/tests/overlay3/src/main/AndroidManifest.xml b/tests/overlay3/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..1c35a5b
--- /dev/null
+++ b/tests/overlay3/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="com.android.tests.overlay2">
+    <application android:label="@string/app_name" android:icon="@drawable/icon">
+        <activity android:name=".Main"
+                  android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/overlay3/src/main/java/com/android/tests/overlay2/Main.java b/tests/overlay3/src/main/java/com/android/tests/overlay2/Main.java
new file mode 100644
index 0000000..e8a0a83
--- /dev/null
+++ b/tests/overlay3/src/main/java/com/android/tests/overlay2/Main.java
@@ -0,0 +1,15 @@
+package com.android.tests.overlay2;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class Main extends Activity
+{
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+    }
+}
diff --git a/tests/overlay3/src/main/res/drawable/beta_overlay.png b/tests/overlay3/src/main/res/drawable/beta_overlay.png
new file mode 100644
index 0000000..b55e544
--- /dev/null
+++ b/tests/overlay3/src/main/res/drawable/beta_overlay.png
Binary files differ
diff --git a/tests/overlay3/src/main/res/drawable/debug_overlay.png b/tests/overlay3/src/main/res/drawable/debug_overlay.png
new file mode 100644
index 0000000..b55e544
--- /dev/null
+++ b/tests/overlay3/src/main/res/drawable/debug_overlay.png
Binary files differ
diff --git a/tests/overlay3/src/main/res/drawable/free_beta_debug_overlay.png b/tests/overlay3/src/main/res/drawable/free_beta_debug_overlay.png
new file mode 100644
index 0000000..b55e544
--- /dev/null
+++ b/tests/overlay3/src/main/res/drawable/free_beta_debug_overlay.png
Binary files differ
diff --git a/tests/overlay3/src/main/res/drawable/free_beta_overlay.png b/tests/overlay3/src/main/res/drawable/free_beta_overlay.png
new file mode 100644
index 0000000..b55e544
--- /dev/null
+++ b/tests/overlay3/src/main/res/drawable/free_beta_overlay.png
Binary files differ
diff --git a/tests/overlay3/src/main/res/drawable/free_normal_overlay.png b/tests/overlay3/src/main/res/drawable/free_normal_overlay.png
new file mode 100644
index 0000000..b55e544
--- /dev/null
+++ b/tests/overlay3/src/main/res/drawable/free_normal_overlay.png
Binary files differ
diff --git a/tests/overlay3/src/main/res/drawable/free_overlay.png b/tests/overlay3/src/main/res/drawable/free_overlay.png
new file mode 100644
index 0000000..b55e544
--- /dev/null
+++ b/tests/overlay3/src/main/res/drawable/free_overlay.png
Binary files differ
diff --git a/tests/overlay3/src/main/res/drawable/icon.png b/tests/overlay3/src/main/res/drawable/icon.png
new file mode 100644
index 0000000..a07c69f
--- /dev/null
+++ b/tests/overlay3/src/main/res/drawable/icon.png
Binary files differ
diff --git a/tests/overlay3/src/main/res/drawable/no_overlay.png b/tests/overlay3/src/main/res/drawable/no_overlay.png
new file mode 100644
index 0000000..47e1adf
--- /dev/null
+++ b/tests/overlay3/src/main/res/drawable/no_overlay.png
Binary files differ
diff --git a/tests/overlay3/src/main/res/layout/main.xml b/tests/overlay3/src/main/res/layout/main.xml
new file mode 100644
index 0000000..1046827
--- /dev/null
+++ b/tests/overlay3/src/main/res/layout/main.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    >
+    <ImageView
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:src="@drawable/no_overlay"
+        android:id="@+id/no_overlay" />
+    <ImageView
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:src="@drawable/debug_overlay"
+        android:id="@+id/debug_overlay" />
+    <ImageView
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:src="@drawable/beta_overlay"
+        android:id="@+id/beta_overlay" />
+    <ImageView
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:src="@drawable/free_normal_overlay"
+        android:id="@+id/free_normal_overlay" />
+    <ImageView
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:src="@drawable/free_beta_debug_overlay"
+        android:id="@+id/free_beta_debug_overlay" />
+</LinearLayout>
+
diff --git a/tests/overlay3/src/main/res/values/strings.xml b/tests/overlay3/src/main/res/values/strings.xml
new file mode 100644
index 0000000..d1420e7
--- /dev/null
+++ b/tests/overlay3/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">_Test-Overlay2</string>
+</resources>
diff --git a/tests/pkgOverride/build.gradle b/tests/pkgOverride/build.gradle
index 9b909a9..72b1dc4 100644
--- a/tests/pkgOverride/build.gradle
+++ b/tests/pkgOverride/build.gradle
@@ -3,7 +3,7 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
 apply plugin: 'android'
diff --git a/tests/proguard/build.gradle b/tests/proguard/build.gradle
index 394a039..655605d 100644
--- a/tests/proguard/build.gradle
+++ b/tests/proguard/build.gradle
@@ -3,7 +3,7 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
 apply plugin: 'android'
diff --git a/tests/proguard/src/instrumentTest/java/com/android/tests/basic/MainTest.java b/tests/proguard/src/instrumentTest/java/com/android/tests/basic/MainTest.java
index cbbc52b..44bc9f4 100644
--- a/tests/proguard/src/instrumentTest/java/com/android/tests/basic/MainTest.java
+++ b/tests/proguard/src/instrumentTest/java/com/android/tests/basic/MainTest.java
@@ -38,5 +38,12 @@
     public void testTextViewContent() {
         assertEquals("1234", mTextView.getText());
     }
+
+    /** Test using a obfuscated class */
+    public void testObfuscatedCode() {
+        final Main a = getActivity();
+        StringProvider sp = a.getStringProvider();
+        assertEquals("42", sp.getString(42));
+    }
 }
 
diff --git a/tests/proguard/src/main/java/com/android/tests/basic/Main.java b/tests/proguard/src/main/java/com/android/tests/basic/Main.java
index 6d3ae10..e32404a 100644
--- a/tests/proguard/src/main/java/com/android/tests/basic/Main.java
+++ b/tests/proguard/src/main/java/com/android/tests/basic/Main.java
@@ -4,20 +4,23 @@
 import android.os.Bundle;
 import android.widget.TextView;
 
-public class Main extends Activity
-{
+public class Main extends Activity {
 
     private int foo = 1234;
 
+    private final StringProvider mStringProvider = new StringProvider();
+
     /** Called when the activity is first created. */
     @Override
-    public void onCreate(Bundle savedInstanceState)
-    {
+    public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
 
         TextView tv = (TextView) findViewById(R.id.dateText);
-        StringProvider stringProvider = new StringProvider();
-        tv.setText(stringProvider.getString(foo));
+        tv.setText(getStringProvider().getString(foo));
+    }
+
+    public StringProvider getStringProvider() {
+        return mStringProvider;
     }
 }
diff --git a/tests/proguardLib/build.gradle b/tests/proguardLib/build.gradle
index 62a4405..b899f9a 100644
--- a/tests/proguardLib/build.gradle
+++ b/tests/proguardLib/build.gradle
@@ -3,6 +3,6 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
diff --git a/tests/proguardLib/lib/build.gradle b/tests/proguardLib/lib/build.gradle
index 3b209bf..efbffe6 100644
--- a/tests/proguardLib/lib/build.gradle
+++ b/tests/proguardLib/lib/build.gradle
@@ -9,6 +9,6 @@
         versionName "2.0"
         minSdkVersion 16
         targetSdkVersion 16
-        proguardFile 'config.pro'
+        consumerProguardFiles 'config.pro'
     }
-}
\ No newline at end of file
+}
diff --git a/tests/renamedApk/build.gradle b/tests/renamedApk/build.gradle
index 5ff689c..0323afe 100644
--- a/tests/renamedApk/build.gradle
+++ b/tests/renamedApk/build.gradle
@@ -3,7 +3,7 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
 apply plugin: 'android'
diff --git a/tests/renderscript/build.gradle b/tests/renderscript/build.gradle
index 7530870..93e17af 100644
--- a/tests/renderscript/build.gradle
+++ b/tests/renderscript/build.gradle
@@ -3,7 +3,7 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
 apply plugin: 'android'
diff --git a/tests/renderscriptInLib/build.gradle b/tests/renderscriptInLib/build.gradle
index 62a4405..b899f9a 100644
--- a/tests/renderscriptInLib/build.gradle
+++ b/tests/renderscriptInLib/build.gradle
@@ -3,6 +3,6 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
diff --git a/tests/renderscriptMultiSrc/build.gradle b/tests/renderscriptMultiSrc/build.gradle
index e41f301..3a3b9f6 100644
--- a/tests/renderscriptMultiSrc/build.gradle
+++ b/tests/renderscriptMultiSrc/build.gradle
@@ -3,7 +3,7 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
 apply plugin: 'android'
diff --git a/tests/repo/app/build.gradle b/tests/repo/app/build.gradle
index b7d8e4a..29512f3 100644
--- a/tests/repo/app/build.gradle
+++ b/tests/repo/app/build.gradle
@@ -3,7 +3,7 @@
         maven { url '../../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
 
diff --git a/tests/repo/baseLibrary/build.gradle b/tests/repo/baseLibrary/build.gradle
index ca226de..1f5dbe6 100644
--- a/tests/repo/baseLibrary/build.gradle
+++ b/tests/repo/baseLibrary/build.gradle
@@ -3,7 +3,7 @@
         maven { url '../../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
 
diff --git a/tests/repo/library/build.gradle b/tests/repo/library/build.gradle
index d2f6ae6..2d1ccd5 100644
--- a/tests/repo/library/build.gradle
+++ b/tests/repo/library/build.gradle
@@ -3,7 +3,7 @@
         maven { url '../../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
 
diff --git a/tests/rsSupportMode/build.gradle b/tests/rsSupportMode/build.gradle
new file mode 100644
index 0000000..9df3260
--- /dev/null
+++ b/tests/rsSupportMode/build.gradle
@@ -0,0 +1,39 @@
+buildscript {
+    repositories {
+        maven { url '../../../../out/host/gradle/repo' }
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
+    }
+}
+apply plugin: 'android'
+
+android {
+    compileSdkVersion 18
+    buildToolsVersion "18.1.1"
+
+    defaultConfig {
+        minSdkVersion 8
+        targetSdkVersion 16
+        renderscriptTargetApi 18
+        renderscriptSupportMode true
+    }
+
+    productFlavors {
+        x86 {
+            ndk {
+                abiFilter "x86"
+            }
+        }
+        arm {
+            ndk {
+                abiFilter "armeabi-v7a"
+            }
+        }
+        mips {
+            ndk {
+                abiFilter "mips"
+            }
+        }
+    }
+}
diff --git a/tests/rsSupportMode/proguard-project.txt b/tests/rsSupportMode/proguard-project.txt
new file mode 100644
index 0000000..f2fe155
--- /dev/null
+++ b/tests/rsSupportMode/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
diff --git a/tests/rsSupportMode/project.properties b/tests/rsSupportMode/project.properties
new file mode 100644
index 0000000..ca810ec
--- /dev/null
+++ b/tests/rsSupportMode/project.properties
@@ -0,0 +1,22 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-18
+renderscript.target=18
+renderscript.support.mode=true
+
+
+key.store=/Users/xav/.android/debug.keystore
+key.store.password=android
+key.alias=androiddebugkey
+key.alias.password=android
diff --git a/tests/rsSupportMode/src/main/AndroidManifest.xml b/tests/rsSupportMode/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..0129fa8
--- /dev/null
+++ b/tests/rsSupportMode/src/main/AndroidManifest.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.rs.image2">
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-sdk android:minSdkVersion="8" />
+    <application android:label="IP GB">
+        <activity android:name="ImageProcessingActivity2">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/rsSupportMode/src/main/java/com/android/rs/image/BWFilter.java b/tests/rsSupportMode/src/main/java/com/android/rs/image/BWFilter.java
new file mode 100644
index 0000000..4b19856
--- /dev/null
+++ b/tests/rsSupportMode/src/main/java/com/android/rs/image/BWFilter.java
@@ -0,0 +1,34 @@
+/*
+ * 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.rs.image2;
+
+import java.lang.Math;
+
+
+public class BWFilter extends TestBase {
+    private ScriptC_bwfilter mScript;
+
+    public void createTest(android.content.res.Resources res) {
+        mScript = new ScriptC_bwfilter(mRS);
+    }
+
+    public void runTest() {
+        mScript.invoke_prepareBwFilter(50, 50, 50);
+        mScript.forEach_bwFilterKernel(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
+}
diff --git a/tests/rsSupportMode/src/main/java/com/android/rs/image/Blend.java b/tests/rsSupportMode/src/main/java/com/android/rs/image/Blend.java
new file mode 100644
index 0000000..d81ba88
--- /dev/null
+++ b/tests/rsSupportMode/src/main/java/com/android/rs/image/Blend.java
@@ -0,0 +1,170 @@
+/*
+ * 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.rs.image2;
+
+import java.lang.Math;
+import java.lang.Short;
+
+import android.support.v8.renderscript.*;
+import android.util.Log;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.view.View;
+import android.widget.Spinner;
+
+public class Blend extends TestBase {
+    private ScriptIntrinsicBlend mBlend;
+    private ScriptC_blend mBlendHelper;
+    private short image1Alpha = 128;
+    private short image2Alpha = 128;
+
+    String mIntrinsicNames[];
+
+    private Allocation image1;
+    private Allocation image2;
+    private int currentIntrinsic = 0;
+
+    private AdapterView.OnItemSelectedListener mIntrinsicSpinnerListener =
+            new AdapterView.OnItemSelectedListener() {
+                public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
+                    currentIntrinsic = pos;
+                    if (mRS != null) {
+                        runTest();
+                        act.updateDisplay();
+                    }
+                }
+
+                public void onNothingSelected(AdapterView parent) {
+
+                }
+            };
+
+    public void createTest(android.content.res.Resources res) {
+        mBlend = ScriptIntrinsicBlend.create(mRS, Element.U8_4(mRS));
+        mBlendHelper = new ScriptC_blend(mRS);
+        mBlendHelper.set_alpha((short)128);
+
+        image1 = Allocation.createTyped(mRS, mInPixelsAllocation.getType());
+        image2 = Allocation.createTyped(mRS, mInPixelsAllocation2.getType());
+
+        mIntrinsicNames = new String[14];
+        mIntrinsicNames[0] = "Source";
+        mIntrinsicNames[1] = "Destination";
+        mIntrinsicNames[2] = "Source Over";
+        mIntrinsicNames[3] = "Destination Over";
+        mIntrinsicNames[4] = "Source In";
+        mIntrinsicNames[5] = "Destination In";
+        mIntrinsicNames[6] = "Source Out";
+        mIntrinsicNames[7] = "Destination Out";
+        mIntrinsicNames[8] = "Source Atop";
+        mIntrinsicNames[9] = "Destination Atop";
+        mIntrinsicNames[10] = "XOR";
+        mIntrinsicNames[11] = "Add";
+        mIntrinsicNames[12] = "Subtract";
+        mIntrinsicNames[13] = "Multiply";
+    }
+
+    public boolean onSpinner1Setup(Spinner s) {
+        s.setAdapter(new ArrayAdapter<String>(
+            act, R.layout.spinner_layout, mIntrinsicNames));
+        s.setOnItemSelectedListener(mIntrinsicSpinnerListener);
+        return true;
+    }
+
+    public boolean onBar1Setup(SeekBar b, TextView t) {
+        t.setText("Image 1 Alpha");
+        b.setMax(255);
+        b.setProgress(image1Alpha);
+        return true;
+    }
+
+    public void onBar1Changed(int progress) {
+        image1Alpha = (short)progress;
+    }
+
+    public boolean onBar2Setup(SeekBar b, TextView t) {
+        t.setText("Image 2 Alpha");
+        b.setMax(255);
+        b.setProgress(image2Alpha);
+        return true;
+    }
+
+    public void onBar2Changed(int progress) {
+        image2Alpha = (short)progress;
+    }
+
+    public void runTest() {
+        image1.copy2DRangeFrom(0, 0, mInPixelsAllocation.getType().getX(), mInPixelsAllocation.getType().getY(), mInPixelsAllocation, 0, 0);
+        image2.copy2DRangeFrom(0, 0, mInPixelsAllocation2.getType().getX(), mInPixelsAllocation2.getType().getY(), mInPixelsAllocation2, 0, 0);
+
+        mBlendHelper.set_alpha(image1Alpha);
+        mBlendHelper.forEach_setImageAlpha(image1);
+
+        mBlendHelper.set_alpha(image2Alpha);
+        mBlendHelper.forEach_setImageAlpha(image2);
+
+        switch (currentIntrinsic) {
+        case 0:
+            mBlend.forEachSrc(image1, image2);
+            break;
+        case 1:
+            mBlend.forEachDst(image1, image2);
+            break;
+        case 2:
+            mBlend.forEachSrcOver(image1, image2);
+            break;
+        case 3:
+            mBlend.forEachDstOver(image1, image2);
+            break;
+        case 4:
+            mBlend.forEachSrcIn(image1, image2);
+            break;
+        case 5:
+            mBlend.forEachDstIn(image1, image2);
+            break;
+        case 6:
+            mBlend.forEachSrcOut(image1, image2);
+            break;
+        case 7:
+            mBlend.forEachDstOut(image1, image2);
+            break;
+        case 8:
+            mBlend.forEachSrcAtop(image1, image2);
+            break;
+        case 9:
+            mBlend.forEachDstAtop(image1, image2);
+            break;
+        case 10:
+            mBlend.forEachXor(image1, image2);
+            break;
+        case 11:
+            mBlend.forEachAdd(image1, image2);
+            break;
+        case 12:
+            mBlend.forEachSubtract(image1, image2);
+            break;
+        case 13:
+            mBlend.forEachMultiply(image1, image2);
+            break;
+        }
+
+        mOutPixelsAllocation.copy2DRangeFrom(0, 0, image2.getType().getX(), image2.getType().getY(), image2, 0, 0);
+    }
+
+}
diff --git a/tests/rsSupportMode/src/main/java/com/android/rs/image/Blur25.java b/tests/rsSupportMode/src/main/java/com/android/rs/image/Blur25.java
new file mode 100644
index 0000000..b518b02
--- /dev/null
+++ b/tests/rsSupportMode/src/main/java/com/android/rs/image/Blur25.java
@@ -0,0 +1,114 @@
+/*
+ * 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.rs.image2;
+
+import java.lang.Math;
+
+import android.support.v8.renderscript.*;
+import android.util.Log;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+public class Blur25 extends TestBase {
+    private boolean mUseIntrinsic = false;
+    private ScriptIntrinsicBlur mIntrinsic;
+
+    private int MAX_RADIUS = 25;
+    private ScriptC_threshold mScript;
+    private float mRadius = MAX_RADIUS;
+    private float mSaturation = 1.0f;
+    private Allocation mScratchPixelsAllocation1;
+    private Allocation mScratchPixelsAllocation2;
+
+
+    public Blur25(boolean useIntrinsic) {
+        mUseIntrinsic = useIntrinsic;
+    }
+
+    public boolean onBar1Setup(SeekBar b, TextView t) {
+        t.setText("Radius");
+        b.setProgress(100);
+        return true;
+    }
+
+
+    public void onBar1Changed(int progress) {
+        mRadius = ((float)progress) / 100.0f * MAX_RADIUS;
+        if (mRadius <= 0.10f) {
+            mRadius = 0.10f;
+        }
+        if (mUseIntrinsic) {
+            mIntrinsic.setRadius(mRadius);
+        } else {
+            mScript.invoke_setRadius((int)mRadius);
+        }
+    }
+
+
+    public void createTest(android.content.res.Resources res) {
+        int width = mInPixelsAllocation.getType().getX();
+        int height = mInPixelsAllocation.getType().getY();
+
+        if (mUseIntrinsic) {
+            mIntrinsic = ScriptIntrinsicBlur.create(mRS, Element.U8_4(mRS));
+            mIntrinsic.setRadius(MAX_RADIUS);
+            mIntrinsic.setInput(mInPixelsAllocation);
+        } else {
+
+            Type.Builder tb = new Type.Builder(mRS, Element.F32_4(mRS));
+            tb.setX(width);
+            tb.setY(height);
+            mScratchPixelsAllocation1 = Allocation.createTyped(mRS, tb.create());
+            mScratchPixelsAllocation2 = Allocation.createTyped(mRS, tb.create());
+
+            mScript = new ScriptC_threshold(mRS, res, R.raw.threshold);
+            mScript.set_width(width);
+            mScript.set_height(height);
+            mScript.invoke_setRadius(MAX_RADIUS);
+
+            mScript.set_InPixel(mInPixelsAllocation);
+            mScript.set_ScratchPixel1(mScratchPixelsAllocation1);
+            mScript.set_ScratchPixel2(mScratchPixelsAllocation2);
+        }
+    }
+
+    public void runTest() {
+        if (mUseIntrinsic) {
+            mIntrinsic.forEach(mOutPixelsAllocation);
+        } else {
+            mScript.forEach_copyIn(mInPixelsAllocation, mScratchPixelsAllocation1);
+            mScript.forEach_horz(mScratchPixelsAllocation2);
+            mScript.forEach_vert(mOutPixelsAllocation);
+        }
+    }
+
+    public void setupBenchmark() {
+        if (mUseIntrinsic) {
+            mIntrinsic.setRadius(MAX_RADIUS);
+        } else {
+            mScript.invoke_setRadius(MAX_RADIUS);
+        }
+    }
+
+    public void exitBenchmark() {
+        if (mUseIntrinsic) {
+            mIntrinsic.setRadius(mRadius);
+        } else {
+            mScript.invoke_setRadius((int)mRadius);
+        }
+    }
+}
diff --git a/tests/rsSupportMode/src/main/java/com/android/rs/image/Blur25G.java b/tests/rsSupportMode/src/main/java/com/android/rs/image/Blur25G.java
new file mode 100644
index 0000000..19aa9f7
--- /dev/null
+++ b/tests/rsSupportMode/src/main/java/com/android/rs/image/Blur25G.java
@@ -0,0 +1,93 @@
+/*
+ * 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.rs.image2;
+
+import java.lang.Math;
+
+import android.graphics.Bitmap;
+import android.support.v8.renderscript.*;
+import android.util.Log;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+public class Blur25G extends TestBase {
+    private final int MAX_RADIUS = 25;
+    private float mRadius = MAX_RADIUS;
+
+    private ScriptIntrinsicBlur mIntrinsic;
+
+    private ScriptC_greyscale mScript;
+    private Allocation mScratchPixelsAllocation1;
+    private Allocation mScratchPixelsAllocation2;
+
+
+    public Blur25G() {
+    }
+
+    public boolean onBar1Setup(SeekBar b, TextView t) {
+        t.setText("Radius");
+        b.setProgress(100);
+        return true;
+    }
+
+
+    public void onBar1Changed(int progress) {
+        mRadius = ((float)progress) / 100.0f * MAX_RADIUS;
+        if (mRadius <= 0.10f) {
+            mRadius = 0.10f;
+        }
+        mIntrinsic.setRadius(mRadius);
+    }
+
+
+    public void createTest(android.content.res.Resources res) {
+        int width = mInPixelsAllocation.getType().getX();
+        int height = mInPixelsAllocation.getType().getY();
+
+        Type.Builder tb = new Type.Builder(mRS, Element.U8(mRS));
+        tb.setX(width);
+        tb.setY(height);
+        mScratchPixelsAllocation1 = Allocation.createTyped(mRS, tb.create());
+        mScratchPixelsAllocation2 = Allocation.createTyped(mRS, tb.create());
+
+        mScript = new ScriptC_greyscale(mRS);
+        mScript.forEach_toU8(mInPixelsAllocation, mScratchPixelsAllocation1);
+
+        mIntrinsic = ScriptIntrinsicBlur.create(mRS, Element.U8(mRS));
+        mIntrinsic.setRadius(MAX_RADIUS);
+        mIntrinsic.setInput(mScratchPixelsAllocation1);
+    }
+
+    public void runTest() {
+        mIntrinsic.forEach(mScratchPixelsAllocation2);
+    }
+
+    public void setupBenchmark() {
+        mIntrinsic.setRadius(MAX_RADIUS);
+    }
+
+    public void exitBenchmark() {
+        mIntrinsic.setRadius(mRadius);
+    }
+
+    public void updateBitmap(Bitmap b) {
+        mScript.forEach_toU8_4(mScratchPixelsAllocation2, mOutPixelsAllocation);
+        mOutPixelsAllocation.copyTo(b);
+    }
+
+}
+
diff --git a/tests/rsSupportMode/src/main/java/com/android/rs/image/ColorCube.java b/tests/rsSupportMode/src/main/java/com/android/rs/image/ColorCube.java
new file mode 100644
index 0000000..e960385
--- /dev/null
+++ b/tests/rsSupportMode/src/main/java/com/android/rs/image/ColorCube.java
@@ -0,0 +1,79 @@
+/*
+ * 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.rs.image2;
+
+import java.lang.Math;
+
+import android.support.v8.renderscript.*;
+import android.util.Log;
+
+public class ColorCube extends TestBase {
+    private Allocation mCube;
+    private ScriptC_colorcube mScript;
+    private ScriptIntrinsic3DLUT mIntrinsic;
+    private boolean mUseIntrinsic;
+
+    public ColorCube(boolean useIntrinsic) {
+        mUseIntrinsic = useIntrinsic;
+    }
+
+    private void initCube() {
+        final int sx = 32;
+        final int sy = 32;
+        final int sz = 16;
+
+        Type.Builder tb = new Type.Builder(mRS, Element.U8_4(mRS));
+        tb.setX(sx);
+        tb.setY(sy);
+        tb.setZ(sz);
+        Type t = tb.create();
+        mCube = Allocation.createTyped(mRS, t);
+
+        int dat[] = new int[sx * sy * sz];
+        for (int z = 0; z < sz; z++) {
+            for (int y = 0; y < sy; y++) {
+                for (int x = 0; x < sx; x++ ) {
+                    int v = 0xff000000;
+                    v |= (0xff * x / (sx - 1));
+                    v |= (0xff * y / (sy - 1)) << 8;
+                    v |= (0xff * z / (sz - 1)) << 16;
+                    dat[z*sy*sx + y*sx + x] = v;
+                }
+            }
+        }
+
+        mCube.copyFromUnchecked(dat);
+    }
+
+    public void createTest(android.content.res.Resources res) {
+        mScript = new ScriptC_colorcube(mRS, res, R.raw.colorcube);
+        mIntrinsic = ScriptIntrinsic3DLUT.create(mRS, Element.U8_4(mRS));
+
+        initCube();
+        mScript.invoke_setCube(mCube);
+        mIntrinsic.setLUT(mCube);
+    }
+
+    public void runTest() {
+        if (mUseIntrinsic) {
+            mIntrinsic.forEach(mInPixelsAllocation, mOutPixelsAllocation);
+        } else {
+            mScript.forEach_root(mInPixelsAllocation, mOutPixelsAllocation);
+        }
+    }
+
+}
diff --git a/tests/rsSupportMode/src/main/java/com/android/rs/image/ColorMatrix.java b/tests/rsSupportMode/src/main/java/com/android/rs/image/ColorMatrix.java
new file mode 100644
index 0000000..3b0f86a
--- /dev/null
+++ b/tests/rsSupportMode/src/main/java/com/android/rs/image/ColorMatrix.java
@@ -0,0 +1,62 @@
+/*
+ * 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.rs.image2;
+
+import java.lang.Math;
+
+import android.support.v8.renderscript.*;
+import android.util.Log;
+
+public class ColorMatrix extends TestBase {
+    private ScriptC_colormatrix mScript;
+    private ScriptIntrinsicColorMatrix mIntrinsic;
+    private boolean mUseIntrinsic;
+    private boolean mUseGrey;
+
+    public ColorMatrix(boolean useIntrinsic, boolean useGrey) {
+        mUseIntrinsic = useIntrinsic;
+        mUseGrey = useGrey;
+    }
+
+    public void createTest(android.content.res.Resources res) {
+        Matrix4f m = new Matrix4f();
+        m.set(1, 0, 0.2f);
+        m.set(1, 1, 0.9f);
+        m.set(1, 2, 0.2f);
+
+        if (mUseIntrinsic) {
+            mIntrinsic = ScriptIntrinsicColorMatrix.create(mRS, Element.U8_4(mRS));
+            if (mUseGrey) {
+                mIntrinsic.setGreyscale();
+            } else {
+                mIntrinsic.setColorMatrix(m);
+            }
+        } else {
+            mScript = new ScriptC_colormatrix(mRS, res, R.raw.colormatrix);
+            mScript.invoke_setMatrix(m);
+        }
+    }
+
+    public void runTest() {
+        if (mUseIntrinsic) {
+            mIntrinsic.forEach(mInPixelsAllocation, mOutPixelsAllocation);
+        } else {
+            mScript.forEach_root(mInPixelsAllocation, mOutPixelsAllocation);
+        }
+    }
+
+}
diff --git a/tests/rsSupportMode/src/main/java/com/android/rs/image/Contrast.java b/tests/rsSupportMode/src/main/java/com/android/rs/image/Contrast.java
new file mode 100644
index 0000000..3ae5d2a
--- /dev/null
+++ b/tests/rsSupportMode/src/main/java/com/android/rs/image/Contrast.java
@@ -0,0 +1,34 @@
+/*
+ * 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.rs.image2;
+
+import java.lang.Math;
+
+
+public class Contrast extends TestBase {
+    private ScriptC_contrast mScript;
+
+    public void createTest(android.content.res.Resources res) {
+        mScript = new ScriptC_contrast(mRS);
+    }
+
+    public void runTest() {
+        mScript.invoke_setBright(50.f);
+        mScript.forEach_contrast(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
+}
diff --git a/tests/rsSupportMode/src/main/java/com/android/rs/image/Convolve3x3.java b/tests/rsSupportMode/src/main/java/com/android/rs/image/Convolve3x3.java
new file mode 100644
index 0000000..d4852f0
--- /dev/null
+++ b/tests/rsSupportMode/src/main/java/com/android/rs/image/Convolve3x3.java
@@ -0,0 +1,66 @@
+/*
+ * 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.rs.image2;
+
+import java.lang.Math;
+
+import android.support.v8.renderscript.*;
+import android.util.Log;
+
+public class Convolve3x3 extends TestBase {
+    private ScriptC_ip2_convolve3x3 mScript;
+    private ScriptIntrinsicConvolve3x3 mIntrinsic;
+
+    private int mWidth;
+    private int mHeight;
+    private boolean mUseIntrinsic;
+
+    public Convolve3x3(boolean useIntrinsic) {
+        mUseIntrinsic = useIntrinsic;
+    }
+
+    public void createTest(android.content.res.Resources res) {
+        mWidth = mInPixelsAllocation.getType().getX();
+        mHeight = mInPixelsAllocation.getType().getY();
+
+        float f[] = new float[9];
+        f[0] =  0.f;    f[1] = -1.f;    f[2] =  0.f;
+        f[3] = -1.f;    f[4] =  5.f;    f[5] = -1.f;
+        f[6] =  0.f;    f[7] = -1.f;    f[8] =  0.f;
+
+        if (mUseIntrinsic) {
+            mIntrinsic = ScriptIntrinsicConvolve3x3.create(mRS, Element.U8_4(mRS));
+            mIntrinsic.setCoefficients(f);
+            mIntrinsic.setInput(mInPixelsAllocation);
+        } else {
+            mScript = new ScriptC_ip2_convolve3x3(mRS, res, R.raw.ip2_convolve3x3);
+            mScript.set_gCoeffs(f);
+            mScript.set_gIn(mInPixelsAllocation);
+            mScript.set_gWidth(mWidth);
+            mScript.set_gHeight(mHeight);
+        }
+    }
+
+    public void runTest() {
+        if (mUseIntrinsic) {
+            mIntrinsic.forEach(mOutPixelsAllocation);
+        } else {
+            mScript.forEach_root(mOutPixelsAllocation);
+        }
+    }
+
+}
diff --git a/tests/rsSupportMode/src/main/java/com/android/rs/image/Convolve5x5.java b/tests/rsSupportMode/src/main/java/com/android/rs/image/Convolve5x5.java
new file mode 100644
index 0000000..d2da3c4
--- /dev/null
+++ b/tests/rsSupportMode/src/main/java/com/android/rs/image/Convolve5x5.java
@@ -0,0 +1,80 @@
+/*
+ * 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.rs.image2;
+
+import java.lang.Math;
+
+import android.support.v8.renderscript.*;
+import android.util.Log;
+
+public class Convolve5x5 extends TestBase {
+    private ScriptC_convolve5x5 mScript;
+    private ScriptIntrinsicConvolve5x5 mIntrinsic;
+
+    private int mWidth;
+    private int mHeight;
+    private boolean mUseIntrinsic;
+
+    public Convolve5x5(boolean useIntrinsic) {
+        mUseIntrinsic = useIntrinsic;
+    }
+
+    public void createTest(android.content.res.Resources res) {
+        mWidth = mInPixelsAllocation.getType().getX();
+        mHeight = mInPixelsAllocation.getType().getY();
+
+        float f[] = new float[25];
+        //f[0] = 0.012f; f[1] = 0.025f; f[2] = 0.031f; f[3] = 0.025f; f[4] = 0.012f;
+        //f[5] = 0.025f; f[6] = 0.057f; f[7] = 0.075f; f[8] = 0.057f; f[9] = 0.025f;
+        //f[10]= 0.031f; f[11]= 0.075f; f[12]= 0.095f; f[13]= 0.075f; f[14]= 0.031f;
+        //f[15]= 0.025f; f[16]= 0.057f; f[17]= 0.075f; f[18]= 0.057f; f[19]= 0.025f;
+        //f[20]= 0.012f; f[21]= 0.025f; f[22]= 0.031f; f[23]= 0.025f; f[24]= 0.012f;
+
+        //f[0] = 1.f; f[1] = 2.f; f[2] = 0.f; f[3] = -2.f; f[4] = -1.f;
+        //f[5] = 4.f; f[6] = 8.f; f[7] = 0.f; f[8] = -8.f; f[9] = -4.f;
+        //f[10]= 6.f; f[11]=12.f; f[12]= 0.f; f[13]=-12.f; f[14]= -6.f;
+        //f[15]= 4.f; f[16]= 8.f; f[17]= 0.f; f[18]= -8.f; f[19]= -4.f;
+        //f[20]= 1.f; f[21]= 2.f; f[22]= 0.f; f[23]= -2.f; f[24]= -1.f;
+
+        f[0] = -1.f; f[1] = -3.f; f[2] = -4.f; f[3] = -3.f; f[4] = -1.f;
+        f[5] = -3.f; f[6] =  0.f; f[7] =  6.f; f[8] =  0.f; f[9] = -3.f;
+        f[10]= -4.f; f[11]=  6.f; f[12]= 20.f; f[13]=  6.f; f[14]= -4.f;
+        f[15]= -3.f; f[16]=  0.f; f[17]=  6.f; f[18]=  0.f; f[19]= -3.f;
+        f[20]= -1.f; f[21]= -3.f; f[22]= -4.f; f[23]= -3.f; f[24]= -1.f;
+
+        if (mUseIntrinsic) {
+            mIntrinsic = ScriptIntrinsicConvolve5x5.create(mRS, Element.U8_4(mRS));
+            mIntrinsic.setCoefficients(f);
+            mIntrinsic.setInput(mInPixelsAllocation);
+        } else {
+            mScript = new ScriptC_convolve5x5(mRS, res, R.raw.convolve5x5);
+            mScript.set_gCoeffs(f);
+            mScript.set_gIn(mInPixelsAllocation);
+            mScript.set_gWidth(mWidth);
+            mScript.set_gHeight(mHeight);
+        }
+    }
+
+    public void runTest() {
+        if (mUseIntrinsic) {
+            mIntrinsic.forEach(mOutPixelsAllocation);
+        } else {
+            mScript.forEach_root(mOutPixelsAllocation);
+        }
+    }
+
+}
diff --git a/tests/rsSupportMode/src/main/java/com/android/rs/image/Copy.java b/tests/rsSupportMode/src/main/java/com/android/rs/image/Copy.java
new file mode 100644
index 0000000..ef71907
--- /dev/null
+++ b/tests/rsSupportMode/src/main/java/com/android/rs/image/Copy.java
@@ -0,0 +1,35 @@
+/*
+ * 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.rs.image2;
+
+import java.lang.Math;
+
+import android.support.v8.renderscript.*;
+import android.util.Log;
+
+public class Copy extends TestBase {
+    private ScriptC_copy mScript;
+
+    public void createTest(android.content.res.Resources res) {
+        mScript = new ScriptC_copy(mRS, res, R.raw.copy);
+    }
+
+    public void runTest() {
+        mScript.forEach_root(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
+}
diff --git a/tests/rsSupportMode/src/main/java/com/android/rs/image/CrossProcess.java b/tests/rsSupportMode/src/main/java/com/android/rs/image/CrossProcess.java
new file mode 100644
index 0000000..96787d7
--- /dev/null
+++ b/tests/rsSupportMode/src/main/java/com/android/rs/image/CrossProcess.java
@@ -0,0 +1,60 @@
+/*
+ * 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.rs.image2;
+
+import java.lang.Math;
+
+import android.support.v8.renderscript.*;
+import android.util.Log;
+
+public class CrossProcess extends TestBase {
+    private ScriptIntrinsicLUT mIntrinsic;
+
+    public void createTest(android.content.res.Resources res) {
+        mIntrinsic = ScriptIntrinsicLUT.create(mRS, Element.U8_4(mRS));
+        for (int ct=0; ct < 256; ct++) {
+            float f = ((float)ct) / 255.f;
+
+            float r = f;
+            if (r < 0.5f) {
+                r = 4.0f * r * r * r;
+            } else {
+                r = 1.0f - r;
+                r = 1.0f - (4.0f * r * r * r);
+            }
+            mIntrinsic.setRed(ct, (int)(r * 255.f + 0.5f));
+
+            float g = f;
+            if (g < 0.5f) {
+                g = 2.0f * g * g;
+            } else {
+                g = 1.0f - g;
+                g = 1.0f - (2.0f * g * g);
+            }
+            mIntrinsic.setGreen(ct, (int)(g * 255.f + 0.5f));
+
+            float b = f * 0.5f + 0.25f;
+            mIntrinsic.setBlue(ct, (int)(b * 255.f + 0.5f));
+        }
+
+    }
+
+    public void runTest() {
+        mIntrinsic.forEach(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
+}
diff --git a/tests/rsSupportMode/src/main/java/com/android/rs/image/Exposure.java b/tests/rsSupportMode/src/main/java/com/android/rs/image/Exposure.java
new file mode 100644
index 0000000..deb6b46
--- /dev/null
+++ b/tests/rsSupportMode/src/main/java/com/android/rs/image/Exposure.java
@@ -0,0 +1,35 @@
+/*
+ * 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.rs.image2;
+
+import java.lang.Math;
+
+import android.support.v8.renderscript.*;
+
+public class Exposure extends TestBase {
+    private ScriptC_exposure mScript;
+
+    public void createTest(android.content.res.Resources res) {
+        mScript = new ScriptC_exposure(mRS);
+    }
+
+    public void runTest() {
+        mScript.invoke_setBright(50.f);
+        mScript.forEach_exposure(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
+}
diff --git a/tests/rsSupportMode/src/main/java/com/android/rs/image/Fisheye.java b/tests/rsSupportMode/src/main/java/com/android/rs/image/Fisheye.java
new file mode 100644
index 0000000..97beb88
--- /dev/null
+++ b/tests/rsSupportMode/src/main/java/com/android/rs/image/Fisheye.java
@@ -0,0 +1,135 @@
+/*
+ * 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.rs.image2;
+
+import android.support.v8.renderscript.*;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+public class Fisheye extends TestBase {
+    private ScriptC_fisheye_full mScript_full = null;
+    private ScriptC_fisheye_relaxed mScript_relaxed = null;
+    private ScriptC_fisheye_approx_full mScript_approx_full = null;
+    private ScriptC_fisheye_approx_relaxed mScript_approx_relaxed = null;
+    private final boolean approx;
+    private final boolean relaxed;
+    private float center_x = 0.5f;
+    private float center_y = 0.5f;
+    private float scale = 0.5f;
+
+    public Fisheye(boolean approx, boolean relaxed) {
+        this.approx = approx;
+        this.relaxed = relaxed;
+    }
+
+    public boolean onBar1Setup(SeekBar b, TextView t) {
+        t.setText("Scale");
+        b.setMax(100);
+        b.setProgress(25);
+        return true;
+    }
+    public boolean onBar2Setup(SeekBar b, TextView t) {
+        t.setText("Shift center X");
+        b.setMax(100);
+        b.setProgress(50);
+        return true;
+    }
+    public boolean onBar3Setup(SeekBar b, TextView t) {
+        t.setText("Shift center Y");
+        b.setMax(100);
+        b.setProgress(50);
+        return true;
+    }
+
+    public void onBar1Changed(int progress) {
+        scale = progress / 50.0f;
+        do_init();
+    }
+    public void onBar2Changed(int progress) {
+        center_x = progress / 100.0f;
+        do_init();
+    }
+    public void onBar3Changed(int progress) {
+        center_y = progress / 100.0f;
+        do_init();
+    }
+
+    private void do_init() {
+        if (approx) {
+            if (relaxed)
+                mScript_approx_relaxed.invoke_init_filter(
+                        mInPixelsAllocation.getType().getX(),
+                        mInPixelsAllocation.getType().getY(), center_x,
+                        center_y, scale);
+            else
+                mScript_approx_full.invoke_init_filter(
+                        mInPixelsAllocation.getType().getX(),
+                        mInPixelsAllocation.getType().getY(), center_x,
+                        center_y, scale);
+        } else if (relaxed)
+            mScript_relaxed.invoke_init_filter(
+                    mInPixelsAllocation.getType().getX(),
+                    mInPixelsAllocation.getType().getY(), center_x, center_y,
+                    scale);
+        else
+            mScript_full.invoke_init_filter(
+                    mInPixelsAllocation.getType().getX(),
+                    mInPixelsAllocation.getType().getY(), center_x, center_y,
+                    scale);
+    }
+
+    public void createTest(android.content.res.Resources res) {
+        if (approx) {
+            if (relaxed) {
+                mScript_approx_relaxed = new ScriptC_fisheye_approx_relaxed(mRS,
+                        res, R.raw.fisheye_approx_relaxed);
+                mScript_approx_relaxed.set_in_alloc(mInPixelsAllocation);
+                mScript_approx_relaxed.set_sampler(Sampler.CLAMP_LINEAR(mRS));
+            } else {
+                mScript_approx_full = new ScriptC_fisheye_approx_full(mRS, res,
+                        R.raw.fisheye_approx_full);
+                mScript_approx_full.set_in_alloc(mInPixelsAllocation);
+                mScript_approx_full.set_sampler(Sampler.CLAMP_LINEAR(mRS));
+            }
+        } else if (relaxed) {
+            mScript_relaxed = new ScriptC_fisheye_relaxed(mRS, res,
+                    R.raw.fisheye_relaxed);
+            mScript_relaxed.set_in_alloc(mInPixelsAllocation);
+            mScript_relaxed.set_sampler(Sampler.CLAMP_LINEAR(mRS));
+        } else {
+            mScript_full = new ScriptC_fisheye_full(mRS, res,
+                    R.raw.fisheye_full);
+            mScript_full.set_in_alloc(mInPixelsAllocation);
+            mScript_full.set_sampler(Sampler.CLAMP_LINEAR(mRS));
+        }
+        do_init();
+    }
+
+    public void runTest() {
+        if (approx) {
+            if (relaxed)
+                mScript_approx_relaxed.forEach_root(mOutPixelsAllocation);
+            else
+                mScript_approx_full.forEach_root(mOutPixelsAllocation);
+        } else if (relaxed)
+            mScript_relaxed.forEach_root(mOutPixelsAllocation);
+        else
+            mScript_full.forEach_root(mOutPixelsAllocation);
+    }
+
+}
+
diff --git a/tests/rsSupportMode/src/main/java/com/android/rs/image/Grain.java b/tests/rsSupportMode/src/main/java/com/android/rs/image/Grain.java
new file mode 100644
index 0000000..dfd3c32
--- /dev/null
+++ b/tests/rsSupportMode/src/main/java/com/android/rs/image/Grain.java
@@ -0,0 +1,89 @@
+/*
+ * 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.rs.image2;
+
+import java.lang.Math;
+
+import android.support.v8.renderscript.*;
+import android.util.Log;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+public class Grain extends TestBase {
+    private ScriptC_grain mScript;
+    private Allocation mNoise;
+    private Allocation mNoise2;
+
+
+    public boolean onBar1Setup(SeekBar b, TextView t) {
+        t.setText("Strength");
+        b.setProgress(50);
+        return true;
+    }
+
+    public void onBar1Changed(int progress) {
+        float s = progress / 100.0f;
+        mScript.set_gNoiseStrength(s);
+    }
+
+    private int findHighBit(int v) {
+        int bit = 0;
+        while (v > 1) {
+            bit++;
+            v >>= 1;
+        }
+        return bit;
+    }
+
+
+    public void createTest(android.content.res.Resources res) {
+        int width = mInPixelsAllocation.getType().getX();
+        int height = mInPixelsAllocation.getType().getY();
+
+        int noiseW = findHighBit(width);
+        int noiseH = findHighBit(height);
+        if (noiseW > 9) {
+            noiseW = 9;
+        }
+        if (noiseH > 9) {
+            noiseH = 9;
+        }
+        noiseW = 1 << noiseW;
+        noiseH = 1 << noiseH;
+
+        Type.Builder tb = new Type.Builder(mRS, Element.U8(mRS));
+        tb.setX(noiseW);
+        tb.setY(noiseH);
+        mNoise = Allocation.createTyped(mRS, tb.create());
+        mNoise2 = Allocation.createTyped(mRS, tb.create());
+
+        mScript = new ScriptC_grain(mRS, res, R.raw.grain);
+        mScript.set_gWMask(noiseW - 1);
+        mScript.set_gHMask(noiseH - 1);
+        mScript.set_gNoiseStrength(0.5f);
+        mScript.set_gBlendSource(mNoise);
+        mScript.set_gNoise(mNoise2);
+    }
+
+    public void runTest() {
+        mScript.forEach_genRand(mNoise);
+        mScript.forEach_blend9(mNoise2);
+        mScript.forEach_root(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
+}
+
diff --git a/tests/rsSupportMode/src/main/java/com/android/rs/image/Greyscale.java b/tests/rsSupportMode/src/main/java/com/android/rs/image/Greyscale.java
new file mode 100644
index 0000000..5b16e24
--- /dev/null
+++ b/tests/rsSupportMode/src/main/java/com/android/rs/image/Greyscale.java
@@ -0,0 +1,32 @@
+/*
+ * 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.rs.image2;
+
+import android.util.Log;
+
+public class Greyscale extends TestBase {
+    private ScriptC_greyscale mScript;
+
+    public void createTest(android.content.res.Resources res) {
+        mScript = new ScriptC_greyscale(mRS, res, R.raw.greyscale);
+    }
+
+    public void runTest() {
+        mScript.forEach_root(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
+}
diff --git a/tests/rsSupportMode/src/main/java/com/android/rs/image/GroupTest.java b/tests/rsSupportMode/src/main/java/com/android/rs/image/GroupTest.java
new file mode 100644
index 0000000..a7ceebe
--- /dev/null
+++ b/tests/rsSupportMode/src/main/java/com/android/rs/image/GroupTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.rs.image2;
+
+import java.lang.Math;
+
+import android.support.v8.renderscript.*;
+import android.util.Log;
+
+public class GroupTest extends TestBase {
+    private ScriptIntrinsicConvolve3x3 mConvolve;
+    private ScriptIntrinsicColorMatrix mMatrix;
+
+    private Allocation mScratchPixelsAllocation1;
+    private ScriptGroup mGroup;
+
+    private int mWidth;
+    private int mHeight;
+    private boolean mUseNative;
+
+
+    public GroupTest(boolean useNative) {
+        mUseNative = useNative;
+    }
+
+    public void createTest(android.content.res.Resources res) {
+        mWidth = mInPixelsAllocation.getType().getX();
+        mHeight = mInPixelsAllocation.getType().getY();
+
+        mConvolve = ScriptIntrinsicConvolve3x3.create(mRS, Element.U8_4(mRS));
+        mMatrix = ScriptIntrinsicColorMatrix.create(mRS, Element.U8_4(mRS));
+
+        float f[] = new float[9];
+        f[0] =  0.f;    f[1] = -1.f;    f[2] =  0.f;
+        f[3] = -1.f;    f[4] =  5.f;    f[5] = -1.f;
+        f[6] =  0.f;    f[7] = -1.f;    f[8] =  0.f;
+        mConvolve.setCoefficients(f);
+
+        Matrix4f m = new Matrix4f();
+        m.set(1, 0, 0.2f);
+        m.set(1, 1, 0.9f);
+        m.set(1, 2, 0.2f);
+        mMatrix.setColorMatrix(m);
+
+        Type.Builder tb = new Type.Builder(mRS, Element.U8_4(mRS));
+        tb.setX(mWidth);
+        tb.setY(mHeight);
+        Type connect = tb.create();
+
+        if (mUseNative) {
+            ScriptGroup.Builder b = new ScriptGroup.Builder(mRS);
+            b.addKernel(mConvolve.getKernelID());
+            b.addKernel(mMatrix.getKernelID());
+            b.addConnection(connect, mConvolve.getKernelID(), mMatrix.getKernelID());
+            mGroup = b.create();
+        } else {
+            mScratchPixelsAllocation1 = Allocation.createTyped(mRS, connect);
+        }
+    }
+
+    public void runTest() {
+        mConvolve.setInput(mInPixelsAllocation);
+        if (mUseNative) {
+            mGroup.setOutput(mMatrix.getKernelID(), mOutPixelsAllocation);
+            mGroup.execute();
+        } else {
+            mConvolve.forEach(mScratchPixelsAllocation1);
+            mMatrix.forEach(mScratchPixelsAllocation1, mOutPixelsAllocation);
+        }
+    }
+
+}
diff --git a/tests/rsSupportMode/src/main/java/com/android/rs/image/ImageProcessingActivity2.java b/tests/rsSupportMode/src/main/java/com/android/rs/image/ImageProcessingActivity2.java
new file mode 100644
index 0000000..4b0e2dd
--- /dev/null
+++ b/tests/rsSupportMode/src/main/java/com/android/rs/image/ImageProcessingActivity2.java
@@ -0,0 +1,470 @@
+/*
+ * 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.rs.image2;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.graphics.BitmapFactory;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.support.v8.renderscript.*;
+import android.view.SurfaceView;
+import android.view.SurfaceHolder;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.SeekBar;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.view.View;
+import android.util.Log;
+import java.lang.Math;
+
+import android.os.Environment;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+public class ImageProcessingActivity2 extends Activity
+                                       implements SeekBar.OnSeekBarChangeListener {
+    private final String TAG = "Img";
+    public final String RESULT_FILE = "image_processing_result.csv";
+
+    RenderScript mRS;
+    Allocation mInPixelsAllocation;
+    Allocation mInPixelsAllocation2;
+    Allocation mOutPixelsAllocation;
+
+    /**
+     * Define enum type for test names
+     */
+    public enum TestName {
+        // totally there are 38 test cases
+        LEVELS_VEC3_RELAXED ("Levels Vec3 Relaxed"),
+        LEVELS_VEC4_RELAXED ("Levels Vec4 Relaxed"),
+        LEVELS_VEC3_FULL ("Levels Vec3 Full"),
+        LEVELS_VEC4_FULL ("Levels Vec4 Full"),
+        BLUR_RADIUS_25 ("Blur radius 25"),
+        INTRINSIC_BLUE_RADIUS_25 ("Intrinsic Blur radius 25"),
+        GREYSCALE ("Greyscale"),
+        GRAIN ("Grain"),
+        FISHEYE_FULL ("Fisheye Full"),
+        FISHEYE_RELAXED ("Fisheye Relaxed"),
+        FISHEYE_APPROXIMATE_FULL ("Fisheye Approximate Full"),
+        FISHEYE_APPROXIMATE_RELAXED ("Fisheye Approximate Relaxed"),
+        VIGNETTE_FULL ("Vignette Full"),
+        VIGNETTE_RELAXED ("Vignette Relaxed"),
+        VIGNETTE_APPROXIMATE_FULL ("Vignette Approximate Full"),
+        VIGNETTE_APPROXIMATE_RELAXED ("Vignette Approximate Relaxed"),
+        GROUP_TEST_EMULATED ("Group Test (emulated)"),
+        GROUP_TEST_NATIVE ("Group Test (native)"),
+        CONVOLVE_3X3 ("Convolve 3x3"),
+        INTRINSICS_CONVOLVE_3X3 ("Intrinsics Convolve 3x3"),
+        COLOR_MATRIX ("ColorMatrix"),
+        INTRINSICS_COLOR_MATRIX ("Intrinsics ColorMatrix"),
+        INTRINSICS_COLOR_MATRIX_GREY ("Intrinsics ColorMatrix Grey"),
+        COPY ("Copy"),
+        CROSS_PROCESS_USING_LUT ("CrossProcess (using LUT)"),
+        CONVOLVE_5X5 ("Convolve 5x5"),
+        INTRINSICS_CONVOLVE_5X5 ("Intrinsics Convolve 5x5"),
+        MANDELBROT ("Mandelbrot"),
+        INTRINSICS_BLEND ("Intrinsics Blend"),
+        INTRINSICS_BLUR_25G ("Intrinsics Blur 25 uchar"),
+        VIBRANCE ("Vibrance"),
+        BW_FILTER ("BW Filter"),
+        SHADOWS ("Shadows"),
+        CONTRAST ("Contrast"),
+        EXPOSURE ("Exposure"),
+        WHITE_BALANCE ("White Balance"),
+        COLOR_CUBE ("Color Cube"),
+        COLOR_CUBE_3D_INTRINSIC ("Color Cube (3D LUT intrinsic)");
+
+
+        private final String name;
+
+        private TestName(String s) {
+            name = s;
+        }
+
+        // return quoted string as displayed test name
+        public String toString() {
+            return name;
+        }
+    }
+
+    Bitmap mBitmapIn;
+    Bitmap mBitmapIn2;
+    Bitmap mBitmapOut;
+
+    private Spinner mSpinner;
+    private SeekBar mBar1;
+    private SeekBar mBar2;
+    private SeekBar mBar3;
+    private SeekBar mBar4;
+    private SeekBar mBar5;
+    private TextView mText1;
+    private TextView mText2;
+    private TextView mText3;
+    private TextView mText4;
+    private TextView mText5;
+
+    private float mSaturation = 1.0f;
+
+    private TextView mBenchmarkResult;
+    private Spinner mTestSpinner;
+
+    private SurfaceView mSurfaceView;
+    private ImageView mDisplayView;
+
+    private boolean mDoingBenchmark;
+
+    private TestBase mTest;
+    private int mRunCount;
+
+    public void updateDisplay() {
+            mTest.updateBitmap(mBitmapOut);
+            mDisplayView.invalidate();
+    }
+
+    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+        if (fromUser) {
+
+            if (seekBar == mBar1) {
+                mTest.onBar1Changed(progress);
+            } else if (seekBar == mBar2) {
+                mTest.onBar2Changed(progress);
+            } else if (seekBar == mBar3) {
+                mTest.onBar3Changed(progress);
+            } else if (seekBar == mBar4) {
+                mTest.onBar4Changed(progress);
+            } else if (seekBar == mBar5) {
+                mTest.onBar5Changed(progress);
+            }
+
+            mTest.runTest();
+            updateDisplay();
+        }
+    }
+
+    public void onStartTrackingTouch(SeekBar seekBar) {
+    }
+
+    public void onStopTrackingTouch(SeekBar seekBar) {
+    }
+
+    void setupBars() {
+        mSpinner.setVisibility(View.VISIBLE);
+        mTest.onSpinner1Setup(mSpinner);
+
+        mBar1.setVisibility(View.VISIBLE);
+        mText1.setVisibility(View.VISIBLE);
+        mTest.onBar1Setup(mBar1, mText1);
+
+        mBar2.setVisibility(View.VISIBLE);
+        mText2.setVisibility(View.VISIBLE);
+        mTest.onBar2Setup(mBar2, mText2);
+
+        mBar3.setVisibility(View.VISIBLE);
+        mText3.setVisibility(View.VISIBLE);
+        mTest.onBar3Setup(mBar3, mText3);
+
+        mBar4.setVisibility(View.VISIBLE);
+        mText4.setVisibility(View.VISIBLE);
+        mTest.onBar4Setup(mBar4, mText4);
+
+        mBar5.setVisibility(View.VISIBLE);
+        mText5.setVisibility(View.VISIBLE);
+        mTest.onBar5Setup(mBar5, mText5);
+    }
+
+
+    void changeTest(TestName testName) {
+        if (mTest != null) {
+            mTest.destroy();
+        }
+        switch(testName) {
+        case LEVELS_VEC3_RELAXED:
+            mTest = new LevelsV4(false, false);
+            break;
+        case LEVELS_VEC4_RELAXED:
+            mTest = new LevelsV4(false, true);
+            break;
+        case LEVELS_VEC3_FULL:
+            mTest = new LevelsV4(true, false);
+            break;
+        case LEVELS_VEC4_FULL:
+            mTest = new LevelsV4(true, true);
+            break;
+        case BLUR_RADIUS_25:
+            mTest = new Blur25(false);
+            break;
+        case INTRINSIC_BLUE_RADIUS_25:
+            mTest = new Blur25(true);
+            break;
+        case GREYSCALE:
+            mTest = new Greyscale();
+            break;
+        case GRAIN:
+            mTest = new Grain();
+            break;
+        case FISHEYE_FULL:
+            mTest = new Fisheye(false, false);
+            break;
+        case FISHEYE_RELAXED:
+            mTest = new Fisheye(false, true);
+            break;
+        case FISHEYE_APPROXIMATE_FULL:
+            mTest = new Fisheye(true, false);
+            break;
+        case FISHEYE_APPROXIMATE_RELAXED:
+            mTest = new Fisheye(true, true);
+            break;
+        case VIGNETTE_FULL:
+            mTest = new Vignette(false, false);
+            break;
+        case VIGNETTE_RELAXED:
+            mTest = new Vignette(false, true);
+            break;
+        case VIGNETTE_APPROXIMATE_FULL:
+            mTest = new Vignette(true, false);
+            break;
+        case VIGNETTE_APPROXIMATE_RELAXED:
+            mTest = new Vignette(true, true);
+            break;
+        case GROUP_TEST_EMULATED:
+            mTest = new GroupTest(false);
+            break;
+        case GROUP_TEST_NATIVE:
+            mTest = new GroupTest(true);
+            break;
+        case CONVOLVE_3X3:
+            mTest = new Convolve3x3(false);
+            break;
+        case INTRINSICS_CONVOLVE_3X3:
+            mTest = new Convolve3x3(true);
+            break;
+        case COLOR_MATRIX:
+            mTest = new ColorMatrix(false, false);
+            break;
+        case INTRINSICS_COLOR_MATRIX:
+            mTest = new ColorMatrix(true, false);
+            break;
+        case INTRINSICS_COLOR_MATRIX_GREY:
+            mTest = new ColorMatrix(true, true);
+            break;
+        case COPY:
+            mTest = new Copy();
+            break;
+        case CROSS_PROCESS_USING_LUT:
+            mTest = new CrossProcess();
+            break;
+        case CONVOLVE_5X5:
+            mTest = new Convolve5x5(false);
+            break;
+        case INTRINSICS_CONVOLVE_5X5:
+            mTest = new Convolve5x5(true);
+            break;
+        case MANDELBROT:
+            mTest = new Mandelbrot();
+            break;
+        case INTRINSICS_BLEND:
+            mTest = new Blend();
+            break;
+        case INTRINSICS_BLUR_25G:
+            mTest = new Blur25G();
+            break;
+        case VIBRANCE:
+            mTest = new Vibrance();
+            break;
+        case BW_FILTER:
+            mTest = new BWFilter();
+            break;
+        case SHADOWS:
+            mTest = new Shadows();
+            break;
+        case CONTRAST:
+            mTest = new Contrast();
+            break;
+        case EXPOSURE:
+            mTest = new Exposure();
+            break;
+        case WHITE_BALANCE:
+            mTest = new WhiteBalance();
+            break;
+        case COLOR_CUBE:
+            mTest = new ColorCube(false);
+            break;
+        case COLOR_CUBE_3D_INTRINSIC:
+            mTest = new ColorCube(true);
+            break;
+        }
+
+        mTest.createBaseTest(this, mBitmapIn, mBitmapIn2, mBitmapOut);
+        setupBars();
+
+        mTest.runTest();
+        updateDisplay();
+        mBenchmarkResult.setText("Result: not run");
+    }
+
+    void setupTests() {
+        mTestSpinner.setAdapter(new ArrayAdapter<TestName>(
+            this, R.layout.spinner_layout, TestName.values()));
+    }
+
+    private AdapterView.OnItemSelectedListener mTestSpinnerListener =
+            new AdapterView.OnItemSelectedListener() {
+                public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
+                    changeTest(TestName.values()[pos]);
+                }
+
+                public void onNothingSelected(AdapterView parent) {
+
+                }
+            };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+
+        mBitmapIn = loadBitmap(R.drawable.img1600x1067);
+        mBitmapIn2 = loadBitmap(R.drawable.img1600x1067b);
+        mBitmapOut = Bitmap.createBitmap(mBitmapIn.getWidth(), mBitmapIn.getHeight(),
+                                         mBitmapIn.getConfig());
+
+        mSurfaceView = (SurfaceView) findViewById(R.id.surface);
+
+        mDisplayView = (ImageView) findViewById(R.id.display);
+        mDisplayView.setImageBitmap(mBitmapOut);
+
+        mSpinner = (Spinner) findViewById(R.id.spinner1);
+
+        mBar1 = (SeekBar) findViewById(R.id.slider1);
+        mBar2 = (SeekBar) findViewById(R.id.slider2);
+        mBar3 = (SeekBar) findViewById(R.id.slider3);
+        mBar4 = (SeekBar) findViewById(R.id.slider4);
+        mBar5 = (SeekBar) findViewById(R.id.slider5);
+
+        mBar1.setOnSeekBarChangeListener(this);
+        mBar2.setOnSeekBarChangeListener(this);
+        mBar3.setOnSeekBarChangeListener(this);
+        mBar4.setOnSeekBarChangeListener(this);
+        mBar5.setOnSeekBarChangeListener(this);
+
+        mText1 = (TextView) findViewById(R.id.slider1Text);
+        mText2 = (TextView) findViewById(R.id.slider2Text);
+        mText3 = (TextView) findViewById(R.id.slider3Text);
+        mText4 = (TextView) findViewById(R.id.slider4Text);
+        mText5 = (TextView) findViewById(R.id.slider5Text);
+
+        mTestSpinner = (Spinner) findViewById(R.id.filterselection);
+        mTestSpinner.setOnItemSelectedListener(mTestSpinnerListener);
+
+        mBenchmarkResult = (TextView) findViewById(R.id.benchmarkText);
+        mBenchmarkResult.setText("Result: not run");
+
+
+        mRS = RenderScript.create(this);
+        mInPixelsAllocation = Allocation.createFromBitmap(mRS, mBitmapIn);
+        mInPixelsAllocation2 = Allocation.createFromBitmap(mRS, mBitmapIn2);
+        mOutPixelsAllocation = Allocation.createFromBitmap(mRS, mBitmapOut);
+
+
+        setupTests();
+        changeTest(TestName.LEVELS_VEC3_RELAXED);
+    }
+
+
+    private Bitmap loadBitmap(int resource) {
+        final BitmapFactory.Options options = new BitmapFactory.Options();
+        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
+        return BitmapFactory.decodeResource(getResources(), resource, options);
+    }
+
+    // button hook
+    public void benchmark(View v) {
+        float t = getBenchmark();
+        //long javaTime = javaFilter();
+        //mBenchmarkResult.setText("RS: " + t + " ms  Java: " + javaTime + " ms");
+        mBenchmarkResult.setText("Result: " + t + " ms");
+        Log.v(TAG, "getBenchmark: Renderscript frame time core ms " + t);
+    }
+
+    public void benchmark_all(View v) {
+        // write result into a file
+        File externalStorage = Environment.getExternalStorageDirectory();
+        if (!externalStorage.canWrite()) {
+            Log.v(TAG, "sdcard is not writable");
+            return;
+        }
+        File resultFile = new File(externalStorage, RESULT_FILE);
+        //resultFile.setWritable(true, false);
+        try {
+            BufferedWriter rsWriter = new BufferedWriter(new FileWriter(resultFile));
+            Log.v(TAG, "Saved results in: " + resultFile.getAbsolutePath());
+            for (TestName tn: TestName.values()) {
+                changeTest(tn);
+                float t = getBenchmark();
+                String s = new String("" + tn.toString() + ", " + t);
+                rsWriter.write(s + "\n");
+                Log.v(TAG, "Test " + s + "ms\n");
+            }
+            rsWriter.close();
+        } catch (IOException e) {
+            Log.v(TAG, "Unable to write result file " + e.getMessage());
+        }
+        changeTest(TestName.LEVELS_VEC3_RELAXED);
+    }
+
+    // For benchmark test
+    public float getBenchmark() {
+        mDoingBenchmark = true;
+
+        mTest.setupBenchmark();
+        long result = 0;
+
+        //Log.v(TAG, "Warming");
+        long t = java.lang.System.currentTimeMillis() + 250;
+        do {
+            mTest.runTest();
+            mTest.finish();
+        } while (t > java.lang.System.currentTimeMillis());
+
+        //Log.v(TAG, "Benchmarking");
+        int ct = 0;
+        t = java.lang.System.currentTimeMillis();
+        do {
+            mTest.runTest();
+            mTest.finish();
+            ct++;
+        } while ((t+1000) > java.lang.System.currentTimeMillis());
+        t = java.lang.System.currentTimeMillis() - t;
+        float ft = (float)t;
+        ft /= ct;
+
+        mTest.exitBenchmark();
+        mDoingBenchmark = false;
+
+        return ft;
+    }
+}
diff --git a/tests/rsSupportMode/src/main/java/com/android/rs/image/LevelsV4.java b/tests/rsSupportMode/src/main/java/com/android/rs/image/LevelsV4.java
new file mode 100644
index 0000000..fbe3727
--- /dev/null
+++ b/tests/rsSupportMode/src/main/java/com/android/rs/image/LevelsV4.java
@@ -0,0 +1,161 @@
+/*
+ * 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.rs.image2;
+
+import java.lang.Math;
+
+import android.support.v8.renderscript.*;
+import android.util.Log;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+
+public class LevelsV4 extends TestBase {
+    private ScriptC_levels_relaxed mScriptR;
+    private ScriptC_levels_full mScriptF;
+    private float mInBlack = 0.0f;
+    private float mOutBlack = 0.0f;
+    private float mInWhite = 255.0f;
+    private float mOutWhite = 255.0f;
+    private float mSaturation = 1.0f;
+
+    Matrix3f satMatrix = new Matrix3f();
+    float mInWMinInB;
+    float mOutWMinOutB;
+    float mOverInWMinInB;
+
+    boolean mUseFull;
+    boolean mUseV4;
+
+    LevelsV4(boolean useFull, boolean useV4) {
+        mUseFull = useFull;
+        mUseV4 = useV4;
+    }
+
+
+    private void setLevels() {
+        mInWMinInB = mInWhite - mInBlack;
+        mOutWMinOutB = mOutWhite - mOutBlack;
+        mOverInWMinInB = 1.f / mInWMinInB;
+
+        mScriptR.set_inBlack(mInBlack);
+        mScriptR.set_outBlack(mOutBlack);
+        mScriptR.set_inWMinInB(mInWMinInB);
+        mScriptR.set_outWMinOutB(mOutWMinOutB);
+        mScriptR.set_overInWMinInB(mOverInWMinInB);
+        mScriptF.set_inBlack(mInBlack);
+        mScriptF.set_outBlack(mOutBlack);
+        mScriptF.set_inWMinInB(mInWMinInB);
+        mScriptF.set_outWMinOutB(mOutWMinOutB);
+        mScriptF.set_overInWMinInB(mOverInWMinInB);
+    }
+
+    private void setSaturation() {
+        float rWeight = 0.299f;
+        float gWeight = 0.587f;
+        float bWeight = 0.114f;
+        float oneMinusS = 1.0f - mSaturation;
+
+        satMatrix.set(0, 0, oneMinusS * rWeight + mSaturation);
+        satMatrix.set(0, 1, oneMinusS * rWeight);
+        satMatrix.set(0, 2, oneMinusS * rWeight);
+        satMatrix.set(1, 0, oneMinusS * gWeight);
+        satMatrix.set(1, 1, oneMinusS * gWeight + mSaturation);
+        satMatrix.set(1, 2, oneMinusS * gWeight);
+        satMatrix.set(2, 0, oneMinusS * bWeight);
+        satMatrix.set(2, 1, oneMinusS * bWeight);
+        satMatrix.set(2, 2, oneMinusS * bWeight + mSaturation);
+        mScriptR.set_colorMat(satMatrix);
+        mScriptF.set_colorMat(satMatrix);
+    }
+
+    public boolean onBar1Setup(SeekBar b, TextView t) {
+        b.setProgress(50);
+        t.setText("Saturation");
+        return true;
+    }
+    public boolean onBar2Setup(SeekBar b, TextView t) {
+        b.setMax(128);
+        b.setProgress(0);
+        t.setText("In Black");
+        return true;
+    }
+    public boolean onBar3Setup(SeekBar b, TextView t) {
+        b.setMax(128);
+        b.setProgress(0);
+        t.setText("Out Black");
+        return true;
+    }
+    public boolean onBar4Setup(SeekBar b, TextView t) {
+        b.setMax(128);
+        b.setProgress(128);
+        t.setText("Out White");
+        return true;
+    }
+    public boolean onBar5Setup(SeekBar b, TextView t) {
+        b.setMax(128);
+        b.setProgress(128);
+        t.setText("Out White");
+        return true;
+    }
+
+    public void onBar1Changed(int progress) {
+        mSaturation = (float)progress / 50.0f;
+        setSaturation();
+    }
+    public void onBar2Changed(int progress) {
+        mInBlack = (float)progress;
+        setLevels();
+    }
+    public void onBar3Changed(int progress) {
+        mOutBlack = (float)progress;
+        setLevels();
+    }
+    public void onBar4Changed(int progress) {
+        mInWhite = (float)progress + 127.0f;
+        setLevels();
+    }
+    public void onBar5Changed(int progress) {
+        mOutWhite = (float)progress + 127.0f;
+        setLevels();
+    }
+
+    public void createTest(android.content.res.Resources res) {
+        mScriptR = new ScriptC_levels_relaxed(mRS, res, R.raw.levels_relaxed);
+        mScriptF = new ScriptC_levels_full(mRS, res, R.raw.levels_full);
+        setSaturation();
+        setLevels();
+    }
+
+    public void runTest() {
+        if (mUseFull) {
+            if (mUseV4) {
+                mScriptF.forEach_root4(mInPixelsAllocation, mOutPixelsAllocation);
+            } else {
+                mScriptF.forEach_root(mInPixelsAllocation, mOutPixelsAllocation);
+            }
+        } else {
+            if (mUseV4) {
+                mScriptR.forEach_root4(mInPixelsAllocation, mOutPixelsAllocation);
+            } else {
+                mScriptR.forEach_root(mInPixelsAllocation, mOutPixelsAllocation);
+            }
+        }
+    }
+
+}
+
diff --git a/tests/rsSupportMode/src/main/java/com/android/rs/image/Mandelbrot.java b/tests/rsSupportMode/src/main/java/com/android/rs/image/Mandelbrot.java
new file mode 100644
index 0000000..1780587
--- /dev/null
+++ b/tests/rsSupportMode/src/main/java/com/android/rs/image/Mandelbrot.java
@@ -0,0 +1,92 @@
+/*
+ * 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.rs.image2;
+
+import java.lang.Math;
+
+import android.util.Log;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+public class Mandelbrot extends TestBase {
+    private ScriptC_mandelbrot mScript;
+
+    public boolean onBar1Setup(SeekBar b, TextView t) {
+        t.setText("Iterations");
+        b.setProgress(0);
+        return true;
+    }
+
+    public void onBar1Changed(int progress) {
+        int iters = progress * 3 + 50;
+        mScript.set_gMaxIteration(iters);
+    }
+
+    public boolean onBar2Setup(SeekBar b, TextView t) {
+        t.setText("Lower Bound: X");
+        b.setProgress(0);
+        return true;
+    }
+
+    public void onBar2Changed(int progress) {
+        float scaleFactor = mScript.get_scaleFactor();
+        // allow viewport to be moved by 2x scale factor
+        float lowerBoundX = -2.f + ((progress / scaleFactor) / 50.f);
+        mScript.set_lowerBoundX(lowerBoundX);
+    }
+
+    public boolean onBar3Setup(SeekBar b, TextView t) {
+        t.setText("Lower Bound: Y");
+        b.setProgress(0);
+        return true;
+    }
+
+    public void onBar3Changed(int progress) {
+        float scaleFactor = mScript.get_scaleFactor();
+        // allow viewport to be moved by 2x scale factor
+        float lowerBoundY = -2.f + ((progress / scaleFactor) / 50.f);
+        mScript.set_lowerBoundY(lowerBoundY);
+    }
+
+    public boolean onBar4Setup(SeekBar b, TextView t) {
+        t.setText("Scale Factor");
+        b.setProgress(0);
+        return true;
+    }
+
+    public void onBar4Changed(int progress) {
+        float scaleFactor = 4.f - (3.96f * (progress / 100.f));
+        mScript.set_scaleFactor(scaleFactor);
+    }
+
+    public void createTest(android.content.res.Resources res) {
+        int width = mOutPixelsAllocation.getType().getX();
+        int height = mOutPixelsAllocation.getType().getY();
+
+        mScript = new ScriptC_mandelbrot(mRS, res, R.raw.mandelbrot);
+        mScript.set_gDimX(width);
+        mScript.set_gDimY(height);
+        mScript.set_gMaxIteration(50);
+    }
+
+    public void runTest() {
+        mScript.forEach_root(mOutPixelsAllocation);
+        mRS.finish();
+    }
+
+}
+
diff --git a/tests/rsSupportMode/src/main/java/com/android/rs/image/Shadows.java b/tests/rsSupportMode/src/main/java/com/android/rs/image/Shadows.java
new file mode 100644
index 0000000..353c56d
--- /dev/null
+++ b/tests/rsSupportMode/src/main/java/com/android/rs/image/Shadows.java
@@ -0,0 +1,32 @@
+/*
+ * 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.rs.image2;
+
+
+public class Shadows extends TestBase {
+    private ScriptC_shadows mScript;
+
+    public void createTest(android.content.res.Resources res) {
+        mScript = new ScriptC_shadows(mRS);
+    }
+
+    public void runTest() {
+        mScript.invoke_prepareShadows(50.f);
+        mScript.forEach_shadowsKernel(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
+}
diff --git a/tests/rsSupportMode/src/main/java/com/android/rs/image/TestBase.java b/tests/rsSupportMode/src/main/java/com/android/rs/image/TestBase.java
new file mode 100644
index 0000000..eeabc73
--- /dev/null
+++ b/tests/rsSupportMode/src/main/java/com/android/rs/image/TestBase.java
@@ -0,0 +1,128 @@
+/*
+ * 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.rs.image2;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.graphics.BitmapFactory;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.support.v8.renderscript.*;
+import android.view.SurfaceView;
+import android.view.SurfaceHolder;
+import android.widget.ImageView;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import android.view.View;
+import android.util.Log;
+import java.lang.Math;
+import android.widget.Spinner;
+
+public class TestBase  {
+    protected final String TAG = "Img";
+
+    protected RenderScript mRS;
+    protected Allocation mInPixelsAllocation;
+    protected Allocation mInPixelsAllocation2;
+    protected Allocation mOutPixelsAllocation;
+
+    protected ImageProcessingActivity2 act;
+
+    // Override to use UI elements
+    public void onBar1Changed(int progress) {
+    }
+    public void onBar2Changed(int progress) {
+    }
+    public void onBar3Changed(int progress) {
+    }
+    public void onBar4Changed(int progress) {
+    }
+    public void onBar5Changed(int progress) {
+    }
+
+    // Override to use UI elements
+    // Unused bars will be hidden.
+    public boolean onBar1Setup(SeekBar b, TextView t) {
+        b.setVisibility(View.INVISIBLE);
+        t.setVisibility(View.INVISIBLE);
+        return false;
+    }
+    public boolean onBar2Setup(SeekBar b, TextView t) {
+        b.setVisibility(View.INVISIBLE);
+        t.setVisibility(View.INVISIBLE);
+        return false;
+    }
+    public boolean onBar3Setup(SeekBar b, TextView t) {
+        b.setVisibility(View.INVISIBLE);
+        t.setVisibility(View.INVISIBLE);
+        return false;
+    }
+    public boolean onBar4Setup(SeekBar b, TextView t) {
+        b.setVisibility(View.INVISIBLE);
+        t.setVisibility(View.INVISIBLE);
+        return false;
+    }
+    public boolean onBar5Setup(SeekBar b, TextView t) {
+        b.setVisibility(View.INVISIBLE);
+        t.setVisibility(View.INVISIBLE);
+        return false;
+    }
+
+    public boolean onSpinner1Setup(Spinner s) {
+        s.setVisibility(View.INVISIBLE);
+        return false;
+    }
+
+    public final void createBaseTest(ImageProcessingActivity2 ipact, Bitmap b, Bitmap b2, Bitmap outb) {
+        act = ipact;
+        mRS = ipact.mRS;
+
+        mInPixelsAllocation = ipact.mInPixelsAllocation;
+        mInPixelsAllocation2 = ipact.mInPixelsAllocation2;
+        mOutPixelsAllocation = ipact.mOutPixelsAllocation;
+
+        createTest(act.getResources());
+    }
+
+    // Must override
+    public void createTest(android.content.res.Resources res) {
+    }
+
+    // Must override
+    public void runTest() {
+    }
+
+    public void finish() {
+        mRS.finish();
+    }
+
+    public void destroy() {
+    }
+
+    public void updateBitmap(Bitmap b) {
+        mOutPixelsAllocation.copyTo(b);
+    }
+
+    // Override to configure specific benchmark config.
+    public void setupBenchmark() {
+    }
+
+    // Override to reset after benchmark.
+    public void exitBenchmark() {
+    }
+}
diff --git a/tests/rsSupportMode/src/main/java/com/android/rs/image/Vibrance.java b/tests/rsSupportMode/src/main/java/com/android/rs/image/Vibrance.java
new file mode 100644
index 0000000..37c0aa7
--- /dev/null
+++ b/tests/rsSupportMode/src/main/java/com/android/rs/image/Vibrance.java
@@ -0,0 +1,32 @@
+/*
+ * 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.rs.image2;
+
+public class Vibrance extends TestBase {
+    private ScriptC_vibrance mScript;
+
+    public void createTest(android.content.res.Resources res) {
+        mScript = new ScriptC_vibrance(mRS);
+    }
+
+    public void runTest() {
+        mScript.set_vibrance(50.f);
+        mScript.invoke_prepareVibrance();
+        mScript.forEach_vibranceKernel(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
+}
diff --git a/tests/rsSupportMode/src/main/java/com/android/rs/image/Vignette.java b/tests/rsSupportMode/src/main/java/com/android/rs/image/Vignette.java
new file mode 100644
index 0000000..9f6d34d
--- /dev/null
+++ b/tests/rsSupportMode/src/main/java/com/android/rs/image/Vignette.java
@@ -0,0 +1,150 @@
+/*
+ * 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.rs.image2;
+
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+public class Vignette extends TestBase {
+    private ScriptC_vignette_full mScript_full = null;
+    private ScriptC_vignette_relaxed mScript_relaxed = null;
+    private ScriptC_vignette_approx_full mScript_approx_full = null;
+    private ScriptC_vignette_approx_relaxed mScript_approx_relaxed = null;
+    private final boolean approx;
+    private final boolean relaxed;
+    private float center_x = 0.5f;
+    private float center_y = 0.5f;
+    private float scale = 0.5f;
+    private float shade = 0.5f;
+    private float slope = 20.0f;
+
+    public Vignette(boolean approx, boolean relaxed) {
+        this.approx = approx;
+        this.relaxed = relaxed;
+    }
+
+    public boolean onBar1Setup(SeekBar b, TextView t) {
+        t.setText("Scale");
+        b.setMax(100);
+        b.setProgress(25);
+        return true;
+    }
+    public boolean onBar2Setup(SeekBar b, TextView t) {
+        t.setText("Shade");
+        b.setMax(100);
+        b.setProgress(50);
+        return true;
+    }
+    public boolean onBar3Setup(SeekBar b, TextView t) {
+        t.setText("Slope");
+        b.setMax(100);
+        b.setProgress(20);
+        return true;
+    }
+    public boolean onBar4Setup(SeekBar b, TextView t) {
+        t.setText("Shift center X");
+        b.setMax(100);
+        b.setProgress(50);
+        return true;
+    }
+    public boolean onBar5Setup(SeekBar b, TextView t) {
+        t.setText("Shift center Y");
+        b.setMax(100);
+        b.setProgress(50);
+        return true;
+    }
+
+    public void onBar1Changed(int progress) {
+        scale = progress / 50.0f;
+        do_init();
+    }
+    public void onBar2Changed(int progress) {
+        shade = progress / 100.0f;
+        do_init();
+    }
+    public void onBar3Changed(int progress) {
+        slope = (float)progress;
+        do_init();
+    }
+    public void onBar4Changed(int progress) {
+        center_x = progress / 100.0f;
+        do_init();
+    }
+    public void onBar5Changed(int progress) {
+        center_y = progress / 100.0f;
+        do_init();
+    }
+
+    private void do_init() {
+        if (approx) {
+            if (relaxed)
+                mScript_approx_relaxed.invoke_init_vignette(
+                        mInPixelsAllocation.getType().getX(),
+                        mInPixelsAllocation.getType().getY(), center_x,
+                        center_y, scale, shade, slope);
+            else
+                mScript_approx_full.invoke_init_vignette(
+                        mInPixelsAllocation.getType().getX(),
+                        mInPixelsAllocation.getType().getY(), center_x,
+                        center_y, scale, shade, slope);
+        } else if (relaxed)
+            mScript_relaxed.invoke_init_vignette(
+                    mInPixelsAllocation.getType().getX(),
+                    mInPixelsAllocation.getType().getY(), center_x, center_y,
+                    scale, shade, slope);
+        else
+            mScript_full.invoke_init_vignette(
+                    mInPixelsAllocation.getType().getX(),
+                    mInPixelsAllocation.getType().getY(), center_x, center_y,
+                    scale, shade, slope);
+    }
+
+    public void createTest(android.content.res.Resources res) {
+        if (approx) {
+            if (relaxed)
+                mScript_approx_relaxed = new ScriptC_vignette_approx_relaxed(
+                        mRS, res, R.raw.vignette_approx_relaxed);
+            else
+                mScript_approx_full = new ScriptC_vignette_approx_full(
+                        mRS, res, R.raw.vignette_approx_full);
+        } else if (relaxed)
+            mScript_relaxed = new ScriptC_vignette_relaxed(mRS, res,
+                    R.raw.vignette_relaxed);
+        else
+            mScript_full = new ScriptC_vignette_full(mRS, res,
+                    R.raw.vignette_full);
+        do_init();
+    }
+
+    public void runTest() {
+        if (approx) {
+            if (relaxed)
+                mScript_approx_relaxed.forEach_root(mInPixelsAllocation,
+                        mOutPixelsAllocation);
+            else
+                mScript_approx_full.forEach_root(mInPixelsAllocation,
+                        mOutPixelsAllocation);
+        } else if (relaxed)
+            mScript_relaxed.forEach_root(mInPixelsAllocation,
+                    mOutPixelsAllocation);
+        else
+            mScript_full.forEach_root(mInPixelsAllocation,
+                    mOutPixelsAllocation);
+    }
+
+}
+
diff --git a/tests/rsSupportMode/src/main/java/com/android/rs/image/WhiteBalance.java b/tests/rsSupportMode/src/main/java/com/android/rs/image/WhiteBalance.java
new file mode 100644
index 0000000..658e3b1
--- /dev/null
+++ b/tests/rsSupportMode/src/main/java/com/android/rs/image/WhiteBalance.java
@@ -0,0 +1,38 @@
+/*
+ * 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.rs.image2;
+
+import java.lang.Math;
+
+import android.support.v8.renderscript.*;
+
+public class WhiteBalance extends TestBase {
+    private ScriptC_wbalance mScript;
+
+    public void createTest(android.content.res.Resources res) {
+        mScript = new ScriptC_wbalance(mRS);
+    }
+
+    public void runTest() {
+        mScript.set_histogramSource(mInPixelsAllocation);
+        mScript.set_histogramWidth(mInPixelsAllocation.getType().getX());
+        mScript.set_histogramHeight(mInPixelsAllocation.getType().getY());
+        mScript.invoke_prepareWhiteBalance();
+        mScript.forEach_whiteBalanceKernel(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
+}
diff --git a/tests/rsSupportMode/src/main/res/drawable-nodpi/city.png b/tests/rsSupportMode/src/main/res/drawable-nodpi/city.png
new file mode 100644
index 0000000..856eeff
--- /dev/null
+++ b/tests/rsSupportMode/src/main/res/drawable-nodpi/city.png
Binary files differ
diff --git a/tests/rsSupportMode/src/main/res/drawable-nodpi/img1600x1067.jpg b/tests/rsSupportMode/src/main/res/drawable-nodpi/img1600x1067.jpg
new file mode 100644
index 0000000..05d3ee2
--- /dev/null
+++ b/tests/rsSupportMode/src/main/res/drawable-nodpi/img1600x1067.jpg
Binary files differ
diff --git a/tests/rsSupportMode/src/main/res/drawable-nodpi/img1600x1067b.jpg b/tests/rsSupportMode/src/main/res/drawable-nodpi/img1600x1067b.jpg
new file mode 100644
index 0000000..aed0781
--- /dev/null
+++ b/tests/rsSupportMode/src/main/res/drawable-nodpi/img1600x1067b.jpg
Binary files differ
diff --git a/tests/rsSupportMode/src/main/res/layout/main.xml b/tests/rsSupportMode/src/main/res/layout/main.xml
new file mode 100644
index 0000000..f0a2b92
--- /dev/null
+++ b/tests/rsSupportMode/src/main/res/layout/main.xml
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+            android:orientation="vertical"
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent"
+            android:id="@+id/toplevel">
+    <SurfaceView
+        android:id="@+id/surface"
+        android:layout_width="1dip"
+        android:layout_height="1dip" />
+    <ScrollView
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent">
+            <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:orientation="vertical"
+                android:layout_width="fill_parent"
+                android:layout_height="fill_parent">
+            <ImageView
+                android:id="@+id/display"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" />
+            <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:orientation="horizontal"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content">
+                    <Button
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:text="@string/benchmark"
+                        android:onClick="benchmark"/>
+                    <TextView
+                        android:id="@+id/benchmarkText"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:textSize="8pt"
+                        android:text="@string/saturation"/>
+            </LinearLayout>
+            <Spinner
+                android:id="@+id/filterselection"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"/>
+            <Spinner
+                android:id="@+id/spinner1"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"/>
+            <TextView
+                android:id="@+id/slider1Text"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:textSize="8pt"
+                android:layout_marginLeft="10sp"
+                android:layout_marginTop="15sp"
+                android:text="@string/saturation"/>
+             <SeekBar
+                android:id="@+id/slider1"
+                android:layout_marginLeft="10sp"
+                android:layout_marginRight="10sp"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"/>
+            <TextView
+                android:id="@+id/slider2Text"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:textSize="8pt"
+                android:layout_marginLeft="10sp"
+                android:layout_marginTop="15sp"
+                android:text="@string/gamma"/>
+            <SeekBar
+                android:id="@+id/slider2"
+                android:layout_marginLeft="10sp"
+                android:layout_marginRight="10sp"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"/>
+            <TextView
+                android:id="@+id/slider3Text"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="10sp"
+                android:layout_marginTop="15sp"
+                android:textSize="8pt"
+                android:text="@string/out_white"/>
+            <SeekBar
+                android:id="@+id/slider3"
+                android:layout_marginLeft="10sp"
+                android:layout_marginRight="10sp"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"/>
+            <TextView
+                android:id="@+id/slider4Text"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:textSize="8pt"
+                android:layout_marginLeft="10sp"
+                android:layout_marginTop="15sp"
+                android:text="@string/in_white"/>
+            <SeekBar
+                android:id="@+id/slider4"
+                android:layout_marginLeft="10sp"
+                android:layout_marginRight="10sp"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"/>
+            <TextView
+                android:id="@+id/slider5Text"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:textSize="8pt"
+                android:layout_marginLeft="10sp"
+                android:layout_marginTop="15sp"
+                android:text="@string/in_white"/>
+            <SeekBar
+                android:id="@+id/slider5"
+                android:layout_marginLeft="10sp"
+                android:layout_marginRight="10sp"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"/>
+            <Button
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/benchmark_all"
+                    android:onClick="benchmark_all"/>
+            </LinearLayout>
+    </ScrollView>
+</LinearLayout>
+
diff --git a/tests/rsSupportMode/src/main/res/layout/spinner_layout.xml b/tests/rsSupportMode/src/main/res/layout/spinner_layout.xml
new file mode 100644
index 0000000..8196bbf
--- /dev/null
+++ b/tests/rsSupportMode/src/main/res/layout/spinner_layout.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- 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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:padding="10dp"
+    android:textSize="16sp"
+/>
diff --git a/tests/rsSupportMode/src/main/res/values/strings.xml b/tests/rsSupportMode/src/main/res/values/strings.xml
new file mode 100644
index 0000000..a7dd165
--- /dev/null
+++ b/tests/rsSupportMode/src/main/res/values/strings.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright (C) 2008 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- General -->
+    <skip />
+    <!--slider label -->
+    <string name="blur_description">Blur Radius</string>
+    <string name="in_white">In White</string>
+    <string name="out_white">Out White</string>
+    <string name="in_black">In Black</string>
+    <string name="out_black">Out Black</string>
+    <string name="gamma">Gamma</string>
+    <string name="saturation">Saturation</string>
+    <string name="benchmark">Benchmark</string>
+    <string name="benchmark_all">Benchmark All</string>
+
+</resources>
diff --git a/tests/rsSupportMode/src/main/rs/blend.rs b/tests/rsSupportMode/src/main/rs/blend.rs
new file mode 100644
index 0000000..9ec1246
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/blend.rs
@@ -0,0 +1,23 @@
+// Copyright (C) 2011 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.
+
+#include "ip.rsh"
+
+uchar alpha = 0x0;
+
+void setImageAlpha(uchar4 *v_out, uint32_t x, uint32_t y) {
+  v_out->rgba = convert_uchar4((convert_uint4(v_out->rgba) * alpha) >> (uint4)8);
+  v_out->a = alpha;
+}
+
diff --git a/tests/rsSupportMode/src/main/rs/bwfilter.rs b/tests/rsSupportMode/src/main/rs/bwfilter.rs
new file mode 100644
index 0000000..e706d44
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/bwfilter.rs
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+//#pragma rs_fp_relaxed
+
+static float sr = 0.f;
+static float sg = 0.f;
+static float sb = 0.f;
+
+void prepareBwFilter(uint32_t rw, uint32_t gw, uint32_t bw) {
+
+    sr = rw;
+    sg = gw;
+    sb = bw;
+
+    float imageMin = min(sg,sb);
+    imageMin = fmin(sr,imageMin);
+    float imageMax = max(sg,sb);
+    imageMax = fmax(sr,imageMax);
+    float avg = (imageMin + imageMax)/2;
+    sb /= avg;
+    sg /= avg;
+    sr /= avg;
+
+}
+
+void bwFilterKernel(const uchar4 *in, uchar4 *out) {
+    float r = in->r * sr;
+    float g = in->g * sg;
+    float b = in->b * sb;
+    float localMin, localMax, avg;
+    localMin = fmin(g,b);
+    localMin = fmin(r,localMin);
+    localMax = fmax(g,b);
+    localMax = fmax(r,localMax);
+    avg = (localMin+localMax) * 0.5f;
+    out->r = out->g = out->b = rsClamp(avg, 0, 255);
+}
diff --git a/tests/rsSupportMode/src/main/rs/colorcube.rs b/tests/rsSupportMode/src/main/rs/colorcube.rs
new file mode 100644
index 0000000..c0d6ace
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/colorcube.rs
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+#pragma rs_fp_relaxed
+
+
+static rs_allocation gCube;
+static int4 gDims;
+static int4 gCoordMul;
+
+
+void setCube(rs_allocation c) {
+    gCube = c;
+    gDims.x = rsAllocationGetDimX(gCube);
+    gDims.y = rsAllocationGetDimY(gCube);
+    gDims.z = rsAllocationGetDimZ(gCube);
+    gDims.w = 0;
+
+    float4 m = (float4)(1.f / 255.f) * convert_float4(gDims - 1);
+    gCoordMul = convert_int4(m * (float4)0x10000);
+
+    rsDebug("dims", gDims);
+    rsDebug("gCoordMul", gCoordMul);
+}
+
+void root(const uchar4 *in, uchar4 *out, uint32_t x, uint32_t y) {
+    //rsDebug("root", in);
+
+    int4 baseCoord = convert_int4(*in) * gCoordMul;
+    int4 coord1 = baseCoord >> (int4)16;
+    int4 coord2 = min(coord1 + 1, gDims - 1);
+
+    int4 weight2 = baseCoord & 0xffff;
+    int4 weight1 = (int4)0x10000 - weight2;
+
+    uint4 v000 = convert_uint4(rsGetElementAt_uchar4(gCube, coord1.x, coord1.y, coord1.z));
+    uint4 v100 = convert_uint4(rsGetElementAt_uchar4(gCube, coord2.x, coord1.y, coord1.z));
+    uint4 v010 = convert_uint4(rsGetElementAt_uchar4(gCube, coord1.x, coord2.y, coord1.z));
+    uint4 v110 = convert_uint4(rsGetElementAt_uchar4(gCube, coord2.x, coord2.y, coord1.z));
+    uint4 v001 = convert_uint4(rsGetElementAt_uchar4(gCube, coord1.x, coord1.y, coord2.z));
+    uint4 v101 = convert_uint4(rsGetElementAt_uchar4(gCube, coord2.x, coord1.y, coord2.z));
+    uint4 v011 = convert_uint4(rsGetElementAt_uchar4(gCube, coord1.x, coord2.y, coord2.z));
+    uint4 v111 = convert_uint4(rsGetElementAt_uchar4(gCube, coord2.x, coord2.y, coord2.z));
+
+    uint4 yz00 = ((v000 * weight1.x) + (v100 * weight2.x)) >> (uint4)8;
+    uint4 yz10 = ((v010 * weight1.x) + (v110 * weight2.x)) >> (uint4)8;
+    uint4 yz01 = ((v001 * weight1.x) + (v101 * weight2.x)) >> (uint4)8;
+    uint4 yz11 = ((v011 * weight1.x) + (v111 * weight2.x)) >> (uint4)8;
+
+    uint4 z0 = ((yz00 * weight1.y) + (yz10 * weight2.y)) >> (uint4)16;
+    uint4 z1 = ((yz01 * weight1.y) + (yz11 * weight2.y)) >> (uint4)16;
+
+    uint4 v = ((z0 * weight1.z) + (z1 * weight2.z)) >> (uint4)16;
+    uint4 v2 = (v + 0x7f) >> (uint4)8;
+
+    *out = convert_uchar4(v2);
+    out->a = 0xff;
+
+    #if 0
+    if (in->r != out->r) {
+        rsDebug("dr", in->r - out->r);
+        //rsDebug("in", convert_int4(*in));
+        //rsDebug("coord1", coord1);
+        //rsDebug("coord2", coord2);
+        //rsDebug("weight1", weight1);
+        //rsDebug("weight2", weight2);
+        //rsDebug("yz00", yz00);
+        //rsDebug("z0", z0);
+        //rsDebug("v", v);
+        //rsDebug("v2", v2);
+        //rsDebug("out", convert_int4(*out));
+    }
+    #endif
+}
+
diff --git a/tests/rsSupportMode/src/main/rs/colormatrix.fs b/tests/rsSupportMode/src/main/rs/colormatrix.fs
new file mode 100644
index 0000000..86fb248
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/colormatrix.fs
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+
+static rs_matrix4x4 Mat;
+
+void init() {
+    rsMatrixLoadIdentity(&Mat);
+}
+
+void setMatrix(rs_matrix4x4 m) {
+    Mat = m;
+}
+
+uchar4 __attribute__((kernel)) root(uchar4 in) {
+    float4 f = convert_float4(in);
+    f = rsMatrixMultiply(&Mat, f);
+    f = clamp(f, 0.f, 255.f);
+    return convert_uchar4(f);
+}
+
diff --git a/tests/rsSupportMode/src/main/rs/contrast.rs b/tests/rsSupportMode/src/main/rs/contrast.rs
new file mode 100644
index 0000000..d3743d3
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/contrast.rs
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+
+static float brightM = 0.f;
+static float brightC = 0.f;
+
+void setBright(float v) {
+    brightM = pow(2.f, v / 100.f);
+    brightC = 127.f - brightM * 127.f;
+}
+
+void contrast(const uchar4 *in, uchar4 *out)
+{
+#if 0
+    out->r = rsClamp((int)(brightM * in->r + brightC), 0, 255);
+    out->g = rsClamp((int)(brightM * in->g + brightC), 0, 255);
+    out->b = rsClamp((int)(brightM * in->b + brightC), 0, 255);
+#else
+    float3 v = convert_float3(in->rgb) * brightM + brightC;
+    out->rgb = convert_uchar3(clamp(v, 0.f, 255.f));
+#endif
+}
diff --git a/tests/rsSupportMode/src/main/rs/convolve5x5.fs b/tests/rsSupportMode/src/main/rs/convolve5x5.fs
new file mode 100644
index 0000000..922a593
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/convolve5x5.fs
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+
+int32_t gWidth;
+int32_t gHeight;
+rs_allocation gIn;
+
+float gCoeffs[25];
+
+uchar4 __attribute__((kernel)) root(uint32_t x, uint32_t y) {
+    uint32_t x0 = max((int32_t)x-2, 0);
+    uint32_t x1 = max((int32_t)x-1, 0);
+    uint32_t x2 = x;
+    uint32_t x3 = min((int32_t)x+1, gWidth-1);
+    uint32_t x4 = min((int32_t)x+2, gWidth-1);
+
+    uint32_t y0 = max((int32_t)y-2, 0);
+    uint32_t y1 = max((int32_t)y-1, 0);
+    uint32_t y2 = y;
+    uint32_t y3 = min((int32_t)y+1, gHeight-1);
+    uint32_t y4 = min((int32_t)y+2, gHeight-1);
+
+    float4 p0 = convert_float4(rsGetElementAt_uchar4(gIn, x0, y0)) * gCoeffs[0]
+              + convert_float4(rsGetElementAt_uchar4(gIn, x1, y0)) * gCoeffs[1]
+              + convert_float4(rsGetElementAt_uchar4(gIn, x2, y0)) * gCoeffs[2]
+              + convert_float4(rsGetElementAt_uchar4(gIn, x3, y0)) * gCoeffs[3]
+              + convert_float4(rsGetElementAt_uchar4(gIn, x4, y0)) * gCoeffs[4];
+
+    float4 p1 = convert_float4(rsGetElementAt_uchar4(gIn, x0, y1)) * gCoeffs[5]
+              + convert_float4(rsGetElementAt_uchar4(gIn, x1, y1)) * gCoeffs[6]
+              + convert_float4(rsGetElementAt_uchar4(gIn, x2, y1)) * gCoeffs[7]
+              + convert_float4(rsGetElementAt_uchar4(gIn, x3, y1)) * gCoeffs[8]
+              + convert_float4(rsGetElementAt_uchar4(gIn, x4, y1)) * gCoeffs[9];
+
+    float4 p2 = convert_float4(rsGetElementAt_uchar4(gIn, x0, y2)) * gCoeffs[10]
+              + convert_float4(rsGetElementAt_uchar4(gIn, x1, y2)) * gCoeffs[11]
+              + convert_float4(rsGetElementAt_uchar4(gIn, x2, y2)) * gCoeffs[12]
+              + convert_float4(rsGetElementAt_uchar4(gIn, x3, y2)) * gCoeffs[13]
+              + convert_float4(rsGetElementAt_uchar4(gIn, x4, y2)) * gCoeffs[14];
+
+    float4 p3 = convert_float4(rsGetElementAt_uchar4(gIn, x0, y3)) * gCoeffs[15]
+              + convert_float4(rsGetElementAt_uchar4(gIn, x1, y3)) * gCoeffs[16]
+              + convert_float4(rsGetElementAt_uchar4(gIn, x2, y3)) * gCoeffs[17]
+              + convert_float4(rsGetElementAt_uchar4(gIn, x3, y3)) * gCoeffs[18]
+              + convert_float4(rsGetElementAt_uchar4(gIn, x4, y3)) * gCoeffs[19];
+
+    float4 p4 = convert_float4(rsGetElementAt_uchar4(gIn, x0, y4)) * gCoeffs[20]
+              + convert_float4(rsGetElementAt_uchar4(gIn, x1, y4)) * gCoeffs[21]
+              + convert_float4(rsGetElementAt_uchar4(gIn, x2, y4)) * gCoeffs[22]
+              + convert_float4(rsGetElementAt_uchar4(gIn, x3, y4)) * gCoeffs[23]
+              + convert_float4(rsGetElementAt_uchar4(gIn, x4, y4)) * gCoeffs[24];
+
+    p0 = clamp(p0 + p1 + p2 + p3 + p4, 0.f, 255.f);
+    return convert_uchar4(p0);
+}
+
+
diff --git a/tests/rsSupportMode/src/main/rs/copy.fs b/tests/rsSupportMode/src/main/rs/copy.fs
new file mode 100644
index 0000000..6595874
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/copy.fs
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+
+uchar4 __attribute__((kernel)) root(uchar4 v_in) {
+    return v_in;
+}
+
+
diff --git a/tests/rsSupportMode/src/main/rs/exposure.rs b/tests/rsSupportMode/src/main/rs/exposure.rs
new file mode 100644
index 0000000..0f05cb9
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/exposure.rs
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+
+static float bright = 0.f;
+
+void setBright(float v) {
+    bright = 255.f / (255.f - v);
+}
+
+void exposure(const uchar4 *in, uchar4 *out)
+{
+    out->r = rsClamp((int)(bright * in->r), 0, 255);
+    out->g = rsClamp((int)(bright * in->g), 0, 255);
+    out->b = rsClamp((int)(bright * in->b), 0, 255);
+}
+
diff --git a/tests/rsSupportMode/src/main/rs/fisheye.rsh b/tests/rsSupportMode/src/main/rs/fisheye.rsh
new file mode 100644
index 0000000..2eacb7d
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/fisheye.rsh
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+rs_allocation in_alloc;
+rs_sampler sampler;
+
+static float2 center, neg_center, inv_dimensions, axis_scale;
+static float alpha, radius2, factor;
+
+void init_filter(uint32_t dim_x, uint32_t dim_y, float center_x, float center_y, float k) {
+    center.x = center_x;
+    center.y = center_y;
+    neg_center = -center;
+    inv_dimensions.x = 1.f / (float)dim_x;
+    inv_dimensions.y = 1.f / (float)dim_y;
+    alpha = k * 2.0f + 0.75f;
+
+    axis_scale = (float2)1.f;
+    if (dim_x > dim_y)
+        axis_scale.y = (float)dim_y / (float)dim_x;
+    else
+        axis_scale.x = (float)dim_x / (float)dim_y;
+    
+    const float bound2 = 0.25f * (axis_scale.x*axis_scale.x + axis_scale.y*axis_scale.y);
+    const float bound = sqrt(bound2);
+    const float radius = 1.15f * bound;
+    radius2 = radius*radius;
+    const float max_radian = M_PI_2 - atan(alpha / bound * sqrt(radius2 - bound2));
+    factor = bound / max_radian;
+}
+
+uchar4 __attribute__((kernel)) root(uint32_t x, uint32_t y) {
+    // Convert x and y to floating point coordinates with center as origin
+    const float2 inCoord = {(float)x, (float)y};
+    const float2 coord = mad(inCoord, inv_dimensions, neg_center);
+    const float2 scaledCoord = axis_scale * coord;
+    const float dist2 = scaledCoord.x*scaledCoord.x + scaledCoord.y*scaledCoord.y;
+    const float inv_dist = rsqrt(dist2);
+    const float radian = M_PI_2 - atan((alpha * sqrt(radius2 - dist2)) * inv_dist);
+    const float scalar = radian * factor * inv_dist;
+    const float2 new_coord = mad(coord, scalar, center);
+    const float4 fout = rsSample(in_alloc, sampler, new_coord);
+    return rsPackColorTo8888(fout);
+}
+
diff --git a/tests/rsSupportMode/src/main/rs/fisheye_approx.rsh b/tests/rsSupportMode/src/main/rs/fisheye_approx.rsh
new file mode 100644
index 0000000..fcf0a3d
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/fisheye_approx.rsh
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+rs_allocation in_alloc;
+rs_sampler sampler;
+
+static float2 center, neg_center, inv_dimensions, axis_scale;
+static float alpha, radius2, factor;
+
+void init_filter(uint32_t dim_x, uint32_t dim_y, float center_x, float center_y, float k) {
+    center.x = center_x;
+    center.y = center_y;
+    neg_center = -center;
+    inv_dimensions.x = 1.f / (float)dim_x;
+    inv_dimensions.y = 1.f / (float)dim_y;
+    alpha = k * 2.0f + 0.75f;
+
+    axis_scale = (float2)1.f;
+    if (dim_x > dim_y)
+        axis_scale.y = (float)dim_y / (float)dim_x;
+    else
+        axis_scale.x = (float)dim_x / (float)dim_y;
+
+    const float bound2 = 0.25f * (axis_scale.x*axis_scale.x + axis_scale.y*axis_scale.y);
+    const float bound = sqrt(bound2);
+    const float radius = 1.15f * bound;
+    radius2 = radius*radius;
+    const float max_radian = M_PI_2 - atan(alpha / bound * sqrt(radius2 - bound2));
+    factor = bound / max_radian;
+}
+
+uchar4 __attribute__((kernel)) root(uint32_t x, uint32_t y) {
+    // Convert x and y to floating point coordinates with center as origin
+    const float2 inCoord = {(float)x, (float)y};
+    const float2 coord = mad(inCoord, inv_dimensions, neg_center);
+    const float2 scaledCoord = axis_scale * coord;
+    const float dist2 = scaledCoord.x*scaledCoord.x + scaledCoord.y*scaledCoord.y;
+    const float inv_dist = half_rsqrt(dist2);
+    const float radian = M_PI_2 - atan((alpha * half_sqrt(radius2 - dist2)) * inv_dist);
+    const float scalar = radian * factor * inv_dist;
+    const float2 new_coord = mad(coord, scalar, center);
+    const float4 fout = rsSample(in_alloc, sampler, new_coord);
+    return rsPackColorTo8888(fout);
+}
+
diff --git a/tests/rsSupportMode/src/main/rs/fisheye_approx_full.rs b/tests/rsSupportMode/src/main/rs/fisheye_approx_full.rs
new file mode 100644
index 0000000..ed69ff4
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/fisheye_approx_full.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+
+#include "fisheye_approx.rsh"
+
diff --git a/tests/rsSupportMode/src/main/rs/fisheye_approx_relaxed.fs b/tests/rsSupportMode/src/main/rs/fisheye_approx_relaxed.fs
new file mode 100644
index 0000000..ed69ff4
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/fisheye_approx_relaxed.fs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+
+#include "fisheye_approx.rsh"
+
diff --git a/tests/rsSupportMode/src/main/rs/fisheye_full.rs b/tests/rsSupportMode/src/main/rs/fisheye_full.rs
new file mode 100644
index 0000000..f986b5d
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/fisheye_full.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+
+#include "fisheye.rsh"
+
diff --git a/tests/rsSupportMode/src/main/rs/fisheye_relaxed.fs b/tests/rsSupportMode/src/main/rs/fisheye_relaxed.fs
new file mode 100644
index 0000000..f986b5d
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/fisheye_relaxed.fs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+
+#include "fisheye.rsh"
+
diff --git a/tests/rsSupportMode/src/main/rs/grain.fs b/tests/rsSupportMode/src/main/rs/grain.fs
new file mode 100644
index 0000000..2e62cd7
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/grain.fs
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+
+uchar __attribute__((kernel)) genRand() {
+    return (uchar)rsRand(0xff);
+}
+
+/*
+ * Convolution matrix of distance 2 with fixed point of 'kShiftBits' bits
+ * shifted. Thus the sum of this matrix should be 'kShiftValue'. Entries of
+ * small values are not calculated to gain efficiency.
+ * The order ot pixels represented in this matrix is:
+ *  1  2  3
+ *  4  0  5
+ *  6  7  8
+ *  and the matrix should be: {230, 56, 114, 56, 114, 114, 56, 114, 56}.
+ *  However, since most of the valus are identical, we only use the first three
+ *  entries and the entries corresponding to the pixels is:
+ *  1  2  1
+ *  2  0  2
+ *  1  2  1
+ */
+
+int32_t gWMask;
+int32_t gHMask;
+
+rs_allocation gBlendSource;
+uchar __attribute__((kernel)) blend9(uint32_t x, uint32_t y) {
+    uint32_t x1 = (x-1) & gWMask;
+    uint32_t x2 = (x+1) & gWMask;
+    uint32_t y1 = (y-1) & gHMask;
+    uint32_t y2 = (y+1) & gHMask;
+
+    uint p00 = 56 *  rsGetElementAt_uchar(gBlendSource, x1, y1);
+    uint p01 = 114 * rsGetElementAt_uchar(gBlendSource, x, y1);
+    uint p02 = 56 *  rsGetElementAt_uchar(gBlendSource, x2, y1);
+    uint p10 = 114 * rsGetElementAt_uchar(gBlendSource, x1, y);
+    uint p11 = 230 * rsGetElementAt_uchar(gBlendSource, x, y);
+    uint p12 = 114 * rsGetElementAt_uchar(gBlendSource, x2, y);
+    uint p20 = 56 *  rsGetElementAt_uchar(gBlendSource, x1, y2);
+    uint p21 = 114 * rsGetElementAt_uchar(gBlendSource, x, y2);
+    uint p22 = 56 *  rsGetElementAt_uchar(gBlendSource, x2, y2);
+
+    p00 += p01;
+    p02 += p10;
+    p11 += p12;
+    p20 += p21;
+
+    p22 += p00;
+    p02 += p11;
+
+    p20 += p22;
+    p20 += p02;
+
+    p20 = min(p20 >> 10, (uint)255);
+    return (uchar)p20;
+}
+
+float gNoiseStrength;
+
+rs_allocation gNoise;
+uchar4 __attribute__((kernel)) root(uchar4 in, uint32_t x, uint32_t y) {
+    float4 ip = convert_float4(in);
+    float pnoise = (float) rsGetElementAt_uchar(gNoise, x & gWMask, y & gHMask);
+
+    float energy_level = ip.r + ip.g + ip.b;
+    float energy_mask = (28.f - sqrt(energy_level)) * 0.03571f;
+    pnoise = (pnoise - 128.f) * energy_mask;
+
+    ip += pnoise * gNoiseStrength;
+    ip = clamp(ip, 0.f, 255.f);
+
+    uchar4 p = convert_uchar4(ip);
+    p.a = 0xff;
+    return p;
+}
diff --git a/tests/rsSupportMode/src/main/rs/greyscale.fs b/tests/rsSupportMode/src/main/rs/greyscale.fs
new file mode 100644
index 0000000..4e13072
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/greyscale.fs
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+
+const static float3 gMonoMult = {0.299f, 0.587f, 0.114f};
+
+uchar4 __attribute__((kernel)) root(uchar4 v_in) {
+    float4 f4 = rsUnpackColor8888(v_in);
+
+    float3 mono = dot(f4.rgb, gMonoMult);
+    return rsPackColorTo8888(mono);
+}
+
+uchar __attribute__((kernel)) toU8(uchar4 v_in) {
+    float4 f4 = convert_float4(v_in);
+    return (uchar)dot(f4.rgb, gMonoMult);
+}
+
+uchar4 __attribute__((kernel)) toU8_4(uchar v_in) {
+    return (uchar4)v_in;
+}
+
diff --git a/tests/rsSupportMode/src/main/rs/ip.rsh b/tests/rsSupportMode/src/main/rs/ip.rsh
new file mode 100644
index 0000000..34e213c
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/ip.rsh
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.image2)
+
+
diff --git a/tests/rsSupportMode/src/main/rs/ip2_convolve3x3.rs b/tests/rsSupportMode/src/main/rs/ip2_convolve3x3.rs
new file mode 100644
index 0000000..177e86e
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/ip2_convolve3x3.rs
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+
+int32_t gWidth;
+int32_t gHeight;
+rs_allocation gIn;
+
+float gCoeffs[9];
+
+uchar4 __attribute__((kernel)) root(uint32_t x, uint32_t y) {
+    uint32_t x1 = min((int32_t)x+1, gWidth-1);
+    uint32_t x2 = max((int32_t)x-1, 0);
+    uint32_t y1 = min((int32_t)y+1, gHeight-1);
+    uint32_t y2 = max((int32_t)y-1, 0);
+
+    float4 p00 = convert_float4(rsGetElementAt_uchar4(gIn, x1, y1));
+    float4 p01 = convert_float4(rsGetElementAt_uchar4(gIn, x, y1));
+    float4 p02 = convert_float4(rsGetElementAt_uchar4(gIn, x2, y1));
+    float4 p10 = convert_float4(rsGetElementAt_uchar4(gIn, x1, y));
+    float4 p11 = convert_float4(rsGetElementAt_uchar4(gIn, x, y));
+    float4 p12 = convert_float4(rsGetElementAt_uchar4(gIn, x2, y));
+    float4 p20 = convert_float4(rsGetElementAt_uchar4(gIn, x1, y2));
+    float4 p21 = convert_float4(rsGetElementAt_uchar4(gIn, x, y2));
+    float4 p22 = convert_float4(rsGetElementAt_uchar4(gIn, x2, y2));
+    p00 *= gCoeffs[0];
+    p01 *= gCoeffs[1];
+    p02 *= gCoeffs[2];
+    p10 *= gCoeffs[3];
+    p11 *= gCoeffs[4];
+    p12 *= gCoeffs[5];
+    p20 *= gCoeffs[6];
+    p21 *= gCoeffs[7];
+    p22 *= gCoeffs[8];
+
+    p00 += p01;
+    p02 += p10;
+    p11 += p12;
+    p20 += p21;
+
+    p22 += p00;
+    p02 += p11;
+
+    p20 += p22;
+    p20 += p02;
+
+    p20 = clamp(p20, 0.f, 255.f);
+    return convert_uchar4(p20);
+}
+
+
diff --git a/tests/rsSupportMode/src/main/rs/levels.rsh b/tests/rsSupportMode/src/main/rs/levels.rsh
new file mode 100644
index 0000000..e289906
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/levels.rsh
@@ -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.
+ */
+
+float inBlack;
+float outBlack;
+float inWMinInB;
+float outWMinOutB;
+float overInWMinInB;
+rs_matrix3x3 colorMat;
+
+uchar4 __attribute__((kernel)) root(uchar4 in, uint32_t x, uint32_t y) {
+    uchar4 out;
+    float3 pixel = convert_float4(in).rgb;
+    pixel = rsMatrixMultiply(&colorMat, pixel);
+    pixel = clamp(pixel, 0.f, 255.f);
+    pixel = (pixel - inBlack) * overInWMinInB;
+    pixel = pixel * outWMinOutB + outBlack;
+    pixel = clamp(pixel, 0.f, 255.f);
+    out.xyz = convert_uchar3(pixel);
+    out.w = 0xff;
+    return out;
+}
+
+uchar4 __attribute__((kernel)) root4(uchar4 in, uint32_t x, uint32_t y) {
+    float4 pixel = convert_float4(in);
+    pixel.rgb = rsMatrixMultiply(&colorMat, pixel.rgb);
+    pixel = clamp(pixel, 0.f, 255.f);
+    pixel = (pixel - inBlack) * overInWMinInB;
+    pixel = pixel * outWMinOutB + outBlack;
+    pixel = clamp(pixel, 0.f, 255.f);
+    return convert_uchar4(pixel);
+}
+
diff --git a/tests/rsSupportMode/src/main/rs/levels_full.rs b/tests/rsSupportMode/src/main/rs/levels_full.rs
new file mode 100644
index 0000000..28596ba
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/levels_full.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+
+#include "levels.rsh"
+
diff --git a/tests/rsSupportMode/src/main/rs/levels_relaxed.fs b/tests/rsSupportMode/src/main/rs/levels_relaxed.fs
new file mode 100644
index 0000000..28596ba
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/levels_relaxed.fs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+
+#include "levels.rsh"
+
diff --git a/tests/rsSupportMode/src/main/rs/mandelbrot.rs b/tests/rsSupportMode/src/main/rs/mandelbrot.rs
new file mode 100644
index 0000000..de0bd00
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/mandelbrot.rs
@@ -0,0 +1,55 @@
+// Copyright (C) 2011 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.
+
+#include "ip.rsh"
+
+uint32_t gMaxIteration = 500;
+uint32_t gDimX = 1024;
+uint32_t gDimY = 1024;
+
+float lowerBoundX = -2.f;
+float lowerBoundY = -2.f;
+float scaleFactor = 4.f;
+
+uchar4 __attribute__((kernel)) root(uint32_t x, uint32_t y) {
+  float2 p;
+  p.x = lowerBoundX + ((float)x / gDimX) * scaleFactor;
+  p.y = lowerBoundY + ((float)y / gDimY) * scaleFactor;
+
+  float2 t = 0;
+  float2 t2 = t * t;
+  int iter = 0;
+  while((t2.x + t2.y < 4.f) && (iter < gMaxIteration)) {
+    float xtemp = t2.x - t2.y + p.x;
+    t.y = 2 * t.x * t.y + p.y;
+    t.x = xtemp;
+    iter++;
+    t2 = t * t;
+  }
+
+  if(iter >= gMaxIteration) {
+    // write a non-transparent black pixel
+    return (uchar4){0, 0, 0, 0xff};
+  } else {
+    float mi3 = gMaxIteration / 3.f;
+    if (iter <= (gMaxIteration / 3))
+      return (uchar4){0xff * (iter / mi3), 0, 0, 0xff};
+    else if (iter <= (((gMaxIteration / 3) * 2)))
+      return (uchar4){0xff - (0xff * ((iter - mi3) / mi3)),
+                      (0xff * ((iter - mi3) / mi3)), 0, 0xff};
+    else
+      return (uchar4){0, 0xff - (0xff * ((iter - (mi3 * 2)) / mi3)),
+                      (0xff * ((iter - (mi3 * 2)) / mi3)), 0xff};
+  }
+}
diff --git a/tests/rsSupportMode/src/main/rs/shadows.rs b/tests/rsSupportMode/src/main/rs/shadows.rs
new file mode 100644
index 0000000..f6c149d
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/shadows.rs
@@ -0,0 +1,192 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+//#pragma rs_fp_relaxed
+
+static double shadowFilterMap[] = {
+    -0.00591,  0.0001,
+     1.16488,  0.01668,
+    -0.18027, -0.06791,
+    -0.12625,  0.09001,
+     0.15065, -0.03897
+};
+
+static double poly[] = {
+    0., 0.,
+    0., 0.,
+    0.
+};
+
+static const int ABITS = 4;
+static const int HSCALE = 256;
+static const int k1=255 << ABITS;
+static const int k2=HSCALE << ABITS;
+
+static double fastevalPoly(double *poly,int n, double x){
+
+    double f =x;
+    double sum = poly[0]+poly[1]*f;
+    int i;
+    for (i = 2; i < n; i++) {
+        f*=x;
+        sum += poly[i]*f;
+    }
+    return sum;
+}
+
+static ushort3 rgb2hsv( uchar4 rgb)
+{
+    int iMin,iMax,chroma;
+
+    int ri = rgb.r;
+    int gi = rgb.g;
+    int bi = rgb.b;
+    short rv,rs,rh;
+
+    if (ri > gi) {
+        iMax = max (ri, bi);
+        iMin = min (gi, bi);
+    } else {
+        iMax = max (gi, bi);
+        iMin = min (ri, bi);
+    }
+
+    chroma = iMax - iMin;
+    // set value
+    rv = (short)( iMax << ABITS);
+
+    // set saturation
+    if (rv == 0)
+        rs = 0;
+    else
+        rs = (short)((k1*chroma)/iMax);
+
+    // set hue
+    if (rs == 0)
+        rh = 0;
+    else {
+        if ( ri == iMax ) {
+            rh  = (short)( (k2*(6*chroma+gi - bi))/(6*chroma));
+            if (rh >= k2) rh -= k2;
+        } else if (gi  == iMax)
+            rh  = (short)( (k2*(2*chroma+bi - ri ))/(6*chroma));
+        else // (bi == iMax )
+                    rh  = (short)( (k2*(4*chroma+ri - gi ))/(6*chroma));
+    }
+
+    ushort3 out;
+    out.x = rv;
+    out.y = rs;
+    out.z = rh;
+    return out;
+}
+
+static uchar4 hsv2rgb(ushort3 hsv)
+{
+    int ABITS = 4;
+    int HSCALE = 256;
+    int m;
+    int H,X,ih,is,iv;
+    int k1=255<<ABITS;
+    int k2=HSCALE<<ABITS;
+    int k3=1<<(ABITS-1);
+    int rr=0;
+    int rg=0;
+    int rb=0;
+    short cv = hsv.x;
+    short cs = hsv.y;
+    short ch = hsv.z;
+
+    // set chroma and min component value m
+    //chroma = ( cv * cs )/k1;
+    //m = cv - chroma;
+    m = ((int)cv*(k1 - (int)cs ))/k1;
+
+    // chroma  == 0 <-> cs == 0 --> m=cv
+    if (cs == 0) {
+        rb = ( rg = ( rr =( cv >> ABITS) ));
+    } else {
+        ih=(int)ch;
+        is=(int)cs;
+        iv=(int)cv;
+
+        H = (6*ih)/k2;
+        X = ((iv*is)/k2)*(k2- abs(6*ih- 2*(H>>1)*k2 - k2)) ;
+
+        // removing additional bits --> unit8
+        X=( (X+iv*(k1 - is ))/k1 + k3 ) >> ABITS;
+        m=m >> ABITS;
+
+        // ( chroma + m ) --> cv ;
+        cv=(short) (cv >> ABITS);
+        switch (H) {
+        case 0:
+            rr = cv;
+            rg = X;
+            rb = m;
+            break;
+        case 1:
+            rr = X;
+            rg = cv;
+            rb = m;
+            break;
+        case 2:
+            rr = m;
+            rg = cv;
+            rb = X;
+            break;
+        case 3:
+            rr = m;
+            rg = X;
+            rb = cv;
+            break;
+        case 4:
+            rr = X;
+            rg = m;
+            rb = cv;
+            break;
+        case 5:
+            rr = cv;
+            rg = m ;
+            rb = X;
+            break;
+        }
+    }
+
+    uchar4 rgb;
+
+    rgb.r =  rr;
+    rgb.g =  rg;
+    rgb.b =  rb;
+
+    return rgb;
+}
+
+void prepareShadows(float scale) {
+    double s = (scale>=0)?scale:scale/5;
+    for (int i = 0; i < 5; i++) {
+        poly[i] = fastevalPoly(shadowFilterMap+i*2,2 , s);
+    }
+}
+
+void shadowsKernel(const uchar4 *in, uchar4 *out) {
+    ushort3 hsv = rgb2hsv(*in);
+    double v = (fastevalPoly(poly,5,hsv.x/4080.)*4080);
+    if (v>4080) v = 4080;
+    hsv.x = (unsigned short) ((v>0)?v:0);
+    *out = hsv2rgb(hsv);
+}
diff --git a/tests/rsSupportMode/src/main/rs/threshold.fs b/tests/rsSupportMode/src/main/rs/threshold.fs
new file mode 100644
index 0000000..0b2c2e8
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/threshold.fs
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#include "ip.rsh"
+
+
+int height;
+int width;
+static int radius;
+
+rs_allocation InPixel;
+rs_allocation ScratchPixel1;
+rs_allocation ScratchPixel2;
+
+const int MAX_RADIUS = 25;
+
+// Store our coefficients here
+static float gaussian[MAX_RADIUS * 2 + 1];
+
+void setRadius(int rad) {
+    radius = rad;
+    // Compute gaussian weights for the blur
+    // e is the euler's number
+    float e = 2.718281828459045f;
+    float pi = 3.1415926535897932f;
+    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
+    // x is of the form [-radius .. 0 .. radius]
+    // and sigma varies with radius.
+    // Based on some experimental radius values and sigma's
+    // we approximately fit sigma = f(radius) as
+    // sigma = radius * 0.4  + 0.6
+    // The larger the radius gets, the more our gaussian blur
+    // will resemble a box blur since with large sigma
+    // the gaussian curve begins to lose its shape
+    float sigma = 0.4f * (float)radius + 0.6f;
+
+    // Now compute the coefficints
+    // We will store some redundant values to save some math during
+    // the blur calculations
+    // precompute some values
+    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
+    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
+
+    float normalizeFactor = 0.0f;
+    float floatR = 0.0f;
+    for (int r = -radius; r <= radius; r ++) {
+        floatR = (float)r;
+        gaussian[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
+        normalizeFactor += gaussian[r + radius];
+    }
+
+    //Now we need to normalize the weights because all our coefficients need to add up to one
+    normalizeFactor = 1.0f / normalizeFactor;
+    for (int r = -radius; r <= radius; r ++) {
+        floatR = (float)r;
+        gaussian[r + radius] *= normalizeFactor;
+    }
+}
+
+float4 __attribute__((kernel)) copyIn(uchar4 in) {
+    return convert_float4(in);
+}
+
+uchar4 __attribute__((kernel)) vert(uint32_t x, uint32_t y) {
+    float3 blurredPixel = 0;
+    int gi = 0;
+    uchar4 out;
+    if ((y > radius) && (y < (height - radius))) {
+        for (int r = -radius; r <= radius; r ++) {
+            float4 i = rsGetElementAt_float4(ScratchPixel2, x, y + r);
+            blurredPixel += i.xyz * gaussian[gi++];
+        }
+    } else {
+        for (int r = -radius; r <= radius; r ++) {
+            int validH = rsClamp((int)y + r, (int)0, (int)(height - 1));
+            float4 i = rsGetElementAt_float4(ScratchPixel2, x, validH);
+            blurredPixel += i.xyz * gaussian[gi++];
+        }
+    }
+
+    out.xyz = convert_uchar3(clamp(blurredPixel, 0.f, 255.f));
+    out.w = 0xff;
+    return out;
+}
+
+float4 __attribute__((kernel)) horz(uint32_t x, uint32_t y) {
+    float4 blurredPixel = 0;
+    int gi = 0;
+    if ((x > radius) && (x < (width - radius))) {
+        for (int r = -radius; r <= radius; r ++) {
+            float4 i = rsGetElementAt_float4(ScratchPixel1, x + r, y);
+            blurredPixel += i * gaussian[gi++];
+        }
+    } else {
+        for (int r = -radius; r <= radius; r ++) {
+            // Stepping left and right away from the pixel
+            int validX = rsClamp((int)x + r, (int)0, (int)(width - 1));
+            float4 i = rsGetElementAt_float4(ScratchPixel1, validX, y);
+            blurredPixel += i * gaussian[gi++];
+        }
+    }
+
+    return blurredPixel;
+}
+
diff --git a/tests/rsSupportMode/src/main/rs/vibrance.rs b/tests/rsSupportMode/src/main/rs/vibrance.rs
new file mode 100644
index 0000000..ad4de58
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/vibrance.rs
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+
+float vibrance = 0.f;
+
+static const float Rf = 0.2999f;
+static const float Gf = 0.587f;
+static const float Bf = 0.114f;
+
+static float S  = 0.f;
+static float MS = 0.f;
+static float Rt = 0.f;
+static float Gt = 0.f;
+static float Bt = 0.f;
+static float Vib = 0.f;
+
+void vibranceKernel(const uchar4 *in, uchar4 *out) {
+
+    float R, G, B;
+
+    int r = in->r;
+    int g = in->g;
+    int b = in->b;
+    float red = (r-max(g, b))/256.f;
+    float sx = (float)(Vib/(1+native_exp(-red*3)));
+    S = sx+1;
+    MS = 1.0f - S;
+    Rt = Rf * MS;
+    Gt = Gf * MS;
+    Bt = Bf * MS;
+    int t = (r + g) / 2;
+    R = r;
+    G = g;
+    B = b;
+
+    float Rc = R * (Rt + S) + G * Gt + B * Bt;
+    float Gc = R * Rt + G * (Gt + S) + B * Bt;
+    float Bc = R * Rt + G * Gt + B * (Bt + S);
+
+    out->r = rsClamp(Rc, 0, 255);
+    out->g = rsClamp(Gc, 0, 255);
+    out->b = rsClamp(Bc, 0, 255);
+
+}
+
+void prepareVibrance() {
+
+    Vib = vibrance/100.f;
+    S  = Vib + 1;
+    MS = 1.0f - S;
+    Rt = Rf * MS;
+    Gt = Gf * MS;
+    Bt = Bf * MS;
+
+}
diff --git a/tests/rsSupportMode/src/main/rs/vignette.rsh b/tests/rsSupportMode/src/main/rs/vignette.rsh
new file mode 100644
index 0000000..04ca1f1
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/vignette.rsh
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+static float2 neg_center, axis_scale, inv_dimensions;
+static float sloped_neg_range, sloped_inv_max_dist, shade, opp_shade;
+
+void init_vignette(uint32_t dim_x, uint32_t dim_y, float center_x, float center_y,
+        float desired_scale, float desired_shade, float desired_slope) {
+
+    neg_center.x = -center_x;
+    neg_center.y = -center_y;
+    inv_dimensions.x = 1.f / (float)dim_x;
+    inv_dimensions.y = 1.f / (float)dim_y;
+
+    axis_scale = (float2)1.f;
+    if (dim_x > dim_y)
+        axis_scale.y = (float)dim_y / (float)dim_x;
+    else
+        axis_scale.x = (float)dim_x / (float)dim_y;
+
+    const float max_dist = 0.5f * length(axis_scale);
+    sloped_inv_max_dist = desired_slope * 1.f/max_dist;
+
+    // Range needs to be between 1.3 to 0.6. When scale is zero then range is
+    // 1.3 which means no vignette at all because the luminousity difference is
+    // less than 1/256.  Expect input scale to be between 0.0 and 1.0.
+    const float neg_range = 0.7f*sqrt(desired_scale) - 1.3f;
+    sloped_neg_range = exp(neg_range * desired_slope);
+
+    shade = desired_shade;
+    opp_shade = 1.f - desired_shade;
+}
+
+uchar4 __attribute__((kernel)) root(uchar4 in, uint32_t x, uint32_t y) {
+    // Convert x and y to floating point coordinates with center as origin
+    const float4 fin = convert_float4(in);
+    const float2 inCoord = {(float)x, (float)y};
+    const float2 coord = mad(inCoord, inv_dimensions, neg_center);
+    const float sloped_dist_ratio = length(axis_scale * coord)  * sloped_inv_max_dist;
+    const float lumen = opp_shade + shade / ( 1.0f + sloped_neg_range * exp(sloped_dist_ratio) );
+    float4 fout;
+    fout.rgb = fin.rgb * lumen;
+    fout.w = fin.w;
+    return convert_uchar4(fout);
+}
+
diff --git a/tests/rsSupportMode/src/main/rs/vignette_approx.rsh b/tests/rsSupportMode/src/main/rs/vignette_approx.rsh
new file mode 100644
index 0000000..5668621
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/vignette_approx.rsh
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+static float2 neg_center, axis_scale, inv_dimensions;
+static float sloped_neg_range, sloped_inv_max_dist, shade, opp_shade;
+
+void init_vignette(uint32_t dim_x, uint32_t dim_y, float center_x, float center_y,
+        float desired_scale, float desired_shade, float desired_slope) {
+
+    neg_center.x = -center_x;
+    neg_center.y = -center_y;
+    inv_dimensions.x = 1.f / (float)dim_x;
+    inv_dimensions.y = 1.f / (float)dim_y;
+
+    axis_scale = (float2)1.f;
+    if (dim_x > dim_y)
+        axis_scale.y = (float)dim_y / (float)dim_x;
+    else
+        axis_scale.x = (float)dim_x / (float)dim_y;
+
+    const float max_dist = 0.5f * length(axis_scale);
+    sloped_inv_max_dist = desired_slope * 1.f/max_dist;
+
+    // Range needs to be between 1.3 to 0.6. When scale is zero then range is
+    // 1.3 which means no vignette at all because the luminousity difference is
+    // less than 1/256.  Expect input scale to be between 0.0 and 1.0.
+    const float neg_range = 0.7f*sqrt(desired_scale) - 1.3f;
+    sloped_neg_range = exp(neg_range * desired_slope);
+
+    shade = desired_shade;
+    opp_shade = 1.f - desired_shade;
+}
+
+uchar4 __attribute__((kernel)) root(uchar4 in, uint32_t x, uint32_t y) {
+    // Convert x and y to floating point coordinates with center as origin
+    const float4 fin = convert_float4(in);
+    const float2 inCoord = {(float)x, (float)y};
+    const float2 coord = mad(inCoord, inv_dimensions, neg_center);
+    const float sloped_dist_ratio = fast_length(axis_scale * coord)  * sloped_inv_max_dist;
+    const float lumen = opp_shade + shade * half_recip(1.f + sloped_neg_range * native_exp(sloped_dist_ratio));
+    float4 fout;
+    fout.rgb = fin.rgb * lumen;
+    fout.w = fin.w;
+    return convert_uchar4(fout);
+}
+
diff --git a/tests/rsSupportMode/src/main/rs/vignette_approx_full.rs b/tests/rsSupportMode/src/main/rs/vignette_approx_full.rs
new file mode 100644
index 0000000..00cbbc4
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/vignette_approx_full.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+
+#include "vignette_approx.rsh"
+
diff --git a/tests/rsSupportMode/src/main/rs/vignette_approx_relaxed.fs b/tests/rsSupportMode/src/main/rs/vignette_approx_relaxed.fs
new file mode 100644
index 0000000..00cbbc4
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/vignette_approx_relaxed.fs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+
+#include "vignette_approx.rsh"
+
diff --git a/tests/rsSupportMode/src/main/rs/vignette_full.rs b/tests/rsSupportMode/src/main/rs/vignette_full.rs
new file mode 100644
index 0000000..8202c5c
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/vignette_full.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+
+#include "vignette.rsh"
+
diff --git a/tests/rsSupportMode/src/main/rs/vignette_relaxed.fs b/tests/rsSupportMode/src/main/rs/vignette_relaxed.fs
new file mode 100644
index 0000000..8202c5c
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/vignette_relaxed.fs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+
+#include "vignette.rsh"
+
diff --git a/tests/rsSupportMode/src/main/rs/wbalance.rs b/tests/rsSupportMode/src/main/rs/wbalance.rs
new file mode 100644
index 0000000..6650671
--- /dev/null
+++ b/tests/rsSupportMode/src/main/rs/wbalance.rs
@@ -0,0 +1,142 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+//#pragma rs_fp_relaxed
+
+static int histR[256] = {0}, histG[256] = {0}, histB[256] = {0};
+
+rs_allocation histogramSource;
+uint32_t histogramHeight;
+uint32_t histogramWidth;
+
+static float scaleR;
+static float scaleG;
+static float scaleB;
+
+static uchar4 estimateWhite() {
+
+    for (int i = 0; i < 256; i++) {
+        histR[i] = 0; histG[i] = 0; histB[i] = 0;
+    }
+
+    for (uint32_t i = 0; i < histogramHeight; i++) {
+        for (uint32_t j = 0; j < histogramWidth; j++) {
+            uchar4 in = rsGetElementAt_uchar4(histogramSource, j, i);
+            histR[in.r]++;
+            histG[in.g]++;
+            histB[in.b]++;
+        }
+    }
+
+    int min_r = -1, min_g = -1, min_b = -1;
+    int max_r =  0, max_g =  0, max_b =  0;
+    int sum_r =  0, sum_g =  0, sum_b =  0;
+
+    for (int i = 1; i < 255; i++) {
+        int r = histR[i];
+        int g = histG[i];
+        int b = histB[i];
+        sum_r += r;
+        sum_g += g;
+        sum_b += b;
+
+        if (r>0){
+            if (min_r < 0) min_r = i;
+            max_r = i;
+        }
+        if (g>0){
+            if (min_g < 0) min_g = i;
+            max_g = i;
+        }
+        if (b>0){
+            if (min_b < 0) min_b = i;
+            max_b = i;
+        }
+    }
+
+    int sum15r = 0, sum15g = 0, sum15b = 0;
+    int count15r = 0, count15g = 0, count15b = 0;
+    int tmp_r = 0, tmp_g = 0, tmp_b = 0;
+
+    for (int i = 254; i >0; i--) {
+        int r = histR[i];
+        int g = histG[i];
+        int b = histB[i];
+        tmp_r += r;
+        tmp_g += g;
+        tmp_b += b;
+
+        if ((tmp_r > sum_r/20) && (tmp_r < sum_r/5)) {
+            sum15r += r*i;
+            count15r += r;
+        }
+        if ((tmp_g > sum_g/20) && (tmp_g < sum_g/5)) {
+            sum15g += g*i;
+            count15g += g;
+        }
+        if ((tmp_b > sum_b/20) && (tmp_b < sum_b/5)) {
+            sum15b += b*i;
+            count15b += b;
+        }
+
+    }
+
+    uchar4 out;
+
+    if ((count15r>0) && (count15g>0) && (count15b>0) ){
+        out.r = sum15r/count15r;
+        out.g = sum15g/count15g;
+        out.b = sum15b/count15b;
+    }else {
+        out.r = out.g = out.b = 255;
+    }
+
+    return out;
+
+}
+
+void prepareWhiteBalance() {
+    uchar4 estimation = estimateWhite();
+    int minimum = min(estimation.r, min(estimation.g, estimation.b));
+    int maximum = max(estimation.r, max(estimation.g, estimation.b));
+    float avg = (minimum + maximum) / 2.f;
+
+    scaleR =  avg/estimation.r;
+    scaleG =  avg/estimation.g;
+    scaleB =  avg/estimation.b;
+
+}
+
+static unsigned char contrastClamp(int c)
+{
+    int N = 255;
+    c &= ~(c >> 31);
+    c -= N;
+    c &= (c >> 31);
+    c += N;
+    return  (unsigned char) c;
+}
+
+void whiteBalanceKernel(const uchar4 *in, uchar4 *out) {
+    float Rc =  in->r*scaleR;
+    float Gc =  in->g*scaleG;
+    float Bc =  in->b*scaleB;
+
+    out->r = contrastClamp(Rc);
+    out->g = contrastClamp(Gc);
+    out->b = contrastClamp(Bc);
+}
diff --git a/tests/sameNamedLibs/build.gradle b/tests/sameNamedLibs/build.gradle
index f210fcf..ef3d0aa 100644
--- a/tests/sameNamedLibs/build.gradle
+++ b/tests/sameNamedLibs/build.gradle
@@ -3,7 +3,7 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }
 
diff --git a/tests/tictactoe/build.gradle b/tests/tictactoe/build.gradle
index 62a4405..b899f9a 100644
--- a/tests/tictactoe/build.gradle
+++ b/tests/tictactoe/build.gradle
@@ -3,6 +3,6 @@
         maven { url '../../../../out/host/gradle/repo' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.0-SNAPSHOT'
+        classpath 'com.android.tools.build:gradle:0.7.0-SNAPSHOT'
     }
 }